1. 首页 > 电脑知识

Java 热门面试题 200 道, java面试题100道

作者:admin 更新时间:2025-07-22
摘要:Java 热门面试题 200 道 Java 热门面试题 200 道(Markdown表格版) Java 热门面试题 200 道(Markdown列表版) Java 热门面试题200道 - 核心知识点摘要 HashMap原理 基于哈希表实现,使用数组+链表/红黑树存储数据 JDK1.8优化:链表长度≥8时转为红黑树、尾插法替代头插法、扩容优化 线程不安全,扩容时容量翻倍并重新哈希 Concurren,Java 热门面试题 200 道, java面试题100道

 

Java 热门面试题 200 道

Java 热门面试题 200 道(Markdown表格版) Java 热门面试题 200 道(Markdown列表版)


Java 热门面试题200道 – 核心 智慧点 简介

HashMap原理

基于哈希表实现,使用数组+链表/红黑树存储数据 JDK1.8优化:链表长度≥8时转为红黑树、尾插法替代头插法、扩容优化 线程不安全,扩容时容量翻倍并重新哈希

ConcurrentHashMap演进

1.7使用分段锁(Segment),1.8改为CAS+synchronized锁单个桶 1.8引入红黑树优化查询,并发度更高,扩容支持多线程协同

框架分类

List:ArrayList(数组)、LinkedList(链表) Set:HashSet、TreeSet(有序)、LinkedHashSet(保序) Map:HashMap、TreeMap、ConcurrentHashMap Queue:ArrayDeque、PriorityQueue(堆)

MySQL索引 制度

联合索引遵循最左前缀匹配,必须包含最左列才能生效 索引按列顺序存储,缺失左列无法利用有序性


文章目录

Java 热门面试题 200 道(Markdown表格版)

题目列表


Java 热门面试题 200 道(Markdown表格版)

题目列表

No. title answer answer2 answer3 tagList
1 说说 Java 中 HashMap 的原理? HashMap 基于哈希表实现,使用数组+链表/红黑树存储数据。通过 key 的 hashCode 计算桶位置(数组索引),发生哈希冲突时采用链地址法解决(JDK1.8 后链表长度≥8时转为红黑树)。允许 null 键/值,非线程安全。扩容时容量翻倍并重新哈希。 1. 数据结构:基于数组+链表/红黑树(JDK1.8+)。 2. 哈希计算:通过key的hashCode()计算桶位置。 3. 冲突解决:链表法(O(1)+O(n)),链表过长时转红黑树(O(log n))。 4. 扩容机制:默认负载因子0.75,扩容时容量翻倍并rehash。 1. 数据结构:JDK 1.8 后采用数组 + 链表 + 红黑树结构。数组是桶(Bucket),每个桶可存储链表或红黑树。 2. 哈希计算:通过 key.hashCode() 的高 16 位异或低 16 位计算哈希值,再通过 (n-1) & hash 确定桶下标,减少哈希冲突。 3. 插入逻辑:若桶为空则直接插入;若为链表,遍历至尾节点插入(尾插法),若链表长度 ≥8 且数组长度 ≥ ,链表转红黑树。 4. 扩容机制:默认负载因子 0.75,当元素数量超过容量 × 负载因子时,扩容为原 2 倍。扩容后重新计算索引,元素要么在原位置,要么在 原位置 + 原容量。 5. 线程安全 难题:多线程下可能因并发扩容导致死循环(JDK 1.7)或数据丢失(1.7/1.8)。 Java,Java 基础
2 Java 中 ConcurrentHashMap 1.7 和 1.8 之间有哪些区别? 1.7 使用分段锁(Segment),每个段独立加锁;1.8 改为 CAS + synchronized 锁单个桶(Node)。1.7 只有链表,1.8 引入红黑树优化长链表查询。1.8 的 size() 计算改用 baseCount + CounterCell 避免全局锁。 1. 数据结构:1.7用Segment分段锁;1.8改用数组+链表/红黑树+CAS+synchronized。 2. 并发度:1.7锁粒度是Segment;1.8锁粒度是单个桶。 3. 哈希冲突:1.8引入红黑树优化长链表查询。 4. API扩展:1.8新增forEach/reduce等并行 技巧。 1. 数据结构: – JDK 1.7:分段锁(Segment 数组 + HashEntry 链表),锁粒度较粗。 – JDK 1.8:数组 + 链表/红黑树 + CAS + synchronized(锁单个桶),锁粒度更细。 2. 并发控制: – 1.7 使用 ReentrantLock 锁 Segment。 – 1.8 对桶头节点用 synchronized 加锁,结合 CAS 实现无锁化插入。 3. 扩容机制: – 1.7 分段扩容,各 Segment 独立扩容。 – 1.8 支持多线程协同扩容(通过 ForwardingNode 标记迁移 情形)。 4. 查询性能:1.8 的 volatile + Unsafe 提供无锁读,效率更高。 5. 红黑树支持:1.8 在链表长度 >8 时转红黑树,避免查询退化至 O(n)。 Java ,Java
3 何故 JDK 1.8 对 HashMap 进行了红黑树的改动? 解决哈希冲突严重时链表过长导致的查询效率退化(O(n) 降至 O(logn))。当链表长度 ≥8 且数组容量 ≥ 时转为红黑树;节点数 ≤6 时退化为链表。平衡空间与 时刻开销。 1. 解决极端情况:当哈希冲突严重时,链表过长导致查询效率退化至O(n)。 2. 性能优化:红黑树将查询 时刻复杂度优化至O(log n)。 3. 阈值控制:链表长度>8且数组长度≥ 时转红黑树;树节点<6时退化为链表。 1. 解决哈希冲突退化 难题:极端情况下(如大量哈希碰撞),链表长度过长会导致查询效率从 O(1) 退化为 O(n)。 2. 优化查询性能:红黑树(自平衡二叉查找树)保证最坏情况查询复杂度为 O(log n),显著提升哈希冲突时的性能。 3. 平衡空间与 时刻:红黑树占用空间比链表大,但权衡后对性能提升更关键。转换阈值(链表 ≥8 转树,树 ≤6 退链表)避免频繁转换开销。 Java ,Java
4 JDK 1.8 对 HashMap 除了红黑树还进行了哪些改动? 1. 头插法改为尾插法(避免多线程扩容死循环) 2. 扩容时旧链表拆分为高低位链表(优化重新哈希计算) 3. 计算哈希值:高位参与运算 (h = key.hashCode()) ^ (h >>> 16) 4. 初始化延迟到首次 put 时。 1. 扩容优化:旧链表拆分为高位链和低位链(无需rehash计算)。 2. 插入逻辑:头插法改为尾插法(避免并发扩容死循环)。 3. 哈希算法:简化hash计算(高位参与运算减少冲突)。 4. 新增 技巧:compute(), merge()等函数式API。 1. 尾插法替代头插法:解决并发扩容时链表死循环 难题(1.7 头插法导致环状链表)。 2. 扩容索引计算优化:元素迁移后位置 = 原位置 或 原位置 + 旧容量,利用高位掩码判断,避免重新计算哈希。 3. 哈希计算优化:key.hashCode() 高 16 位异或低 16 位((h = key.hashCode()) ^ (h >>> 16)),使低位更分散,减少冲突。 4. 链表拆分逻辑:扩容时链表按高位 0/1 拆分为两条链,分别存入新数组的 i 和 i+oldCap 位置。 Java ,Java
5 Java 中有哪些 类?请简单介绍 List:ArrayList(数组)、LinkedList(双向链表)、Vector(线程安全数组) Set:HashSet(基于HashMap)、TreeSet(红黑树)、LinkedHashSet(链表保序) Map:HashMap、TreeMap(键有序)、LinkedHashMap(插入序)、ConcurrentHashMap(线程安全) Queue:ArrayDeque、PriorityQueue(堆) 1. List:有序可重复 – ArrayList(数组)、LinkedList(链表)、Vector(线程安全数组)。 2. Set:无序唯一 – HashSet(哈希表)、TreeSet(红黑树)、LinkedHashSet(链表+哈希表)。 3. Map:键值对 – HashMap、TreeMap(红黑树)、ConcurrentHashMap(线程安全)。 4. Queue:队列 – LinkedList、PriorityQueue(堆)、ArrayDeque(双端队列)。 1. List 有序 : – ArrayList:基于动态数组,随机访问快(O(1)),插入删除慢(O(n))。 – LinkedList:基于双向链表,插入删除快(O(1)),随机访问慢(O(n))。 – Vector:线程安全版 ArrayList( 技巧 synchronized),性能差。 2. Set 无序唯一 : – HashSet:基于 HashMap 实现,元素需实现 hashCode() 和 equals()。 – LinkedHashSet:维护插入顺序的双向链表。 – TreeSet:基于红黑树,元素可排序(实现 Comparable 或传入 Comparator)。 3. Map 键值对 : – HashMap:数组 + 链表/红黑树,非线程安全。 – LinkedHashMap:记录插入顺序或访问顺序(LRU 实现基础)。 – TreeMap:基于红黑树,按键排序。 – ConcurrentHashMap:线程安全的 HashMap。 – Hashtable:线程安全但全表锁,已淘汰。 4. Queue 队列: – LinkedList:可作双向队列。 – PriorityQueue:基于堆的优先级队列。 – ArrayDeque:数组实现的双端队列。 Java ,Java
6 MySQL 索引的最左前缀匹配 制度是 何? 联合索引 (a,b,c) 生效条件:查询必须包含最左列 a。能匹配 a、a,b、a,b,c 的组合,但无法跳过 a 直接查 b,c。原理:索引按列顺序存储,缺失左列时无法利用有序性。 1. 联合索引 制度:从最左列开始连续匹配,遇到范围查询(>、<、between)后停止。 2. 示例:索引(A,B,C)可生效查询:WHERE A=1;WHERE A=1 AND B=2;WHERE A=1 AND B=2 AND C=3;但WHERE B=2无效。 3. 排序优化:索引列顺序影响ORDER BY性能。 1. 定义:联合索引中,查询条件必须从索引最左列开始连续匹配,不能跳过中间列。 2. 匹配 制度: – 精确匹配最左列后,后续列可范围查询或精确匹配。 – 跳过最左列或中间列,则后续列无法使用索引(如索引 (a,b,c),查询 where b=1 and c=2 失效)。 3. 例子:索引 (a,b,c) 生效场景: – where a=1 and b=2 and c=3(全匹配) – where a=1 and b>2(a 精确,b 范围) – where a=1(仅最左列) 4. 失效场景: – 查询条件未包含最左列(如 b=2)。 – 最左列使用范围查询后,后续列无法索引(如 a>1 and b=2,b 无法索引)。 后端,MySQL,数据库
7 数据库的脏读、不可重复读和幻读分别是 何? 脏读:读到未提交的数据(事务A读事务B未提交修改) 不可重复读:同事务内两次读同一数据 结局不同(因其他事务修改) 幻读:同事务内两次查询 结局集数量不同(因其他事务增删数据)。 1. 脏读:读取到其他事务未提交的数据(违反原子性)。 2. 不可重复读:同一事务内多次读取同一数据, 结局不同(由UPDATE引起)。 3. 幻读:同一事务内多次查询,返回不同行数(由INSERT/DELETE引起)。 4. 隔离级别:脏读(READ UNCOMMITTED)、不可重复读(READ COMMITTED)、幻读(REPEATABLE READ)。 1. 脏读(Dirty Read):事务 A 读取了事务 B 未提交的修改数据,若 B 回滚,A 读到的数据无效。 – 例:A 读到 B 未提交的余额更新,后 B 回滚,A 基于错误数据操作。 2. 不可重复读(Non-Repeatable Read):事务 A 多次读取同一数据,期间事务 B 修改并提交了该数据,导致 A 两次读取 结局不一致。 – 例:A 第一次读余额 100,B 更新为 50 并提交,A 再读余额变为 50。 3. 幻读(Phantom Read):事务 A 多次查询同一范围数据,期间事务 B 插入或删除了符合该范围的记录并提交,导致 A 两次查询 结局集不一致。 – 例:A 查询年龄 <30 的用户得 10 条,B 插入一条年龄 25 的记录并提交,A 再查得 11 条。 隔离级别解决: – 读未提交(Read Uncommitted):可能发生所有 难题。 – 读已提交(Read Committed):避免脏读。 – 可重复读(Repeatable Read):避免脏读、不可重复读(MySQL InnoDB 通过 MVCC 避免幻读)。 – 串行化(Serializable):避免所有 难题。 后端,MySQL,数据库
8 MySQL 的存储引擎有哪些?它们之间有 何区别? InnoDB:支持事务、行锁、外键、聚簇索引,适用OLTP MyISAM:表锁、全文索引、高读性能,不支持事务,适用OLAP Memory:内存存储,数据易丢失 区别核心:事务支持(InnoDB支持)、锁粒度(InnoDB行锁 vs MyISAM表锁)、崩溃恢复能力。 1. InnoDB:支持事务、行锁、外键;聚簇索引;适用写密集型场景。 2. MyISAM:不支持事务/行锁;表锁;独立索引文件(.MYI);适用读密集型。 3. Memory:数据存内存;哈希索引;重启数据丢失。 4. 核心差异:事务支持、锁粒度、崩溃恢复能力、索引结构。 1. InnoDB: – 特性:支持事务、行级锁、外键、MVCC(多版本并发控制),提供 ACID 兼容。 – 适用场景:高并发 OLTP(如订单、支付 体系)。 2. MyISAM: – 特性:不支持事务和行锁(仅表锁),支持全文索引,读取性能高。 – 适用场景:读多写少(如日志分析),已逐渐被淘汰。 3. Memory: – 特性:数据存内存,读写快,重启后数据丢失。 – 适用场景:临时表或缓存。 4. Archive: – 特性:高压缩比存储,只支持插入和查询。 – 适用场景:归档历史数据(如日志)。 核心区别: | 特性 | InnoDB | MyISAM | |————–|—————–|————-| | 事务 | 支持 | 不支持 | | 锁粒度 | 行级锁 | 表锁 | | 外键 | 支持 | 不支持 | | 崩溃恢复 | 支持(Redo Log)| 不支持 | | 索引类型 | 聚簇索引 | 非聚簇索引 | 后端,MySQL,数据库
9 MySQL 的覆盖索引是 何? 索引包含查询所需所有字段(如 SELECT a,b FROM t WHERE c=1,索引 (c,a,b))。避免回表查询(直接从索引取数据),显著提升性能。EXPLAIN 显示 “Using index” 即使用了覆盖索引。 1. 定义:查询所需列均包含在索引中,无需回表查数据文件。 2. 优势:减少I/O(仅扫描索引),提升查询速度。 3. 示例:索引(A,B),查询SELECT A,B FROM table WHERE A=1。 4. 限制:SELECT *无法覆盖索引(除非索引包含所有列)。 1. 定义:索引包含查询所需的所有字段,无需回表查询数据行。 2. 优势: – 减少 I/O:仅读取索引树,不访问数据文件。 – 提升性能:索引数据量通常远小于行数据。 3. 实现条件: – SELECT 的列必须全部包含在索引中。 – WHERE 条件使用索引列。 4. 示例: – 表 user(id PK, name, age),索引 idx_name_age(name, age)。 – 查询 SELECT name, age FROM user WHERE name = 'Alice' 可覆盖索引(索引含 name,age)。 – 若查询 SELECT * 则需回表查数据行。 后端,MySQL,数据库
10 MySQL 的索引类型有哪些? 1. 主键索引(唯一 + 非空) 2. 唯一索引(列值唯一) 3. 普通索引(加速查询) 4. 全文索引(FULLTEXT,文本搜索) 5. 组合索引(多列联合) 6. 空间索引(GIS 数据)。 1. 数据结构:B+Tree(默认)、Hash(Memory引擎)、Full-Text(全文索引)。 2. 物理存储:聚簇索引(InnoDB主键索引,叶节点存数据)、非聚簇索引(叶节点存主键值)。 3. 逻辑分类:主键索引、唯一索引、普通索引、联合索引、前缀索引。 1. 按数据结构: – B+Tree 索引:默认类型,支持范围查询和排序,适用于等值、范围查询。 – Hash 索引:仅 Memory 引擎支持,等值查询 O(1),不支持范围查询。 – 全文索引:MyISAM/InnoDB 支持,用于文本关键词搜索(如 MATCH AGAINST)。 – R-Tree 索引:空间索引,支持地理数据(如 GIS 体系)。 2. 按逻辑功能: – 主键索引(PRIMARY):唯一且非空,聚簇索引(InnoDB)。 – 唯一索引(UNIQUE):确保列值唯一,可空。 – 普通索引(INDEX):加速查询,无唯一约束。 – 联合索引:多列组合索引,遵循最左前缀匹配。 3. 按存储方式: – 聚簇索引:数据行与索引存储在一起(如 InnoDB 主键索引)。 – 非聚簇索引:索引与数据分离(如 MyISAM 索引,指向数据文件位置)。 后端,MySQL,数据库
11 MySQL 的索引下推是 何? ICP(Index Condition Pushdown):在存储引擎层提前过滤索引条件(即使非最左前缀)。例如索引 (a,b),查询 WHERE a=1 AND b>2,引擎层直接过滤 b>2,减少回表次数。需满足条件:range/ref/eq_ref 查询,且存储引擎支持(默认开启)。 1. 优化场景:针对联合索引的模糊查询(如WHERE A LIKE ‘a%’ AND B=1)。 2. 原理:在存储引擎层过滤B列(无需回表后再由Server层过滤)。 3. 优势:减少回表次数,提升查询效率。 4. 支持版本:MySQL 5.6+默认开启。 1. 定义:Index Condition Pushdown (ICP),将 WHERE 条件中索引列的过滤操作下推到存储引擎层执行,减少回表次数。 2. 影响: – 减少回表:引擎层提前过滤不符合条件的索引项,避免回表后再过滤。 – 提升性能:尤其对联合索引中非最左列的条件过滤有效。 3. 职业流程(以索引 (a,b) 查询 WHERE a>10 AND b=20 为例): – 无 ICP:存储引擎按 a>10 检索索引,回表取完整数据行,再由 Server 层过滤 b=20。 – 有 ICP:存储引擎按 a>10 检索索引后,直接在引擎层过滤 b=20,仅对符合条件的记录回表。 4. 启用条件: – 查询类型为 range/ref/eq_ref。 – 需回表查询(覆盖索引无需 ICP)。 – 需 MySQL 5.6+ 且默认开启(optimizer_switch=index_condition_pushdown=on)。 后端,MySQL,数据库
12 MySQL InnoDB 引擎中的聚簇索引和非聚簇索引有 何区别? 聚簇索引:叶子节点存数据行(主键索引即聚簇索引) 非聚簇索引(二级索引):叶子节点存主键值,需回表查询数据 区别:1. 数据存储位置(聚簇索引与数据共存) 2. 查询效率(聚簇索引无需回表) 3. 数量限制(每表仅一个聚簇索引)。 1. 聚簇索引:叶节点存储行数据(1个表仅1个),主键自动生成。 2. 非聚簇索引:叶节点存储主键值(需二次查找数据)。 3. 性能:聚簇索引范围查询更快(数据物理连续);非聚簇索引可能需回表。 4. 存储:聚簇索引即数据文件;非聚簇索引是独立B+树。 1. 聚簇索引(Clustered Index): – 数据存储:索引的叶子节点直接存储完整数据行。 – 数量限制:每表仅一个聚簇索引(通常为主键)。 – 性能优势:范围查询和排序高效(数据物理有序)。 – 缺点:插入速度依赖插入顺序(乱序插入可能导致页分裂)。 2. 非聚簇索引(Secondary Index): – 数据存储:叶子节点存储主键值(非数据行物理地址)。 – 数量限制:可存在多个。 – 查询流程:需二次查询(回表)——先查主键值,再用主键查聚簇索引获取数据行。 – 覆盖索引优化:若索引包含查询所需字段,可避免回表。 关键区别: | 特性 | 聚簇索引 | 非聚簇索引 | |————–|——————-|——————-| | 存储内容 | 数据行 | 主键值 | | 索引数量 | 1 个 | 多个 | | 查询速度 | 直接访问数据 | 需回表 | | 页分裂影响| 影响大 | 影响小 | 后端,MySQL,数据库
13 MySQL 中的回表是 何? 通过二级索引查询时,需先查索引找到主键值,再根据主键到聚簇索引中取完整数据行。额外 IO 操作导致性能下降。避免方式:使用覆盖索引或优化索引设计。 1. 现象:通过非聚簇索引查询时,需根据主键值回聚簇索引查找完整数据。 2. 代价:额外I/O操作(随机读),降低查询效率。 3. 优化方案:使用覆盖索引(避免回表);减少SELECT *。 1. 定义:当非聚簇索引无法覆盖查询所需字段时,需根据索引查得的主键值,回到聚簇索引中检索完整数据行。 2. 触发条件: – 查询字段未全包含在非聚簇索引中(非覆盖索引)。 – 查询需访问索引外的列。 3. 性能影响: – 额外 I/O:每行数据需两次索引查找(先查二级索引,再查聚簇索引)。 – 随机读:主键值无序时,回表可能导致磁盘随机 I/O。 4. 优化 技巧: – 覆盖索引:索引包含所有查询字段。 – 索引下推(ICP):减少回表前的无效记录。 – 聚簇索引设计:避免过长主键(二级索引需存储主键值)。 后端,MySQL,数据库
14 MySQL 中使用索引一定有效吗? 怎样排查索引效果? 不一定有效。失效场景:函数操作索引列、类型隐式转换、OR 条件未全索引、LIKE 以通配符开头、不符合最左前缀。排查:1. EXPLAIN 看 type/possible_keys/key 2. 观察 rows 字段预估扫描行数 3. 开启慢查询日志分析。 1. 无效场景:索引列计算/函数、隐式类型转换、OR条件未全覆盖、LIKE以通配符开头。 2. 排查工具:EXPLAIN分析(查看key/type/Extra)。 3. 优化手段:force index强制索引; yze table更新统计信息;调整索引顺序。 索引不一定有效的情况: 1. 索引失效场景: – 未遵循最左前缀 制度(联合索引跳过最左列)。 – 对索引列使用函数或表达式(如 WHERE YEAR(create_time)= 2024)。 – 隐式类型转换(如字符串列用数字查询)。 – OR 连接非索引列条件(如 WHERE a=1 OR b=2,若 b 无索引则全表扫描)。 – 数据量小时(优化器认为全表扫描更快)。 2. 排查 技巧: – EXPLAIN 分析: – type 列:index 或 range 表示索引生效,ALL 为全表扫描。 – key 列:实际使用的索引。 – Extra 列:Using index 表示覆盖索引。 – 慢查询日志:记录执行 时刻长的 SQL,分析是否索引缺失。 – 优化器 :SET optimizer_trace="enabled=on"; 查看索引选择 经过。 3. 优化建议: – 避免索引列参与计算。 – 使用覆盖索引减少回表。 – 定期 ANALYZE TABLE 更新索引统计信息。 后端,MySQL,数据库,场景题
15 RabbitMQ 如何实现延迟队列? 两种方式: 1. 死信队列(DLX):消息设置 TTL 过期后转投 DLX 2. 插件(rabbitmq_delayed_message_exchange):使用 x-delayed-type 交换机,消息头设置 x-delay 参数(毫秒)。推荐插件方式避免 TTL 队列阻塞 难题。 1. 死信队列:消息设置TTL过期后进入死信交换机(需配合x-dead-letter-exchange)。 2. 插件方案:rabbitmq-delayed-message-exchange插件(支持毫秒级延迟)。 3. 实现步骤:生产者发延迟消息 → 交换机路由到临时队列 → 到期后转发到业务队列。 1. 死信队列(DLX): – 设置消息 TTL(过期 时刻),过期后成为死信,路由到死信队列。 – 步骤: 1. 创建业务队列 A,绑定死信交换机(DLX)和死信路由键。 2. 消息发送到 A 并设置 TTL。 3. 消息过期后转发至死信队列 B,由消费者处理。 2. 插件 rabbitmq-delayed-message-exchange: – 安装官方延迟插件,创建 x-delayed-message 类型交换机。 – 发送消息时设置 headers.put("x-delay", 5000) 指定延迟 5 秒。 – 消息在交换机延迟后路由到队列。 3. 对比: – 死信队列:无需插件,但 TTL 在队列固定,灵活性差。 – 插件方案:支持消息级延迟,灵活但需安装插件。 后端,消息队列,RabbitMQ
16 MySQL 中的索引数量是否越多越好? 何故? 不是。 缘故:1. 写性能下降(增删改需维护所有索引) 2. 空间占用增加 3. 优化器选择困难可能选错索引。建议:根据查询需求创建必要索引,优先组合索引覆盖多查询,定期分析索引使用率(SHOW INDEX 观察 Cardinality)。 1. 不是。 缘故: – 写性能下降:INSERT/UPDATE需维护所有索引 – 空间占用:索引文件增大磁盘消耗 – 优化器负担:索引过多可能选错执行 规划 2. 优化建议:优先复用联合索引;删除未使用索引(perfor nce_sche 监控)。 不是越多越好, 缘故: 1. 写性能下降: – INSERT/UPDATE/DELETE 需更新所有索引,索引越多写操作越慢。 – 更新可能引起页分裂和索引树调整。 2. 空间占用: – 每个索引占用独立存储空间(尤其 B+Tree 非叶子节点)。 – 大表索引过多显著增加磁盘和内存压力。 3. 优化器负担: – 索引过多时,优化器选择执行 规划 时刻增加,可能选错索引。 4. 建议: – 优先为高频查询和排序字段建索引。 – 使用联合索引替代多个单列索引。 – 定期清理无用索引(通过慢查询日志分析索引使用率)。 后端,MySQL,数据库
17 何故 RocketMQ 不使用 Zookeeper 而选择自己实现 NameServer? 1. 轻量化:NameServer 无选举逻辑(AP 体系),ZK 是 CP 体系较重 2. 性能:NameServer 纯内存操作,吞吐更高 3. 去中心化:各 NameServer 节点独立无 情形,避免 ZK 集群瓶颈 4. RocketMQ 只需简单服务发现,无需 ZK 强一致性。 1. 轻量化:NameServer无 情形(ZK需选举/持久化),部署简单。 2. 性能:ZK写性能瓶颈(RocketMQ元数据更新频繁)。 3. 解耦:减少外部依赖,避免ZK集群故障影响MQ可用性。 4. 功能定制:NameServer仅需服务发现(路由管理),无需ZK强一致性。 1. 设计目标不同: – ZooKeeper:强一致性(CP),适合分布式协调(如选主、配置同步),但写入性能较低。 – NameServer:最终一致性(AP),轻量级元数据管理(Topic 路由信息),追求高可用和低延迟。 2. 简化依赖与部署: – NameServer 无 情形且节点独立,部署简单(RocketMQ 仅需 2-3 台)。 – ZooKeeper 需奇数节点集群部署,运维复杂。 3. 性能优化: – NameServer 基于内存存储路由信息,响应快(微秒级)。 – ZooKeeper 写操作需集群广播,延迟较高。 4. 降低复杂度: – RocketMQ 无需 ZooKeeper 的强一致性,路由信息短暂不一致可接受(客户端有容错机制)。 RocketMQ,消息队列,后端
18 请详细描述 MySQL 的 B+ 树中查询数据的全 经过 1. 从根节点开始二分查找 2. 沿非叶子节点(仅存索引键 + 指针)逐层向下定位 3. 到达叶子节点(存数据行或主键) 4. 在叶子节点链表二分查找目标键 5. 若聚簇索引直接返回数据;若二级索引则取主键回表查询。 1. 磁盘读取:从根节点(常驻内存)开始,加载对应页到内存。 2. 节点遍历:比较查询值,定位下一层子节点指针(非叶节点存键值和指针)。 3. 叶层搜索:到达叶节点后二分查找目标键值(叶节点双向链表串联)。 4. 结局获取:若聚簇索引直接取数据;若二级索引则回表查询。 1. 从根节点开始: – 加载根节点页(常驻内存)。 2. 逐层查找: – 比较查询键与节点中的键值(有序),找到键值所在区间的子节点指针。 – 递归向下查找,直至叶子节点。 3. 叶子节点定位: – 在叶子节点中二分查找目标键: – 等值查询:找到精确匹配的键。 – 范围查询:找到起始键后顺序遍历后续键。 4. 数据获取: – 聚簇索引:叶子节点直接存储数据行,返回行数据。 – 非聚簇索引:叶子节点存储主键值,需回表查询聚簇索引获取完整数据。 5. 示例:查询 id=25(假设 B+ 树高 3): – 根节点:键 [10, 20, 30] → 25 在 20 和 30 间,进入中间节点 P2。 – 中间节点 P2:键 [20, 25, 28] → 25 匹配,进入叶子节点 L3。 – 叶子节点 L3:找到 id=25 的数据行(或主键值)。 后端,MySQL,数据库
19 RabbitMQ 中消息 何时候候会进入死信交换机? 触发条件: 1. 消息被消费端拒绝(basic.reject/nack)且 requeue=false 2. 消息 TTL 过期 3. 队列达到最大长度(溢出丢弃)。需配置队列的 x-dead-letter-exchange 参数绑定死信交换机。 1. 消息被拒绝(basic.reject/nack)且requeue=false。 2. 消息TTL过期。 3. 队列达到最大长度(x- x-length)。 4. 配置:需队列声明时指定x-dead-letter-exchange参数。 消息在 下面内容情况成为死信(Dead Letter)并进入死信交换机(DLX): 1. 消费者拒绝且不重新入队:basic.reject 或 basic.nack 设置 requeue=false。 2. 消息过期:消息设置 TTL 且未被消费。 3. 队列满:队列达到最大长度限制(x- x-length)。 4. 配置 技巧: – 声明队列时设置参数: – x-dead-letter-exchange:指定死信交换机。 – x-dead-letter-routing-key:指定路由键(可选)。 RabbitMQ,消息队列,后端
20 何故 MySQL 选择使用 B+ 树作为索引结构? 对比 B 树: 1. 更矮胖:非叶子节点不存数据,单节点存更多键,减少 IO 次数 2. 范围查询高效:叶子节点双向链表串联 3. 查询稳定:所有查询均需到叶子节点, 时刻复杂度稳定 O(log n) 4. 更适合磁盘存储:节点 大致匹配磁盘页。 1. 磁盘友好:矮胖树结构减少I/O次数(节点 大致=磁盘页)。 2. 范围查询:叶节点双向链表支持高效范围扫描。 3. 查询稳定:所有查询均需到叶层( 时刻复杂度O(log n))。 4. 对比优势:比B树更适合磁盘存储(非叶节点无数据,单页存更多指针)。 相比 B 树、哈希、红黑树等,B+ 树优势: 1. 磁盘 I/O 友好: – 树矮胖(3-4 层存百万级数据),减少磁盘访问次数。 – 节点 大致固定(如 16KB),匹配磁盘页 大致。 2. 范围查询高效: – 叶子节点形成有序双向链表,范围查询直接遍历链表。 3. 查询更稳定: – 所有查询均需到叶子节点,路径长度相同(O(log n))。 4. 存储效率高: – 非叶子节点仅存键和指针(不存数据),单节点可存更多键,降低树高。 – 数据全存叶子节点,避免非叶子节点数据冗余。 后端,MySQL,数据库
21 RabbitMQ 中无法路由的消息会去到 何处? 取决于 Mandatory 参数: 1. Mandatory=true:通过回调 ReturnListener 返回生产者 2. Mandatory=false(默认):直接丢弃。建议:设置备份交换机(Alternate Exchange)接收无法路由的消息。 1. 前提:生产者发送消息时设置 ndatory=true。 2. 处理机制:触发ReturnListener回调返回消息。 3. 备选方案:通过alternate-exchange指定备用交换机(未配置则丢弃)。 1. 默认丢弃:未配置备用策略时,无法路由的消息直接被丢弃。 2. 备用交换机(Alternate Exchange): – 声明交换机时设置 alternate-exchange 参数指向备用交换机。 – 无法路由的消息转发到备用交换机,再由其路由到特定队列处理(如记录日志或告警)。 3. 示例配置: – 创建备用交换机 ae 和队列 unrouted_queue 并绑定。 – 声明主交换机:arguments.put("alternate-exchange", "ae")。 RabbitMQ,消息队列,后端
22 MySQL 三层 B+ 树能存 几许数据? 假设: – 页 大致 16KB – 主键 BIGINT(8B),指针 6B – 非叶节点每页存约 16KB/(8B+6B)≈1170 键 – 叶节点存数据行(假设1KB/行),每页约16行 计算:根节点有 1170 指针 → 二层 1170 页 → 三层 1170×1170≈137 万页 → 总行数 ≈ 137万 × 16 = 2190 万行。 1. 假设:页 大致16KB,主键BIGINT(8B),指针6B。 2. 非叶节点:单页存约 16KB/(8B+6B)≈1170个键值+指针。 3. 叶节点:假设行数据1KB,单页存约16行。 4. 估算:三层B+树总行数 = 1170 * 1170 * 16 ≈ 2190万行。 计算依据: 1. 假设条件: – 页 大致 16KB,主键 BIGINT(8B),指针 6B。 – 非叶子节点:存主键 + 指针 → 单条记录 14B。 – 叶子节点:存数据行(假设单行 1KB)。 2. 计算 经过: – 根节点:可存记录数 = 16KB / 14B ≈ 1170 条。 – 第二层:1170 个节点,每节点存 1170 条 → 总记录数 = 1170 × 1170 ≈ 137 万。 – 第三层(叶子):137 万叶子节点,每节点存行数 = 16KB / 1KB = 16 行 → 总行数 = 137 万 × 16 ≈ 2190 万。 3. 实际场景: – 行 大致、主键类型、填充因子影响实际容量。 – 通常 3 层 B+ 树可支持千万级数据存储。 后端,MySQL,数据库
23 Kafka 何故要抛弃 Zookeeper? 1. 简化架构:减少外部依赖,运维更简单 2. 提升扩展性:元数据管理内置(KIP-500),突破 ZK 集群写瓶颈 3. 增强稳定性:避免 ZK 故障导致 Kafka 不可用 4. 改进控制器选举:用 Raft 协议替代 ZK 监听。从 Kafka 2.8 起支持 KRaft 模式。 1. 简化架构:KIP-500用自管理元数据(KRaft)替代ZK。 2. 提升性能:减少网络开销(ZK成为吞吐瓶颈)。 3. 运维减负:避免维护两套分布式 体系(ZK配置/扩容复杂)。 4. 更强一致性:Raft协议保证元数据安全。 1. 简化架构: – ZooKeeper 是独立集群,运维复杂(需维护两套 体系)。 – Kafka 2.8+ 引入自管理元数据(KRaft 模式),移除 ZooKeeper 依赖。 2. 提升性能: – ZooKeeper 写操作需集群广播,成为性能瓶颈(尤其分区数多时)。 – KRaft 模式通过 Raft 协议直接管理元数据,降低延迟。 3. 增强扩展性: – ZooKeeper 集群规模受限(通常 ≤7 节点),影响 Kafka 集群扩展。 – KRaft 模式可线性扩展 Controller 节点。 4. 降低故障风险: – ZooKeeper 故障导致 Kafka 不可用。 – KRaft 模式减少外部依赖, 进步 体系自治性。 Kafka,Zookeeper,消息队列
24 详细描述一条 SQL 语句在 MySQL 中的执行 经过。 1. 连接器:建立连接,权限验证 2. 查询缓存:若开启缓存且命中则直接返回(8.0 后移除) 3. 解析器:词法/语法分析,生成语法树 4. 优化器:选择索引,生成执行 规划 5. 执行器:调用存储引擎接口 6. 存储引擎(InnoDB):读写数据(缓冲池、磁盘交互) 7. 返回 结局。 1. 连接器:管理连接,权限验证。 2. 分析器:语法解析(AST),语义检查。 3. 优化器:生成执行 规划,选择索引(基于成本模型)。 4. 执行器:调用存储引擎接口读写数据。 5. 存储引擎(InnoDB):执行索引扫描/回表等操作。 1. 连接器: – 管理客户端连接,验证权限。 – 建立连接后维持会话(若为长连接)。 2. 查询缓存(8.0 已移除): – 历史版本中缓存 SELECT 结局,因失效频繁被废弃。 3. 分析器: – 词法分析:拆分 SQL 为关键词(如 SELECT)、表达式等。 – 语法分析:检查语 确性,生成抽象语法树(AST)。 4. 优化器: – 基于统计信息选择执行 规划(如索引选择、JOIN 顺序)。 – 生成执行 规划树。 5. 执行器: – 调用存储引擎接口执行 规划。 – 操作流程: 1. 开启事务(若需)。 2. 根据索引定位数据。 3. 调用存储引擎读写接口。 4. 返回 结局集。 6. 存储引擎(如 InnoDB): – 执行数据读写(访问内存缓冲池或磁盘)。 – 通过 undo log 实现回滚,redo log 保证持久性。 7. 返回 结局:将 结局集返回客户端。 后端,MySQL,数据库
25 Kafka 中 Zookeeper 的 影响? 在旧版本(KRaft 前)中负责: 1. Broker 注册:维护节点列表与 情形 2. Topic 配置:存储分区、副本分配信息 3. 控制器选举:选主 Broker 管理分区leader 4. 消费者组:保存 offset 和组成员(新版 offset 存内部 Topic)。 1. 元数据存储:Topic分区、Broker列表、ISR 。 2. 控制器选举:选举Controller Broker(管理分区leader)。 3. 集群协调:Broker注册/下线监听。 4. 注意:Kafka 3.0+逐步弃用ZK(迁移至KRaft)。 1. 元数据管理:存储集群元数据(Broker 列表、Topic 配置、分区分配信息)。 2. 控制器选举:选举集群控制器(Controller),负责分区 Leader 选举和副本管理。 3. Broker 注册与发现:Broker 启动时向 ZooKeeper 注册,客户端通过 ZooKeeper 发现可用 Broker。 4. 分区 情形跟踪:监控分区 Leader 情形变化(如故障转移)。 5. ACL 控制:存储访问控制列表(Kafka 0.9+ 已迁移至 Broker)。 注:Kafka 2.8+ 逐步移除 ZooKeeper 依赖(KRaft 模式)。 Kafka,消息队列,后端
26 MySQL 是 怎样实现事务的? 通过 InnoDB 引擎实现: 1. 原子性(A):Undo Log(回滚日志)记录修改前数据 2. 一致性(C):由 A+I+D 共同保证 3. 隔离性(I):锁 + MVCC(多版本并发控制) 4. 持久性(D):Redo Log(重做日志)保证崩溃恢复。 1. 日志机制:redo log(崩溃恢复,保证持久性),undo log(事务回滚/MVCC)。 2. 锁机制:行锁/间隙锁(保证隔离性)。 3. MVCC:多版本并发控制(ReadView+undo log链),实现非锁定读。 4. 两阶段提交:binlog与redo log的协调(保证主从一致)。 1. 日志机制: – Redo Log:物理日志,保证持久性(事务提交前先写 Redo)。 – Undo Log:逻辑日志,保证原子性(回滚时逆向操作)。 2. 锁机制: – 行锁(Record Lock)、间隙锁(Gap Lock)、Next-Key Lock 实现隔离性。 3. MVCC(多版本并发控制): – 通过 ReadView 和版本链实现非锁定读(避免读写冲突)。 4. 事务提交: – 二阶段提交(2PC)保证 binlog 与存储引擎日志一致性。 后端,MySQL,数据库
27 何故 Java 8 移除了永久代(PermGen)并引入了元空间(Metaspace)? 1. 永久代 大致难调优:易触发 OOM 2. 类元数据 生活周期与类加载器绑定,回收复杂 3. 元空间使用本地内存(非 JVM 堆),默认无上限(受物理内存限制) 4. 简化 HotSpot 代码,合并 JRockit 特性。元空间 GC 由类加载器触发。 1. 内存管理:PermGen 大致难调优(易OOM);元空间使用本地内存(自动扩展)。 2. 垃圾回收:PermGen由Full GC回收效率低;元空间由MetaspaceSize控制。 3. 兼容性:为HotSpot与JRockit合并做准备。 4. 存储内容:存类元信息(永久代存部分移至堆,如字符串常量池)。 1. 永久代 难题: – 固定 大致易导致 java.lang.OutOfMemoryError: PermGen space。 – FGC 无法回收类信息(回收效率低)。 2. 元空间优势: – 内存管理:使用本地内存(非 JVM 堆),默认无上限(受物理内存限制)。 – 自动扩容:按需申请内存,避免 OOM。 – 垃圾回收:元数据由 GC 自动回收(类加载器死亡时)。 3. 性能提升: – 减少字符串常量池迁移开销(字符串池移至堆内)。 JVM,Java
28 说一下 Kafka 中关于事务消息的实现? 保证跨分区原子写入: 1. 事务协调器:每个 Producer 对应一个,管理事务 情形 2. 事务ID:标识跨会话事务 3. 两阶段提交: – 发送消息到事务 Topic(未提交) – Commit:写事务结束标记到 __transaction_state 4. 消费者设置 isolation.level=read_committed 过滤未提交消息。 1. 事务协调器:每个Producer对应一个TransactionCoordinator。 2. 两阶段协议: – 开启事务:向Coordinator注册 – 发送消息:消息标记PID(Producer ID) – 提交事务:写事务标记到__transaction_state主题 3. 消费端:需设置isolation.level=read_committed。 1. 事务协调器(Transaction Coordinator): – 每个 Producer 绑定唯一协调器,管理事务 情形。 2. 事务日志(__transaction_state): – 存储事务 情形(Begin/Commit/Abort)。 3. 两阶段提交(2PC)流程: – Begin:Producer 向协调器注册事务。 – Add Messages:发送消息(标记为未提交)。 – Commit: 1. 协调器写 Prepare Commit 到事务日志。 2. 向所有涉及 Broker 写事务标记。 3. 写 Commit 到日志后消息可见。 4. 隔离性: – 读隔离:消费者设置 isolation.level=read_committed 过滤未提交消息。 Kafka,消息队列,后端
29 MySQL 事务的二阶段提交是 何? 用于保证 binlog 和 redo log 一致性: 1. Prepare 阶段:InnoDB 写 redo log(prepare 情形) 2. Commit 阶段: a. 写 binlog b. InnoDB 写 redo log(commit 情形) 崩溃恢复时:若 binlog 完整则提交事务,否则回滚。 1. 目的:保证redo log与binlog的一致性(跨存储引擎)。 2. 阶段: – Prepare:InnoDB写redo log(prepare 情形) – Commit:Server层写binlog后通知InnoDB提交(redo log写commit) 3. 崩溃恢复:根据binlog 情形决定回滚(无binlog)或提交(有binlog)。 1. 目的:保证 binlog 与存储引擎(如 InnoDB)事务日志的一致性。 2. 阶段流程: – Prepare 阶段: – InnoDB 写 redo log 并置为 prepare 情形。 – Server 层写 binlog 到内存(未刷盘)。 – Commit 阶段: – Server 层写 binlog 到磁盘。 – InnoDB 提交事务(redo log 置为 commit)。 3. 崩溃恢复: – 若 binlog 完整:提交事务(有 redo log)。 – 若 binlog 不完整:回滚事务(无对应 binlog)。 后端,MySQL,数据库
30 说一下 RocketMQ 中关于事务消息的实现? 半消息机制: 1. 生产者发送半消息(对消费者不可见) 2. Broker 持久化半消息,返回 ACK 3. 生产者执行本地事务 4. 根据本地事务 结局提交/回滚: – 提交:消息可见,投递给消费者 – 回滚:删除消息 5. Broker 定时回查:若生产者未响应,回查事务 情形。 1. 半消息:生产者发送事务消息(对消费者不可见)。 2. 本地事务:生产者执行本地业务逻辑。 3. 事务 情形:生产者提交/回滚通知Broker。 4. 消息提交:Broker将半消息转为正式消息(或丢弃)。 5. 补偿机制:Broker回查未确认事务 情形(checkLocalTransaction)。 1. 半消息(Half Message): – 发送暂不可消费的消息(存储于 RMQ_SYS_TRANS_HALF_TOPIC)。 2. 事务 情形回查: – Broker 回调 Producer 检查本地事务 情形(若半消息未确认)。 3. 提交/回滚流程: – Commit:半消息转存 诚恳 Topic,消费者可见。 – Rollback:丢弃半消息。 4. 补偿机制: – Producer 实现 TransactionListener 处理本地事务执行和回查。 RocketMQ,后端,消息队列
31 MySQL 中长事务可能会导致哪些 难题? 1. 锁竞争:持有锁 时刻长,阻塞其他事务 2. 回滚段膨胀:Undo Log 无法及时清理 3. 数据一致性风险:未提交修改对其他事务可见(取决于隔离级别) 4. 主从延迟:Binlog 需等待事务提交 5. 内存消耗:事务相关缓冲区无法释放。 1. 锁阻塞:长期持有锁导致其他事务等待(甚至死锁)。 2. 回滚段膨胀:undo log无法及时清理占用空间。 3. 数据一致性:旧版本数据无法被purge(MVCC依赖)。 4. 主从延迟:Binlog同步积压。 5. 监控:infor tion_sche .innodb_trx查询长事务。 1. 锁资源占用: – 长事务持有锁不释放,阻塞其他查询(甚至死锁)。 2. 回滚段膨胀: – Undo log 无法及时清理,占用大量存储空间。 3. MVCC 版本链过长: – 老版本数据无法 purge,影响查询性能。 4. 主从延迟: – Binlog 在事务提交后才写入,从库同步延迟。 5. 解决方案: – 监控 infor tion_sche .innodb_trx。 – 设置事务超时 时刻(innodb_rollback_on_timeout)。 后端,MySQL,数据库
32 RocketMQ 的事务消息有 何缺点?你还了解过别的事务消息实现吗? 缺点: 1. 消息可见延迟(需等待本地事务 结局) 2. 回查机制可能重复执行本地事务 3. 不保证全局事务一致性(需结合业务补偿) 其他实现: – Kafka 事务:基于生产者幂等和事务协调器 – 最大努力通知:通过异步重试保证最终一致。 1. 缺点: – 最终一致性(非强一致) – 事务 情形回查可能失败 – 网络波动导致消息丢失风险 2. 其他方案: – Kafka事务消息(Exactly-Once语义) – 本地事务表+消息表(事务中写DB与消息) RocketMQ 缺点: 1. 消息可见延迟:需等待二次确认(半消息→提交)。 2. 事务 情形回查不可靠:网络故障可能导致消息 情形不一致。 3. 不保证原子性:本地事务与消息发送可能一个成功一个失败。 其他实现: 1. Kafka 事务: – 优点:精确一次语义(EOS)。 – 缺点:性能开销大。 2. 本地消息表: – 业务库建消息表,与本地事务同库同事务提交。 – 异步任务补偿发送(最终一致性)。 消息队列,后端,RocketMQ
33 MySQL 中的 MVCC 是 何? 多版本并发控制:通过数据快照实现非锁定读。InnoDB 实现方式: 1. 每行数据隐含 DB_TRX_ID(事务ID)和 DB_ROLL_PTR(回滚指针) 2. ReadView 结构:记录活跃事务ID列表,用于判断数据版本可见性 3. 快照读(如 SELECT)基于 ReadView 访问 Undo Log 中的历史版本。 1. 多版本并发控制:通过数据快照实现非锁定读。 2. 核心组件: – 隐藏字段:DB_TRX_ID(事务ID)、DB_ROLL_PTR(回滚指针) – undo log:构建版本链 – ReadView:可见性判断(活跃事务列表) 3. 隔离级别:REPEATABLE READ(首次读建立ReadView)和READ COMMITTED(每次读重建ReadView)。 1. 定义:多版本并发控制(Multi-Version Concurrency Control),非锁定读机制。 2. 核心组件: – Undo Log:存储数据历史版本链。 – ReadView:事务快照,包含活跃事务 ID 列表。 3. 可见性 制度: – 数据行隐藏字段:DB_TRX_ID(创建事务ID)、DB_ROLL_PTR(回滚指针)。 – 数据版本对当前事务可见需满足: – DB_TRX_ID < 当前 ReadView 最小活跃 ID(已提交)。 – DB_TRX_ID 不在 ReadView 活跃事务列表中。 4. 隔离级别支持: – RC:每次读生成新 ReadView(可能不可重复读)。 – RR:首次读生成 ReadView,后续复用(避免不可重复读)。 后端,MySQL,数据库
34 何故需要消息队列? 核心 价格: 1. 解耦:生产者和消费者独立演进 2. 异步:非必要操作异步执行,提升响应速度 3. 削峰:缓冲突发流量,保护下游 体系 4. 可靠:消息持久化,确保不丢失 5. 扩展:通过分区水平扩展消费者。 1. 解耦:生产/消费逻辑分离。 2. 异步:非阻塞调用(提升响应速度)。 3. 削峰:缓冲突发流量(避免 体系过载)。 4. 顺序保证:分区有序性。 5. 最终一致性:分布式事务场景。 1. 解耦: – 生产者和消费者异步通信, 体系间不直接依赖。 2. 削峰填谷: – 突发流量写入队列,消费者按能力处理(避免 体系崩溃)。 3. 异步提速: – 主流程快速响应(如订单创建),耗时操作异步执行(如发短信)。 4. 顺序保证: – 分区队列保证消息顺序性(如 Kafka)。 5. 最终一致性: – 分布式事务场景(如库存扣减与订单创建)。 消息队列,后端
35 MySQL 中的事务隔离级别有哪些? 1. 读未提交(Read Uncommitted):可能脏读 2. 读已提交(Read Committed):避免脏读,可能不可重复读 3. 可重复读(Repeatable Read):避免脏读、不可重复读,可能幻读(InnoDB 通过 MVCC 部分避免) 4. 串行化(Serializable):强制事务串行执行。 1. READ UNCOMMITTED:读未提交(可能脏读)。 2. READ COMMITTED:读已提交(避免脏读,可能不可重复读)。 3. REPEATABLE READ:可重复读(InnoDB默认,避免脏读/不可重复读,可能幻读)。 4. SERIALIZABLE:串行化(最高隔离,锁表实现)。 1. 读未提交(Read Uncommitted): – 可能读到未提交数据(脏读)。 2. 读已提交(Read Committed): – 只读已提交数据(避免脏读),但可能不可重复读。 3. 可重复读(Repeatable Read): – 同一事务多次读取 结局一致(避免脏读、不可重复读),InnoDB 通过 MVCC 避免幻读。 4. 串行化(Serializable): – 事务串行执行(避免所有并发 难题),性能最低。 后端,MySQL,数据库
36 说一下消息队列的模型有哪些? 1. 点对点(Queue):消息被一个消费者消费 2. 发布/订阅(Topic):消息广播给所有订阅者 3. 增强模型: – Kafka 分区模型:同一分区内顺序消费 – RabbitMQ Exchange:Direct/Fanout/Topic/Headers 路由 制度。 1. 点对点(Queue):消息被单个消费者消费(RabbitMQ)。 2. 发布订阅(Topic):消息广播给所有订阅者(Kafka/RocketMQ)。 3. 混合模型: – Kafka:Consumer Group内点对点,Group间发布订阅 – RabbitMQ:Exchange路由策略(direct/fanout/topic) 1. 点对点(P2P): – 消息仅被一个消费者消费(如 RabbitMQ Queue)。 2. 发布/订阅(Pub/Sub): – 消息广播到所有订阅者(如 Kafka Topic)。 3. 主题模型(Topic): – 消费者按 Topic 订阅(如 RocketMQ Tag 过滤)。 4. 请求/响应(Request/Reply): – 生产者等待消费者响应(如 RabbitMQ RPC)。 消息队列,后端
37 MySQL 默认的事务隔离级别是 何? 何故选择这个级别? 默认级别:可重复读(Repeatable Read)。 缘故: 1. 平衡性能与一致性:避免脏读和不可重复读 2. InnoDB 通过 MVCC 和 Next-Key Lock 减少幻读 3. 适合多数业务场景(如账户余额查询需 结局稳定)。 1. 默认级别:REPEATABLE READ(可重复读)。 2. 缘故: – 平衡性能与一致性:避免不可重复读(比READ COMMITTED严格) – 实际防幻读:InnoDB通过间隙锁(Gap Lock)解决幻读 – 历史兼容性:早期版本遗留设定 1. 默认级别:可重复读(Repeatable Read)。 2. 选择 缘故: – 平衡性能与一致性:避免脏读和不可重复读,且通过 MVCC 和 Next-Key Lock 解决幻读。 – 历史兼容性:早期版本默认 RR,升级保持兼容。 3. 与标准 SQL 差异: – SQL 标准默认 RC,但 InnoDB 在 RR 下通过 MVCC 提供高性能并发。 后端,MySQL,数据库
38 谈谈你了解的最常见的几种设计模式,说说他们的应用场景 1. 单例模式(全局唯一实例):配置管理、线程池 2. 工厂模式(解耦创建逻辑):Spring BeanFactory 3. 代理模式(控制访问):AOP 切面、RPC 动态代理 4. 观察者模式(事件通知):GUI 事件、消息队列 5. 策略模式(算法切换):支付方式选择、排序算法切换。 1. 单例模式:全局唯一实例(如Spring Bean)。 2. 工厂模式:解耦对象创建(如JDBC连接池)。 3. 观察者模式:事件通知(如GUI事件监听)。 4. 代理模式:增强对象功能(如AOP动态代理)。 5. 策略模式:算法切换(如支付方式选择)。 1. 单例模式: – 场景:全局配置对象、线程池、数据库连接池(如 Spring Bean 默认单例)。 2. 工厂模式: – 场景:JDBC 的 DriverManager.getConnection()、Spring BeanFactory。 3. 代理模式: – 场景:Spring AOP、RPC 远程调用(动态代理)。 4. 观察者模式: – 场景:事件监听(如 GUI 按钮点击)、消息队列发布订阅。 5. 策略模式: – 场景:支付方式选择(微信/支付宝)、排序算法切换(Comparator)。 设计模式
39 MySQL 中有哪些锁类型? 按粒度: 1. 表锁:MyISAM 默认,开销小但并发低 2. 行锁:InnoDB 默认,分记录锁(锁定单行)、间隙锁(锁定范围)、临键锁(记录+间隙) 按行为:共享锁(S 锁,读锁)、排他锁(X 锁,写锁)、意向锁(IS/IX)。 1. 按粒度: – 表锁(MyISAM) – 行锁(InnoDB) 2. 按功能: – 共享锁(S锁):读锁 – 排他锁(X锁):写锁 3. 独特锁: – 意向锁(IS/IX):快速判断表级冲突 – 间隙锁(Gap Lock):防止幻读 – 临键锁(Next-Key Lock):行锁+间隙锁 1. 按粒度: – 表锁:MyISAM 默认,开销小但并发低。 – 行锁:InnoDB 支持,并发高但开销大。 2. 按功能: – 共享锁(S Lock):读锁,允许多事务并发读。 – 排他锁(X Lock):写锁,独占数据。 3. InnoDB 行锁变种: – 记录锁(Record Lock):锁索引记录。 – 间隙锁(Gap Lock):锁索引区间(防幻读)。 – 临键锁(Next-Key Lock):记录锁+间隙锁(RR 隔离默认)。 4. 意向锁: – IS/IX 锁:表级锁,快速判断表内是否有行锁。 后端,MySQL,数据库
40 何是策略模式?一般用在 何场景? 定义:封装可互换的算法族,使它们独立于客户端变化。场景: 1. 支付方式选择(支付宝/微信/银行卡) 2. 排序算法切换(快速/归并/堆排序) 3. 折扣策略(满减/折扣率/立减)。 1. 定义:定义算法族并封装,使它们可互相替换。 2. 核心:Context持有Strategy接口,运行时切换具体实现。 3. 场景: – 支付方式选择(微信/支付宝/银行卡) – 排序算法切换(快速/归并/堆排序) – 折扣策略(满减/打折/积分抵扣) 1. 定义:定义算法族并封装,使它们可互换,让算法独立于使用它的客户。 2. 核心组件: – Context:持有具体策略的引用。 – Strategy:策略接口(定义算法)。 – ConcreteStrategy:具体策略实现。 3. 应用场景: – 支付方式选择(微信/支付宝/银行卡)。 – 排序算法切换(冒泡/快排/归并)。 – 折扣策略(满减/折扣率/固定优惠)。 4. 优点:避免多重条件判断,便于扩展新策略。 设计模式
41 MySQL 的乐观锁和悲观锁是 何? 悲观锁:假定冲突高,先加锁再操作(SELECT … FOR UPDATE) 乐观锁:假定冲突低,通过版本号/ 时刻戳校验(UPDATE … SET version=new_version WHERE version=old_version),失败重试。 1. 悲观锁: – 想法:先加锁再操作 – 实现:SELECT … FOR UPDATE(行锁) – 场景:写密集操作 2. 乐观锁: – 想法:无锁提交,冲突检测 – 实现:版本号/ 时刻戳(UPDATE … SET version=new_version WHERE version=old_version) – 场景:读多写少 1. 悲观锁: – 想法:假设会冲突,操作前先加锁(如 SELECT … FOR UPDATE)。 – 场景:写多读少,强一致性要求高。 2. 乐观锁: – 想法:假设无冲突,提交时检查版本号(如 CAS 或版本字段)。 – 实现: – 版本号:UPDATE … SET version=version+1 WHERE id=1 AND version=old_version。 – 时刻戳:类似版本号。 – 场景:读多写少,冲突概率低。 后端,MySQL,数据库
42 何是 职责链模式?一般用在 何场景? 定义:多个处理器依次处理请求,每个处理器决定是否传递给下一个。场景: 1. 过滤器链(Servlet Filter) 2. 审批流程(经理→总监→CEO) 3. 异常处理(逐级捕获)。 1. 定义:多个处理器依次处理请求(形成链)。 2. 核心:Handler持有next对象,处理完传递请求。 3. 场景: – 过滤器链(如Servlet Filter) – 审批流程(如报销多级审批) – 异常处理(逐级上报) 1. 定义:多个处理器依次处理请求,形成链条,请求沿链传递直到被处理。 2. 核心组件: – Handler:处理接口(定义处理 技巧和后继设置)。 – ConcreteHandler:具体处理器(可决定处理或传递)。 3. 应用场景: – 过滤器链(如 Servlet Filter、Spring Security)。 – 审批流程(如报销多级审批)。 – 异常处理(多层 catch)。 4. 优点:解耦请求发送者和接收者,动态增减处理器。 设计模式
43 MySQL 中如果发生死锁应该 怎样解决? 1. 设置 innodb_deadlock_detect=on(自动检测,默认开启) 2. InnoDB 自动回滚代价较小的事务 3. 人工处理:分析 SHOW ENGINE INNODB STATUS 的死锁日志 4. 预防:按固定顺序访问资源、减小事务粒度、使用索引减少锁范围。 1. 自动处理:InnoDB自动检测并回滚代价小的事务。 2. 手动排查: – SHOW ENGINE INNODB STATUS查看死锁日志 – 监控infor tion_sche .innodb_lock_waits 3. 预防措施: – 事务保持短小 – 按固定顺序访问资源 – 合理设计索引(减少锁范围) 1. 死锁检测: – InnoDB 自动检测(innodb_deadlock_detect=on),回滚代价小的事务。 2. 手动处理: – 查询死锁日志:SHOW ENGINE INNODB STATUS。 – 终止会话:KILL [session_id]。 3. 预防措施: – 按固定顺序访问多张表(如先 A 后 B)。 – 小事务及时提交(减少锁持有 时刻)。 – 为高频冲突场景加表锁(慎用)。 4. 重试机制:代码捕获死锁异常(MySQL error 1213)后重试。 后端,MySQL,数据库
44 何是模板 技巧模式?一般用在 何场景? 定义:父类定义算法骨架,子类重写特定步骤。场景: 1. JdbcTemplate 执行流程(获取连接→执行SQL→处理 结局→释放连接) 2. 职业流审批(提交→各级审批→归档) 3. 框架 生活周期初始化(init()→destroy())。 1. 定义:父类定义算法骨架,子类实现具体步骤。 2. 核心:抽象类声明final模板 技巧(调用抽象钩子 技巧)。 3. 场景: – 框架流程固定(如Spring JdbcTemplate) – 业务流程标准化(如订单处理:校验→计算→支付→通知) 1. 定义:定义算法骨架(模板 技巧),子类重写特定步骤而不改变结构。 2. 核心组件: – AbstractClass:模板 技巧(final)+ 抽象步骤(钩子 技巧)。 – ConcreteClass:实现具体步骤。 3. 应用场景: – 框架流程(如 Spring JdbcTemplate 的 execute())。 – 业务审批流程(固定步骤,不同实现)。 – 算法骨架(如排序的 compareTo() 由子类实现)。 4. 优点:复用代码,扩展性强(好莱坞 制度:不要调用我们,我们会调用你)。 设计模式
45 MySQL 中 count(*)、count(1) 和 count(字段名) 有 何区别? count():统计所有行数(含 NULL) count(1):统计行数(等效 count()) count(字段):统计该字段非 NULL 的行数。性能:InnoDB 中 count(*)≈count(1)>count(主键)>count(非索引字段),因需扫描索引或表。 1. count():统计所有行(含NULL),优化后不取值。 2. count(1):统计所有行(含NULL),等效count()。 3. count(字段名):统计该字段非NULL的行数。 4. 性能:无WHERE时count(*)≈count(1)>count(字段名)(二级索引优先) 1. count(*): – 统计所有行数(含 NULL),优化器自动选择最优索引。 2. count(1): – 统计所有行数(含 NULL),性能 ≈ count()(InnoDB 遍历最小索引)。 3. count(字段名): – 统计非 NULL 行数,若字段无索引则全表扫描。 4. 性能对比: – count() = count(1) > count(主键) > count(非索引字段) 5. 建议:用 count(*) 替代 count(1),语义更清晰。 后端,MySQL,数据库
46 何是观察者模式?一般用在 何场景? 定义:主题(Subject) 情形变化时通知所有观察者(Observer)。场景: 1. 事件监听(按钮点击通知) 2. 消息发布订阅(MQ 消费者订阅 Topic) 3. 数据绑定(Vue.js 响应式更新)。 1. 定义:对象(Subject) 情形变化时通知所有依赖对象(Observer)。 2. 核心:Subject维护Observer列表,提供注册/通知 技巧。 3. 场景: – 事件驱动(如GUI按钮点击事件) – 发布订阅(如消息队列消费者) – 模型-视图更新(如MVC架构) 1. 定义:对象(Subject)维护观察者列表, 情形变化时自动通知所有观察者。 2. 核心组件: – Subject:被观察对象(注册/移除观察者,通知 技巧)。 – Observer:观察者接口(定义更新 技巧)。 – ConcreteObserver:具体观察者。 3. 应用场景: – 事件驱动(如 GUI 按钮点击监听)。 – 发布订阅(如消息队列消费者)。 – 数据监控(如股票价格变动通知)。 4. 优点:解耦观察者和被观察者,支持广播通信。 设计模式
47 MySQL 中 怎样进行 SQL 调优? 步骤: 1. 定位慢 SQL:慢查询日志、EXPLAIN 2. 分析执行 规划:已关注 type(ALL→ref/range)、key、rows、Extra(Using filesort/temporary) 3. 优化手段: – 加索引(覆盖索引、最左前缀) – 避免 SELECT * – 分页优化(避免 OFFSET 过大) – 重构查询(拆复杂 SQL) 4. 参数调优:缓冲池 大致、日志配置。 1. 分析工具:EXPLAIN查看执行 规划;慢查询日志。 2. 索引优化:避免索引失效;覆盖索引;联合索引最左前缀。 3. SQL改写:减少子查询;分页优化(避免OFFSET大偏移)。 4. 结构优化:大表拆分;字段类型精简。 5. 参数调整:调整buffer_pool_size等。 1. 分析工具: – EXPLAIN:查看执行 规划(已关注 type、key、rows、Extra)。 – 慢查询日志:定位耗时 SQL。 2. 索引优化: – 避免索引失效(如函数转换、隐式类型转换)。 – 使用覆盖索引减少回表。 – 联合索引最左前缀匹配。 3. SQL 改写: – 分页优化(避免 OFFSET 过大,用 ID 分页)。 – 避免 SELECT *,只取所需字段。 – 分解复杂 JOIN(小表驱动大表)。 4. 参数调优: – 调整 innodb_buffer_pool_size(通常为物理内存 70%)。 – 优化排序区(sort_buffer_size)。 后端,MySQL,数据库,场景题
48 何是代理模式?一般用在 何场景? 定义:为对象提供代理以控制访问。场景: 1. 远程代理(RPC 调用隐藏网络细节) 2. 虚拟代理(延迟加载大对象) 3. 保护代理(权限校验) 4. 动态代理(AOP 切面增强)。 1. 定义:为对象提供代理以控制访问。 2. 类型: – 静态代理:手动实现代理类 – 动态代理:运行时生成(如JDK Proxy/CGLib) 3. 场景: – AOP切面(日志/事务) – 远程调用(RPC stub) – 权限控制(拦截非法请求) 1. 定义:为对象提供代理,控制对原对象的访问(增强功能)。 2. 类型: – 静态代理:手动编写代理类(硬编码)。 – 动态代理:运行时生成代理类(如 JDK Proxy、CGLIB)。 3. 应用场景: – AOP 编程(如 Spring 事务管理)。 – 远程代理(如 RPC 调用)。 – 保护代理(权限控制)。 – 虚拟代理(延迟加载大对象)。 4. 优点: 责任清晰(原对象专注核心功能),扩展性强。 设计模式
49 说说 Spring 启动 经过? 核心流程: 1. 加载配置:读取 XML/注解配置 2. 创建 BeanFactory:DefaultListableBeanFactory 3. 解析并注册 BeanDefinition 4. 执行 BeanFactoryPostProcessor(如 PropertySourcesPlaceholderConfigurer) 5. 实例化单例 Bean(调用构造函数、依赖注入) 6. 初始化 Bean(执行 Aware 接口、BeanPostProcessor、init-method) 7. 发布 ContextRefreshedEvent 事件。 1. 加载配置:读取XML/注解配置生成BeanDefinition。 2. 实例化:调用BeanFactoryPostProcessor(修改Bean定义)。 3. 依赖注入:填充属性(@Autowired)。 4. 初始化:调用InitializingBean.afterPropertiesSet()和init-method。 5. AOP代理:BeanPostProcessor生成代理对象。 6. 完成:容器就绪(ContextRefreshedEvent事件)。 1. 加载配置: – 读取 XML/注解配置,创建 BeanDefinition。 2. 实例化 BeanFactory: – DefaultListableBeanFactory 存储 Bean 定义。 3. 执行 BeanFactoryPostProcessor: – 修改 BeanDefinition(如 PropertySourcesPlaceholderConfigurer 解析占位符)。 4. 实例化 Bean: – 调用构造函数创建 Bean 实例。 5. 依赖注入: – 通过 setter 或字段注入属性。 6. 执行 BeanPostProcessor: – 前置处理(如 @PostConstruct)和后置处理(如 AOP 代理)。 7. 初始化: – 调用 InitializingBean.afterPropertiesSet() 或 init-method。 8. 完成启动: – 发布 ContextRefreshedEvent 事件。 后端,Spring
50 Redis 集群的实现原理是 何? Redis Cluster 采用去中心化架构: 1. 分片:16384 个槽(Slot)分配给多个节点 2. 请求路由:客户端根据 key 的 CRC16 值计算 Slot,直连对应节点(或重定向) 3. 高可用:主从 + 故障转移(Gossip 协议通信,半数以上主节点同意则故障切换) 4. 数据迁移:支持槽在线迁移。 1. 数据分片:16384个slot(槽)分到多个节点(CRC16(key) mod 16384)。 2. 节点通信:Gossip协议交换 情形信息。 3. 故障转移:主节点宕机时,从节点通过Raft选举升主。 4. 客户端路由:MOVED/ASK重定向(或代理模式)。 1. 数据分片: – 16384 个 Slot 分配到多个节点(CRC16(key) mod 16384)。 2. 节点通信: – Gossip 协议交换节点 情形信息(PING/PONG)。 3. 请求路由: – 客户端直连节点:若 key 不在当前节点,返回 MOVED 重定向。 – 代理模式(如 Redis Proxy)。 4. 高可用: – 主从 :每个主节点有 N 个从节点。 – 故障转移:从节点选举新主(类似 Raft 算法)。 5. 扩容/缩容: – 重分片(Resharding):迁移 Slot 数据到新节点。 后端,Redis
51 怎样使用 MySQL 的 EXPLAIN 语句进行查询分析? 执行 EXPLAIN SELECT … 查看: 1. type:访问类型(const > ref > range > index > ALL) 2. key:实际使用索引 3. rows:预估扫描行数 4. Extra:额外信息(Using index 覆盖索引;Using temporary 临时表;Using filesort 文件排序) 优化目标:减少 rows 值,避免出现 ALL 和文件排序。 1. 执行:EXPLAIN SELECT … 2. 关键字段: – type:访问类型(const > ref > range > index > ALL) – key:实际使用索引 – rows:预估扫描行数 – Extra:额外信息(Using index/Using temporary/Using filesort) 3. 优化目标:避免ALL(全表扫描);减少rows;优化Extra异常项。 1. 执行命令:EXPLAIN SELECT ... 或 EXPLAIN FORMAT=JSON SELECT ...。 2. 关键字段解读: – type:访问类型(性能:const > ref > range > index > ALL)。 – key:实际使用的索引。 – rows:预估扫描行数。 – Extra:额外信息(如 Using index、Using temporary、Using filesort)。 3. 优化建议: – 若 type=ALL:检查是否缺索引。 – 若 Extra=Using filesort:优化排序字段索引。 – 若 rows 远大于实际:更新统计信息(ANALYZE TABLE)。 后端,MySQL,数据库
52 你了解的 Spring 都用到哪些设计模式? 1. 工厂模式:BeanFactory 2. 单例模式:默认 Bean 影响域 3. 代理模式:AOP 动态代理 4. 模板 技巧:JdbcTemplate、RestTemplate 5. 观察者:ApplicationEvent 事件机制 6. 适配器:HandlerAdapter 处理多种 Controller 7. 策略:Resource 接口(ClassPathResource/UrlResource)。 1. 工厂模式:BeanFactory 2. 单例模式:Bean默认单例 3. 代理模式:AOP(JDK/CGLib) 4. 模板 技巧:JdbcTemplate/RestTemplate 5. 观察者模式:ApplicationEvent事件机制 6. 适配器模式:HandlerAdapter(MVC) 1. 工厂模式:BeanFactory 创建 Bean 对象。 2. 单例模式:Bean 默认 影响域为单例。 3. 代理模式:AOP 动态代理(JDK Proxy/CGLIB)。 4. 模板 技巧:JdbcTemplate、RestTemplate 封装通用流程。 5. 观察者模式:ApplicationEvent 事件发布/监听。 6. 适配器模式:HandlerAdapter 适配不同 Controller。 7. 策略模式:ResourceLoader 根据前缀选择资源加载策略。 后端,Spring
53 Redis 集群会出现脑裂 难题吗? 可能发生。 缘故:网络分区导致主节点被孤立,原从节点选举新主,出现双主。解决方案: 1. 配置 min-replicas-to-write:主节点需至少 N 个从节点才可写 2. 设置 cluster-node-timeout:合理超时触发故障转移。 1. 可能发生:网络分区导致多个主节点同时写入。 2. 预防机制: – min-slaves-to-write:主节点需至少N个从节点连接 – min-slaves- x-lag:从节点最大延迟 时刻 3. 结局:当主节点无法满足条件时拒绝写入(避免数据不一致)。 1. 脑裂定义:集群分裂为多个独立子集群,各自选举主节点导致数据不一致。 2. Redis 集群防脑裂机制: – 节点投票:主节点需获得多数节点(N/2+1)认可才能当选。 – 最小主节点数:配置 min-replicas-to-write(写操作需至少 N 个从节点确认)。 3. 风险场景: – 网络分区时,少数派主节点拒绝写请求(需人工介入)。 4. 解决方案: – 合理设置超时 时刻(cluster-node-timeout)。 – 部署奇数节点保证投票多数决。 后端,Redis
54 请描述简单工厂模式的 职业原理。 定义一个工厂类,根据传入参数动态创建不同产品实例。例如: class ProductFactory { public Product createProduct(String type) { if (“A”.equals(type)) return new ProductA(); if (“B”.equals(type)) return new ProductB(); throw new IllegalArgumentException(); } } 1. 定义:通过工厂类创建对象(隐藏实例化逻辑)。 2. 核心:Factory类根据输入参数返回具体产品实例。 3. 示例: class CarFactory { public static Car createCar(String type) { if(“SUV”.equals(type)) return new Suv(); if(“Sedan”.equals(type)) return new Sedan(); } } 1. 定义:通过工厂类静态 技巧创建对象,隐藏实例化细节。 2. 核心组件: – Product:产品接口(定义通用 技巧)。 – ConcreteProduct:具体产品类(实现接口)。 – Factory:工厂类(根据参数创建对应产品)。 3. 职业流程: – 客户端调用 Factory.createProduct(type)。 – 工厂 技巧根据 type 返回 ConcreteProduct 实例。 4. 示例: – 数据库连接工厂(根据参数返回 MySQL/Oracle 连接)。 5. 缺点:新增产品需修改工厂类(违反开闭 制度)。 设计模式
55 Spring 有哪几种事务传播行为? 1. REQUIRED(默认):存在事务则加入,否则新建 2. SUPPORTS:存在则加入,否则非事务运行 3. MANDATORY:必须存在事务,否则抛异常 4. REQUIRES_NEW:新建事务,挂起当前事务 5. NOT_SUPPORTED:非事务运行,挂起当前事务 6. NEVER:必须非事务运行,否则抛异常 7. NESTED:嵌套事务(依赖 Savepoint)。 1. REQUIRED(默认):当前有事务则加入,无则新建。 2. REQUIRES_NEW:新建事务(挂起当前事务)。 3. SUPPORTS:有事务则加入,无则非事务运行。 4. NOT_SUPPORTED:非事务运行(挂起当前事务)。 5. MANDATORY:必须存在事务(否则抛异常)。 6. NEVER:必须无事务(否则抛异常)。 7. NESTED:嵌套事务(Savepoint机制)。 1. REQUIRED(默认): – 当前有事务则加入,无则新建。 2. REQUIRES_NEW: – 新建独立事务,挂起当前事务(完全独立)。 3. SUPPORTS: – 有事务则加入,无则以非事务执行。 4. NOT_SUPPORTED: – 非事务执行,挂起当前事务。 5. MANDATORY: – 必须有事务,否则抛异常。 6. NEVER: – 必须无事务,否则抛异常。 7. NESTED: – 嵌套事务(依赖 Savepoint),外层回滚内层必回滚,内层回滚不影响外层。 后端,Spring
56 Redis 中 怎样实现分布式锁? 使用 SET key value NX PX timeout: 1. NX:仅当 key 不存在时设置 2. PX:设置过期 时刻(防死锁) 3. value 为唯一标识(如 UUID),释放锁时先 GET 再 DEL(Lua 脚本保证原子性) 难题:需解决锁续期(看门狗)、主从切换安全 难题。 1. 核心命令:SET lock_key unique_value NX PX 30000(原子操作)。 2. 释放锁:Lua脚本验证值再删除(if redis.call(“get”,KEYS[1])==ARGV[1] then return redis.call(“del”,KEYS[1]))。 3. 难题:锁续期(Redisson看门狗机制);集群脑裂(RedLock算法)。 1. SETNX 命令: – SET lock_key unique_value NX PX 30000(原子操作)。 2. 释放锁: – Lua 脚本保证原子性:if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end 3. Redisson 实现: – 加锁:RLock lock = redisson.getLock("lock"); lock.lock(); – 看门狗:后台线程续期锁超时 时刻。 4. 注意事项: – 设置唯一值(防误删)。 – 锁超时 时刻需大于业务执行 时刻。 – 集群模式用 RedLock 算法(争议较大)。 后端,Redis
57 MySQL 中 怎样解决深度分页的 难题? 难题:OFFSET 过大导致扫描大量无效行。解决方案: 1. 子查询优化:SELECT * FROM t WHERE id > (SELECT id FROM t ORDER BY id LIMIT 1000000, 1) LIMIT 20 2. 记录上一页最大ID:WHERE id > last_ x_id ORDER BY id LIMIT 20 3. 覆盖索引 + 延迟关联:先查主键,再回表。 1. 难题:LIMIT 1000000,10 需扫描前1000010行。 2. 优化方案: – 子查询:SELECT * FROM table WHERE id > (SELECT id FROM table LIMIT 1000000,1) LIMIT 10 – 游标分页:WHERE id > last_id ORDER BY id LIMIT 10 – 覆盖索引:仅查索引列(无需回表) 1. 难题本质:LIMIT 1000000, 10 需先扫描前 1000000 行(性能差)。 2. 优化方案: – 子查询优化: SELECT * FROM table WHERE id > (SELECT id FROM table LIMIT 1000000, 1) LIMIT 10 – 游标分页(连续翻页): SELECT * FROM table WHERE id > last_id ORDER BY id LIMIT 10 – 延迟关联: SELECT * FROM table INNER JOIN (SELECT id FROM table LIMIT 1000000, 10) AS t USING(id) 3. 业务妥协: – 禁止跳页(仅允许上一页/下一页)。 – 滚动分页(如搜索引擎)。 后端,MySQL,数据库,场景题
58 说说 Springboot 的启动流程? 1. 创建 SpringApplication 对象:初始化 (ApplicationListeners) 2. 执行 run(): a. 加载 SpringApplicationRunListeners b. 准备环境(Environment) c. 创建 ApplicationContext(AnnotationConfigServletWebServerApplicationContext) d. 刷新上下文(调用 AbstractApplicationContext.refresh(),同 Spring) e. 执行 Runner 接口(ApplicationRunner/Com ndLineRunner)。 1. 初始化:SpringApplication.run()加载配置。 2. :发布ApplicationStartingEvent事件。 3. 环境准备:加载配置文件(application.yml)。 4. 上下文创建:创建AnnotationConfigApplicationContext。 5. Bean加载:执行refreshContext()(同Spring流程)。 6. 执行Runner:调用Com ndLineRunner/ApplicationRunner。 1. 创建 SpringApplication: – 初始化 ApplicationContext(根据 classpath 推断 Web 类型)。 2. 运行 run(): – 加载 SpringApplicationRunListener(发布启动事件)。 3. 准备环境: – 读取配置(application.properties、环境变量等)。 4. 创建 ApplicationContext: – 根据类型创建(如 AnnotationConfigServletWebServerApplicationContext)。 5. 刷新上下文: – 执行 AbstractApplicationContext.refresh()(同 Spring 流程)。 6. 执行 Runner: – 调用 ApplicationRunner/Com ndLineRunner。 7. 启动内嵌容器: – 如 Tomcat、Netty(通过 ServletWebServerFactory)。 Java,Spring Boot,后端
59 Redis 实现分布式锁时可能遇到的 难题有哪些? 1. 锁过期但业务未完成:需续期机制(Redisson 看门狗) 2. 主从切换导致锁失效:RedLock 算法(多实例投票) 3. 锁误删:value 存唯一标识,释放时校验 4. 锁不可重入:使用 ThreadLocal 记录重入次数 5. 锁竞争:自旋等待或阻塞队列。 1. 死锁:客户端崩溃未释放锁(设置过期 时刻解决)。 2. 误删锁:A超时释放后B加锁,A又删除B的锁(value存唯一标识)。 3. 锁续期:业务超时(Redisson看门狗自动续期)。 4. 集群脑裂:主从切换导致多客户端持锁(RedLock算法)。 1. 死锁: – 缘故:客户端崩溃未释放锁。 – 解决:设置锁超时 时刻(EX 参数)。 2. 锁误删: – 缘故:A 超时释放锁,B 获取锁后 A 误删 B 的锁。 – 解决:锁值设唯一标识(UUID),删除时校验。 3. 锁续期 难题: – 缘故:业务执行超时,锁提前释放。 – 解决:Redisson 看门狗机制自动续期。 4. 集群脑裂: – 缘故:主从切换导致多客户端持锁。 – 解决:RedLock 算法(需多数节点加锁成功)。 后端,Redis
60 工厂模式和抽象工厂模式有 何区别? 工厂模式: – 一个工厂类创建一种产品 – 通过参数选择具体产品 抽象工厂模式: – 一个工厂接口创建一族相关产品 – 每个具体工厂实现该族所有产品的创建 – 更强调产品间的约束关系(如 GUI 工厂生产按钮+文本框)。 1. 工厂模式: – 创建单一产品 – 核心:一个工厂类 – 示例:CarFactory.createCar() 2. 抽象工厂模式: – 创建产品族(多个相关产品) – 核心:抽象工厂接口+多个工厂实现 – 示例:VehicleFactory.createCar() + createTruck() 1. 工厂模式(简单工厂/工厂 技巧): – 生产单一类型产品(如数据库连接)。 – 工厂 技巧:每个产品对应一个工厂类(开闭 制度)。 2. 抽象工厂模式: – 生产产品族(多个相关产品),如 GUI 库(按钮+文本框)。 – 抽象工厂定义接口(createButton() + createTextBox())。 – 具体工厂实现整套产品(如 MacFactory/WinFactory)。 3. 核心区别: – 工厂模式已关注产品实例化。 – 抽象工厂已关注产品族创建。 设计模式
61 SpringBoot 是 怎样实现自动配置的? 基于条件注解和 SPI 机制: 1. @EnableAutoConfiguration 导入 AutoConfigurationImportSelector 2. 扫描 META-INF/spring.factories 中的自动配置类 3. 配置类使用 @ConditionalOnClass/@ConditionalOnMissingBean 等条件注解动态装配 Bean 4. 通过 @ConfigurationProperties 绑定外部配置(application.properties)。 1. 条件注解:@ConditionalOnClass等(按需加载)。 2. 配置定位:META-INF/spring.factories定义AutoConfiguration类。 3. 流程: – @EnableAutoConfiguration导入AutoConfigurationImportSelector – 扫描所有spring.factories中的配置类 – 根据条件注解筛选生效的配置 4. 定制:application.properties覆盖默认配置。 1. @SpringBootApplication: – 组合注解:@EnableAutoConfiguration + @ComponentScan。 2. @EnableAutoConfiguration: – 加载 META-INF/spring.factories 中的 AutoConfiguration 类。 3. 条件注解: – @ConditionalOnClass:类路径存在才生效。 – @ConditionalOnMissingBean:容器无该 Bean 才生效。 4. 配置绑定: – @ConfigurationProperties 将配置文件映射到 Bean 属性。 5. 示例: – 当引入 spring-boot-starter-data-redis 时,自动配置 RedisTemplate。 Java,Spring Boot,后端
62 何是 MySQL 的主从同步机制?它是 怎样实现的? 主从 流程: 1. 主库写 Binlog 2. 从库 I/O 线程拉取 Binlog 到 Relay Log 3. 从库 SQL 线程重放 Relay Log 中的事件 模式: – 异步 (默认):主库不等待从库ACK – 半同步 :至少一个从库ACK才返回 – GTID :全局事务ID避免重复执行。 1. 主库写Binlog:事务提交前写二进制日志。 2. 从库IO线程:拉取Binlog到本地(relay log)。 3. 从库SQL线程:重放relay log中的事件。 4. 同步模式: – 异步(默认):主库不等待从库ACK – 半同步:至少一个从库ACK – 全同步:所有从库ACK 1. 主从架构: – 主库(Master)处理写,从库(Slave) 数据处理读。 2. 同步流程: – 主库写 binlog:事务提交前写二进制日志。 – 从库 I/O 线程:拉取主库 binlog 到本地 relay log。 – 从库 SQL 线程:重放 relay log 中的 SQL 事件。 3. 模式: – 异步 (默认):主库不等待从库 ACK。 – 半同步 :至少一个从库确认才提交(rpl_semi_sync_ ster_wait_for_slave_count)。 – 全同步 :所有从库确认(MySQL 不支持,需集群方案)。 4. 数据一致性: – 主从不一致常见 缘故:网络延迟、从库重放慢。 后端,MySQL,数据库
63 说说 Redisson 分布式锁的原理? 核心机制: 1. 加锁:Lua 脚本设置 key(hash 结构,field=客户端ID,value=重入次数),设置过期 时刻 2. 看门狗:后台线程定时续期(默认 30 秒过期,每 10 秒续期) 3. 释放锁:Lua 脚本校验客户端ID,减少重入计数,计数为0则删除key 4. 可重入:通过 hash 结构记录同一线程多次加锁。 1. 加锁:Lua脚本尝试设置hash结构(key:锁名, field:客户端ID, value:重入计数)。 2. 看门狗:后台线程定时续期(默认30秒续到30秒)。 3. 解锁:Lua脚本减少计数,计数=0则删除key。 4. 可重入:同一线程多次加锁计数+1。 5. 红锁(RedLock):多主节点多数加锁成功。 1. 加锁机制: – 执行 Lua 脚本原子性操作:if redis.call('exists', KEYS[1]) == 0 then redis.call('hset', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return 1; end; – 通过 Hash 结构存储锁(Key:锁名,Field:客户端ID,Value:重入次数)。 2. 看门狗续期: – 后台线程每隔 10 秒检查锁是否存在,若存在则重置过期 时刻(默认 30 秒),防止业务未完成锁过期。 3. 解锁逻辑: – Lua 脚本验证客户端ID匹配后减少重入次数,若重入次数为0则删除Key。 4. 锁竞争处理: – 获取锁失败时订阅锁释放事件,通过信号量唤醒等待线程尝试重新加锁。 后端,Redis,Redisson
怎样 领会 Spring Boot 中的 starter? Starter 是预置依赖 + 自动配置的模块化组件。 影响: 1. 简化依赖管理:引入一个 starter 即整合相关库(如 spring-boot-starter-web 包含 Tomcat+SpringMVC) 2. 自动配置:根据类路径条件装配 Bean 自定义 starter:创建 Maven 项目,添加 spring.factories 定义配置类。 1. 定义:预置依赖+自动配置的JAR包(约定优于配置)。 2. 影响:简化依赖管理(如spring-boot-starter-web包含Tomcat/SpringMVC)。 3. 原理: – META-INF/spring.factories声明配置类 – @Configuration + @Conditional按需装配Bean 4. 自定义:实现自己的XXX-spring-boot-starter。 1. 核心 影响: – 简化依赖配置:通过一个依赖引入某功能所需的所有库(如 spring-boot-starter-web 包含 Tomcat、Spring MVC)。 2. 自动配置机制: – spring.factories 文件声明配置类(如 EnableAutoConfiguration 下的 WebMvcAutoConfiguration)。 – 条件注解(如 @ConditionalOnClass)控制配置生效条件。 3. 依赖管理: – 父项目 spring-boot-dependencies 统一管理版本号。 4. 自定义 Starter: – 创建 xxx-spring-boot-starter 模块,包含自动配置类及依赖。 Spring Boot,后端
65 怎样使用 Redis 快速实现排行榜? 使用 ZSET(有序 ): 1. ZADD key score member:添加成员(如用户ID)和分数(如积分) 2. ZREVRANGE key 0 9 WITHSCORES:取 Top10 3. ZRANK key member:查排名 4. ZINCRBY key increment member:增加分数。 时刻复杂度 O(log N)。 1. 数据结构:有序 (ZSET)。 2. 核心命令: – ZADD key score member(添加成员) – ZREVRANGE key 0 9 WITHSCORES(取TOP10) – ZRANK key member(查排名) 3. 优势:O(log N)复杂度;天然有序。 1. 数据结构:使用 ZSET(有序 ),成员为玩家ID,分数为排序依据(如积分)。 2. 核心命令: – ZADD rank 1000 user1:添加/更新分数。 – ZREVRANGE rank 0 9 WITHSCORES:获取前十名。 – ZRANK rank user1:查询排名(升序)。 3. 扩展功能: – 分段查询:ZRANGEBYSCORE 按分数范围筛选。 – 实时刷新:结合 INCRBY 原子更新分数。 4. 性能优势: – 操作 时刻复杂度 O(log N),百万数据毫秒级响应。 后端,Redis
66 Redis 中 怎样保证缓存与数据库的数据一致性? 常用策略: 1. 旁路缓存(Cache Aside): – 读:先查缓存,未命中则读DB并回填 – 写:先更新DB,再删除缓存(非更新) 2. 设置缓存过期 时刻:兜底方案 3. 延迟双删:写DB后删缓存,延迟再删一次 4. 消息队列:异步重试删除。注意无法强一致,需业务 忍让短暂不一致。 1. 更新策略: – 旁路缓存(Cache Aside):先更DB再删缓存(延迟双删防脏读) – 写穿透(Write Through):Cache代理写(需Cache支持) 2. 异步补偿: – 订阅Binlog同步(如C ) – 消息队列重试 3. 最终一致:接受短时不一致(设置缓存过期 时刻)。 1. 缓存更新策略: – Cache Aside:读时先读缓存,未命中读DB并回填;写时更新DB后删除缓存。 – Write Through:写请求同时更新缓存和DB(需事务保证)。 2. 删除而非更新:避免并发写导致缓存脏数据。 3. 延迟双删:写DB后删除缓存 → 延迟毫秒 → 再次删除(防旧数据回填)。 4. 订阅Binlog:通过 C 监听数据库变更,异步更新/删除缓存。 5. 设置缓存过期:最终一致性兜底(如设置 TTL 30 分钟)。 后端,Redis
67 说说 TCP 的三次握手? 目的:建立可靠连接。 经过: 1. 客户端 → 服务端:SYN=1, seq=x 2. 服务端 → 客户端:SYN=1, ACK=1, seq=y, ack=x+1 3. 客户端 → 服务端:ACK=1, seq=x+1, ack=y+1 握手后双方分配资源(缓冲区、连接变量)。SYN 洪泛攻击:伪造 IP 发送大量 SYN 不回复 ACK。 1. 经过: – 客户端→服务端:SYN=1, seq=x – 服务端→客户端:SYN=1, ACK=1, seq=y, ack=x+1 – 客户端→服务端:ACK=1, seq=x+1, ack=y+1 2. 目的:双方确认收发能力正常;协商初始序列号(防历史连接)。 1. 流程: – SYN:客户端发送 SYN=1 和初始序列号 seq=x。 – SYN+ACK:服务端回复 SYN=1、ACK=1,ack=x+1,并发送自身序列号 seq=y。 – ACK:客户端发送 ACK=1,ack=y+1,seq=x+1。 2. 目的: – 确认双方收发能力正常。 – 协商初始序列号(防历史连接混淆)。 3. 未握手风险: – 二次握手时服务端直接建立连接,可能因客户端超时重发导致资源浪费。 网络
68 简单说说 Netty 的零拷贝机制? 减少数据 ,提升性能: 1. 文件传输:FileRegion 调用 transferTo() 利用操作 体系 sendfile 机制 2. 复合缓冲区:CompositeByteBuf 逻辑组合多个 Buffer,避免合并 3. 堆外内存:DirectByteBuffer 直接操作内核空间 4. 内存池:重用 ByteBuf 减少分配开销。 1. 目的:减少数据拷贝次数(提升I/O效率)。 2. 实现方式: – 文件传输:FileRegion调用transferTo(利用DMA) – 缓冲区:CompositeByteBuf合并多个Buffer(逻辑组合) – 内存池:重用DirectByteBuffer(避免堆外内存分配开销) 1. 堆外内存 DirectBuffer: – 避免 JVM 堆与 Native 堆间数据拷贝(通过 ByteBuf.allocDirect() 创建)。 2. CompositeByteBuf: – 合并多个 Buffer 逻辑视图,避免物理合并拷贝(如协议拆包)。 3. 文件传输优化: – FileRegion 调用 sendfile 体系调用,文件数据直接由内核缓冲区到 Socket 缓冲区。 4. 内存池复用: – 重用已分配的 ByteBuf 对象,减少内存分配开销。 Netty,后端
69 何是配置中心?有哪些常见的配置中心? 集中管理应用配置的服务。功能: 1. 动态更新配置(无需重启) 2. 环境隔离(dev/test/prod) 3. 版本管理、权限控制 常见实现: – Spring Cloud Config:配合 Git/SVN – Nacos:配置管理 + 服务发现 – Apollo:携程开源的配置中心 – Consul:服务网格的配置方案。 1. 定义:集中管理应用配置的 体系(支持动态更新)。 2. 核心功能: – 配置存储/版本管理 – 动态推送(长轮询/WebSocket) – 权限控制 3. 常见实现: – Spring Cloud Config(Git存储) – Nacos(配置+服务发现) – Apollo(携程开源) – Consul 1. 定义:统一管理应用配置的服务,支持动态更新、版本控制、环境隔离。 2. 核心功能: – 配置集中存储与分发。 – 变更实时推送(如长轮询、WebSocket)。 – 权限审计与历史版本回滚。 3. 常见 体系: – Spring Cloud Config:Git 存储,配合 Bus 实现批量刷新。 – Nacos:支持配置与服务发现,AP/CP 模式切换。 – Apollo(携程):灰度发布、权限管控完善。 – Consul:基于 Raft 协议,强一致性保证。 后端,Spring Cloud
70 说说 TCP 的四次挥手? 目的:安全关闭连接。 经过: 1. 主动方 → 被动方:FIN=1, seq=u 2. 被动方 → 主动方:ACK=1, seq=v, ack=u+1(被动方进入 CLOSE_WAIT) 3. 被动方 → 主动方:FIN=1, ACK=1, seq=w, ack=u+1(被动方发完数据后) 4. 主动方 → 被动方:ACK=1, seq=u+1, ack=w+1(主动方进入 TIME_WAIT 等待 2MSL)。 1. 经过: – 主动方→被动方:FIN=1, seq=u – 被动方→主动方:ACK=1, seq=v, ack=u+1 – 被动方→主动方:FIN=1, ACK=1, seq=w, ack=u+1 – 主动方→被动方:ACK=1, seq=u+1, ack=w+1 2. 目的:双方确认关闭连接;处理残留数据。 3. TIME_WAIT 影响:确保 最后一个ACK送达(防旧连接数据干扰)。 1. 流程: – FIN:主动方发送 FIN=1 和序列号 seq=u。 – ACK:被动方回复 ACK=1,ack=u+1,seq=v(此时进入 CLOSE_WAIT)。 – FIN:被动方处理完数据后发送 FIN=1,ACK=1,seq=w,ack=u+1。 – ACK:主动方回复 ACK=1,ack=w+1,seq=u+1(进入 TIME_WAIT)。 2. 挥手次数 缘故: – TCP 全双工特性需双方独立关闭通道。 3. TIME_WAIT 影响: – 确保被动方收到 最后一个 ACK(防重发 FIN 导致新连接混乱)。 – 默认等待 2MSL(报文最大生存 时刻)。 网络
71 Netty 是 怎样解决粘包和拆包 难题的? 通过解码器(Decoder)处理: 1. 固定长度:FixedLengthFrameDecoder 2. 分隔符:DelimiterBasedFrameDecoder 3. 长度字段:LengthFieldBasedFrameDecoder(最常用) 4. 自定义协议:实现 ByteToMessageDecoder 解析消息头(含长度)。 1. 难题:TCP是字节流协议,消息边界不固定。 2. 解决策略: – 固定长度:FixedLengthFrameDecoder – 分隔符:DelimiterBasedFrameDecoder – 消息头定长:LengthFieldBasedFrameDecoder(头含长度字段) – 自定义协议:设计Header(length字段) 1. 粘包/拆包 缘故: – TCP 是字节流协议,消息边界不固定。 2. 解决方案: – 固定长度解码器:FixedLengthFrameDecoder(如每条消息 100 字节)。 – 分隔符解码器:DelimiterBasedFrameDecoder(如 分割)。 – 长度字段解码器:LengthFieldBasedFrameDecoder(消息头含长度字段)。 – 自定义协议:定义 Header(长度+类型)+ Body 结构。 3. 编码配合: – 发送方按相同 制度封包(如先写 4 字节长度再写内容)。 Netty,后端
72 Spring Boot 是 怎样通过 in 技巧启动 web 项目的? 1. 执行 SpringApplication.run() 2. 创建 AnnotationConfigServletWebServerApplicationContext 3. 在 onRefresh() 阶段调用 createWebServer() 4. 自动装配 ServletWebServerFactory(如 Tomcat) 5. 工厂创建 WebServer(内嵌 Tomcat)并启动 6. 注册 DispatcherServlet。 1. 入口:SpringApplication.run(Application.class, args) 2. 内嵌容器:根据依赖自动选择(Tomcat/Jetty/Undertow)。 3. 启动流程: – 创建Spring容器(AnnotationConfigApplicationContext) – 刷新容器(加载Bean) – 调用ServletWebServerFactory创建WebServer – 启动容器监听端口 1. 入口 技巧:SpringApplication.run(Application.class, args)。 2. 流程: – 创建 SpringApplication 实例,加载 META-INF/spring.factories 中的 ApplicationContextInitializer 和 ApplicationListener。 – 执行 run():初始化环境 → 打印 Banner → 创建应用上下文(如 AnnotationConfigServletWebServerApplicationContext)。 – 刷新上下文:AbstractApplicationContext.refresh()(加载 Bean 定义、初始化单例)。 – 内嵌 Web 服务器启动:通过 ServletWebServerFactory 创建 Tomcat/Jetty 实例并启动。 3. 自动装配:@EnableAutoConfiguration 触发 Web 相关配置(如 DispatcherServlet 注册)。 Spring Boot,后端
73 何情况下需要使用分布式事务,有哪些方案? 场景:跨数据库、跨服务的原子操作(如订单扣库存)。方案: 1. 2PC(两阶段提交):协调者主导,性能低 2. TCC(Try-Confirm-Cancel):业务补偿,开发复杂 3. 本地消息表:最终一致,配合消息队列 4. Saga:长事务拆分,每个步骤有补偿 5. Seata:AT 模式(自动生成回滚 SQL)。 1. 场景:跨服务/数据库的数据一致性(如:订单+库存服务)。 2. 方案: – 2PC(两阶段提交):强一致,性能低(如Seata AT模式) – TCC(Try-Confirm-Cancel):补偿型,需业务改造 – 消息事务:最终一致(如RocketMQ事务消息) – Saga:长事务拆分+逆向补偿 1. 使用场景: – 跨服务调用(如订单创建 + 库存扣减)。 – 跨数据库更新(分库分表环境)。 2. 解决方案: – 2PC(两阶段提交):协调者管理参与者提交/回滚,强一致但阻塞(如 XA 协议)。 – TCC(Try-Confirm-Cancel):业务层实现补偿(Try 预留资源 → Confirm/Cancel 确认或回滚)。 – 本地消息表:事务提交时插入消息表,异步任务推送消息(最终一致)。 – Seata:AT 模式自动回滚(通过代理 SQL 生成 undo log)。 3. 选型: – 强一致选 2PC,高可用选 TCC/消息队列。 Spring Cloud,微服务,分布式事务,后端
74 Redis 何故这么快? 1. 内存操作:数据存内存,读写无磁盘IO 2. 单线程:避免上下文切换和锁竞争(6.0 后多线程处理网络IO) 3. 高效数据结构:SDS、跳表、哈希表 4. I/O 多路复用:epoll/kqueue 处理高并发连接 5. 优化编码:不同长度数据使用不同存储方式。 1. 内存存储:数据操作在内存完成。 2. 单线程:避免上下文切换/锁竞争(6.0后多线程仅限网络I/O)。 3. I/O多路复用:epoll/kqueue高效处理连接。 4. 高效数据结构:跳表/哈希表/压缩列表。 5. 优化编码:不同场景用不同存储结构(如ziplist节省内存)。 1. 内存存储:数据操作在内存完成(纳秒级响应)。 2. 单线程模型: – 避免多线程竞争和上下文切换(6.0 后多线程仅用于网络 I/O)。 – 原子操作无锁冲突。 3. 高效数据结构: – 动态字符串(SDS)、跳表(ZSET)、压缩列表(Hash/List)。 4. I/O 多路复用: – epoll/kqueue 非阻塞处理高并发连接。 5. 优化编码: – 小数据时使用 ziplist 减少内存占用。 后端,Redis
75 怎样使用 Redis 快速实现布隆过滤器? 方案: 1. 原生支持(4.0+):BF.ADD key item, BF.EXISTS key item 2. 自实现: – 使用位图(BITFIELD)和多个哈希函数 – 添加:计算多个位偏移并置为1 – 查询:检查所有位是否为1(可能误判) 场景:防止缓存穿透(判断 key 是否存在)。 1. 原理:位数组+多个哈希函数(判断元素可能存在或一定不存在)。 2. 命令: – BF.RESERVE:创建过滤器 – BF.ADD:添加元素 – BF.EXISTS:检查存在性 3. 注意:需Redis 4.0+并加载RedisBloom模块。 1. 原理: – 多个哈希函数映射元素到位数组,检查所有位为1则可能存在(有误判率)。 2. Redis 实现: – 使用 BITMAP 存储位数组,通过 SETBIT 和 GETBIT 操作。 – 执行步骤: 1. 初始化:SETBIT bloom 1000000 0(设置100万位)。 2. 添加元素:对元素做 k 次哈希,置位对应下标(SETBIT bloom [hash] 1)。 3. 检查存在:若所有 GETBIT bloom [hash] 返回1则可能存在。 3. Redisson 封装: – RBloomFilter<String> bloomFilter = redisson.getBloomFilter("bloom"); – bloomFilter.tryInit(1000000, 0.01); // 100万元素,1%误判率 后端,Redis
76 何故 Java 中 HashMap 的默认负载因子是 0.75? 权衡 时刻与空间: – 负载因子高:空间利用率高,但哈希冲突增加(查询变慢) – 负载因子低:冲突减少,但空间浪费 0.75 是数学推导(泊松分布)和实验的平衡点,此时平均链表长度约 0.5,扩容阈值合理。 1. 空间与 时刻权衡: – 负载因子高:空间利用率高,但冲突概率增加(查询慢) – 负载因子低:冲突少,但空间浪费 2. 泊松分布:当负载因子=0.75时,链表长度≥8的概率极低(约0.000006%)。 3. 工程经验:0.75是空间成本与 时刻成本的折衷值。 1. 空间与 时刻权衡: – 负载因子 = 元素数 / 容量,决定扩容阈值。 2. 数学依据: – Poisson 分布计算:当负载因子 0.75 时,链表长度 ≥8 的概率极小(约 0.00000006):cite[4]。 3. 实际效果: – 小于 0.5:扩容频繁,空间浪费。 – 大于 0.75:哈希冲突增加,链表/红黑树退化风险升高。 4. 工程折中: – 0.75 时平均查找复杂度接近 O(1),空间利用率较合理。 Java ,Java
77 怎样处理 MySQL 的主从同步延迟? 方案: 1. 优化主库写速度:批量插入、减少锁范围 2. 优化从库:硬件升级、关闭 binlog(若仅备份)、并行 (5.6+) 3. 业务层: – 读操作分流到主库(牺牲读写分离) – 关键业务强制读主 – 延迟敏感操作走主库 4. 监控:SHOW SL E STATUS 查看 Seconds_Behind_Master。 1. 架构优化: – 分库分表(减少单库压力) – 读从写主(业务 忍让延迟) 2. 参数调整: – 增大从库innodb_buffer_pool_size – 开启并行 (slave_parallel_workers) 3. 强制读主:对实时性要求高的查询走主库。 1. 定位延迟: – SHOW SL E STATUS 查看 Seconds_Behind_Master。 2. 优化方案: – 写后读主库:关键业务读主库(如支付 结局)。 – 半同步 :至少一个从库确认才返回成功(牺牲部分写入性能)。 – 并行 :启用 slave_parallel_workers 多线程应用 binlog。 – 缓存抵消:写操作后读数据走缓存(Redis)。 3. 硬件/配置优化: – 从库 SSD 磁盘提升 I/O。 – 增大 innodb_buffer_pool_size 减少磁盘访问。 后端,MySQL,数据库
78 Netty 怎样解决 JDK NIO 中的空轮询 Bug? Bug:epoll 空轮询导致 CPU 100%。Netty 方案: 1. 记录 select 空转次数 2. 超过阈值(默认 512)时重建 Selector 3. 将原 Channel 重新注册到新 Selector 源码见 NioEventLoop 的 rebuildSelector()。 1. 难题:Selector.select()在无就绪事件时立即返回(导致CPU 100%)。 2. Netty方案: – 记录空轮询次数 – 超过阈值(默认512)重建Selector – 将原Channel重新注册到新Selector 1. Bug 现象: – JDK NIO 的 Selector.select() 在无事件时立即返回,导致 CPU 100%。 2. Netty 解决方案: – 计数检测:记录空轮询次数,超过阈值(默认 512)时重建 Selector。 – 替换流程: 1. 创建新 Selector。 2. 将旧 Channel 注册到新 Selector。 3. 关闭旧 Selector。 3. 源码关键: – io.netty.channel.nio.NioEventLoop 中的 select() 技巧实现空轮询保护逻辑。 Netty,后端
79 Java 中 HashMap 的扩容机制是 如何的? 触发条件:元素数 > 容量 × 负载因子(默认0.75) 经过: 1. 创建新数组(原容量×2) 2. 遍历旧数组每个桶: – 链表节点:拆分为低位链表(原位置)和高位链表(原位置+旧容量) – 红黑树节点:split() 拆树(若长度≤6 则退化为链表) 3. 迁移后旧数组丢弃。 1. 触发条件:元素数量 > 容量 × 负载因子(默认0.75)。 2. 扩容 经过: – 创建新数组(2倍原 大致) – 遍历旧数组,重新计算桶位置(高位运算优化) – 链表拆分为低位链(原索引)和高位链(原索引+旧容量) 3. 并发 难题:多线程扩容可能导致链表成环(头插法 难题,1.8尾插法解决)。 1. 触发条件: – 元素数量 > 容量 × 负载因子(默认 0.75)。 2. 扩容流程: – 新容量 = 旧容量 × 2(保持 2 的 n 次方)。 – 创建新数组,重新计算索引:(e.hash & oldCap) == 0 则位置不变,否则新位置 = 原位置 + 旧容量。 3. 元素迁移: – JDK 1.8 优化:链表拆分为高低位两组,避免重新计算哈希。 4. 线程安全 难题: – 多线程扩容可能导致循环链表(JDK 1.7)或数据丢失。 5. 性能影响: – 扩容时读写阻塞,初始化时预分配足够容量避免频繁扩容。 Java ,Java
80 何故 Redis 设计为单线程?6.0 版本为何引入多线程? 单线程 缘故: 1. 避免锁开销和上下文切换 2. 内存操作本身快速 3. I/O 多路复用处理并发连接 6.0 引入多线程 缘故: 1. 网络 I/O 成为瓶颈(特别是大键操作) 2. 后台线程处理过期键删除、AOF 刷盘等阻塞任务。注意:命令执行仍是单线程。 1. 单线程 缘故: – 避免锁竞争/上下文切换 – 内存操作本身快速 – I/O多路复用处理并发连接 2. 6.0引入多线程: – 仅限网络I/O解析(read/write) – 核心命令执行仍单线程(保持原子性) – 提升高并发场景吞吐量 1. 单线程优势: – 避免锁竞争和上下文切换,内存操作原子性天然保障。 – 足够应对10万级QPS(内存操作+epoll)。 2. 6.0 多线程 缘故: – 网络 I/O 成为瓶颈:单线程处理大流量时网络包解析延迟升高。 – 实现方式: – I/O 线程池(默认不启用)处理读写事件(io-threads 4)。 – 命令执行仍由主线程串行处理(保证原子性)。 3. 适用场景: – 网络延迟高或大包场景(如批量操作)性能提升显著。 后端,Redis
81 何故 TCP 挥手需要有 TIME_WAIT 情形? 影响: 1. 确保被动方收到 最后一个 ACK(若丢失可重传) 2. 让旧连接的重复报文在网络中消失(等待 2MSL,MSL 通常 30s/1min) 难题:高并发短连接时端口耗尽。解决方案:SO_REUSEADDR 选项复用 TIME_WAIT 端口。 1. 确保 最后一个ACK送达:若ACK丢失,被动方能重发FIN。 2. 防止旧连接数据混淆:等待2MSL(报文最大生存 时刻)使本连接数据包失效。 3. 时刻:通常2分钟(Linux可调tcp_fin_timeout)。 1. 确保被动关闭方收到 ACK: – 若 最后一次 ACK 丢失,被动方重发 FIN,TIME_WAIT 可重传 ACK。 2. 防止旧连接数据混淆: – 等待 2MSL(报文最大生存 时刻 × 2)使本次连接所有报文在网络中消失。 3. 默认时长: – Linux 默认 60 秒(可通过 net.ipv4.tcp_fin_timeout 调整)。 4. 副 影响: – 高并发短连接时端口耗尽(可通过 SO_REUSEADDR 复用端口)。 网络
82 Spring Boot 的核心特性有哪些? 1. 自动配置:基于条件装配 Bean 2. 起步依赖:预置库版本管理 3. Actuator:应用监控端点 4. 嵌入式容器:内嵌 Tomcat/Jetty 5. 外部化配置:支持 properties/yaml/环境变量 6. 生产就绪: 健壮检查、指标收集 7. 无代码生成:纯注解配置。 1. 自动配置:基于条件注解智能装配Bean。 2. 起步依赖:预置依赖组合(如spring-boot-starter-web)。 3. 内嵌容器:无需部署WAR(Tomcat/Jetty)。 4. Actuator:生产级监控端点( 健壮/指标)。 5. 外部化配置:支持多环境配置(application-{profile}.yml)。 1. 自动配置(Auto-configuration):根据项目依赖自动配置Spring应用,减少手动配置。 2. 起步依赖(Starter Dependencies):提供预定义的依赖组合,简化Maven/Gradle配置。 3. 嵌入式服务器(Embedded Servers):内置Tomcat、Jetty等服务器,无需单独部署WAR文件。 4. Actuator:提供生产级监控端点(如 健壮检查、指标收集)。 5. 外部化配置:支持通过属性文件、环境变量等灵活管理配置。 6. 无代码生成与XML配置:基于约定优于配置 制度,减少样板代码。 Spring Boot,后端
83 何故 HashMap 在 Java 中扩容时采用 2 的 n 次方倍? 优化哈希计算与索引定位: 1. 计算桶下标:index = (n – 1) & hash,n 为 2^k 时等价于 hash % n,且位运算更快 2. 扩容迁移时:节点新位置 = 原位置 或 原位置 + 旧容量(利用高位 bit 判断,无需重新哈希)。 1. 哈希优化:计算桶下标时用 (n-1) & hash 代替取模(位运算高效)。 2. 均匀分布:当n为2^k时,(n-1)的二进制全1,减少哈希冲突。 3. 扩容迁移:旧桶数据仅可能分到新表两个位置(高位链/低位链),迁移效率高。 1. 索引计算优化: – index = (n - 1) & hash 等价于取模运算(hash % n),位运算效率更高。 2. 哈希分布均匀: – 当 n 为 2^k 时,(n-1) 的二进制全为 1(如 15=0b1111),减少哈希冲突。 3. 扩容迁移优化: – JDK 1.8 中通过 e.hash & oldCap 判断高位,只需移动一半元素(0 或 oldCap 位置)。 4. 非 2^k 的缺点: – 取模需复杂运算(除法),性能下降。 Java ,Java
84 你在项目中使用的 Redis 客户端是 何? 常见客户端: – Jedis:同步阻塞,线程不安全需连接池 – Lettuce:基于 Netty 异步非阻塞,支持响应式 – Redisson:分布式功能 丰盛(锁、队列等) 根据场景选择:高并发选 Lettuce,需分布式对象选 Redisson。 1. 常见客户端: – Jedis:同步阻塞(连接池管理) – Lettuce:基于Netty异步(支持响应式) – Redisson:分布式对象/锁 高 质量功能 2. Spring整合:Spring Data Redis封装Jedis/Lettuce。 在Java项目中,常用的Redis客户端包括: 1. Jedis:轻量级、同步阻塞式客户端,适用于简单场景。 2. Lettuce:基于Netty的异步非阻塞客户端,支持响应式编程,性能更高。 3. Redisson:提供分布式对象和服务(如锁、队列),适合复杂分布式 体系。 实际项目中,根据需求选择——高并发场景优先Lettuce,需分布式功能则用Redisson。 后端,Redis
85 TCP 超时重传机制是为了解决 何 难题? 解决数据包丢失 难题。原理: 1. 发送数据后启动定时器 2. 超时未收到 ACK 则重传 3. 动态计算 RTO(重传超时 时刻):基于 RTT(往返 时刻)自适应 4. 快速重传:收到 3 个重复 ACK 立即重传(无需等待超时)。 1. 难题:网络丢包导致数据未送达。 2. 机制:发送数据后启动定时器,超时未收到ACK则重发。 3. 动态计算:RTO(重传超时 时刻)基于RTT(往返 时刻)动态调整。 4. 快速重传:收到3个重复ACK立即重传(无需等超时)。 TCP超时重传机制主要解决网络传输中的丢包 难题: 1. 数据包丢失:当发送方未收到接收方的ACK确认时,重传数据包以确保可靠性。 2. 延迟或乱序:通过动态计算RTT(往返 时刻)设置重传超时阈值,避免因网络延迟误判丢包。 核心目标是实现可靠传输,确保数据完整到达。 网络
86 简述 MyBatis 的插件运行原理,以及 怎样编写一个插件? 原理:基于 职责链模式拦截四大对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)。步骤: 1. 实现 Interceptor 接口,重写 intercept() 2. 用 @Intercepts 注解指定拦截 技巧 3. 配置 注册插件 4. 通过 Plugin.wrap() 生成代理对象。 1. 原理:基于 职责链模式(Interceptor Chain)拦截四大组件: – Executor(执行器) – StatementHandler(SQL处理) – ParameterHandler(参数处理) – ResultSetHandler( 结局集处理) 2. 编写步骤: – 实现Interceptor接口,注解指定拦截目标 – 重写intercept 技巧(调用Invocation.proceed()继续链) – 在SqlSessionFactory中注册插件 运行原理: 1. 基于 (Interceptor):通过动态代理拦截MyBatis的核心 技巧(如Executor的query/update)。 2. 职责链模式:多个插件按配置顺序形成拦截链,每个插件可修改或增强原逻辑。 编写步骤: 1. 实现Interceptor接口:重写intercept() 技巧定义拦截逻辑,plugin() 技巧生成代理对象。 2. 使用@Intercepts注解:指定拦截的目标 技巧(如@Signature(type=Executor.class, method=“update”, args={MappedStatement.class, Object.class}))。 3. 注册插件:在MyBatis配置文件中通过标签添加。 后端,MyBatis
87 数组和链表在 Java 中的区别是 何? 数组: – 连续内存,支持随机访问(O(1)) – 大致固定,插入删除需移动元素 – 内存紧凑(无额外指针) 链表: – 非连续内存,通过指针连接 – 插入删除高效(O(1)) – 访问需遍历(O(n)) – 内存开销大(存储指针)。 1. 内存结构: – 数组:连续内存,预分配 大致 – 链表:节点分散(指针链接),动态扩展 2. 访问效率: – 数组:O(1)随机访问 – 链表:O(n)顺序访问 3. 插入删除: – 数组:O(n)(需移动元素) – 链表:O(1)(修改指针) 4. 线程安全:Vector(数组)线程安全;LinkedList非线程安全。 1. 数据结构:数组是连续内存空间,链表(如LinkedList)通过节点指针非连续存储。 2. 访问效率:数组支持O(1)随机访问;链表需遍历,O(n)访问。 3. 插入删除:链表在头尾插入/删除为O(1),数组需移动元素,平均O(n)。 4. 内存占用:数组预分配固定 大致,链表每个元素额外存储指针(空间开销更大)。 5. 扩容:数组需手动 扩容,链表动态增删节点更灵活。 Java ,Java
88 Redis 中常见的数据类型有哪些? 1. String:字符串、整数 2. List:双向链表(支持阻塞操作) 3. Hash:键值对 4. Set:无序唯一 5. ZSet:有序 (score 排序) 6. Bit p:位操作 7. HyperLogLog:基数统计 8. Stream:消息流(5.0+)。 1. String:字符串/整数/浮点数(最大512MB)。 2. Hash:键值对 (适合存储对象)。 3. List:双向链表(支持阻塞操作)。 4. Set:无序唯一 (交并差运算)。 5. Sorted Set:有序 (分值排序)。 6. Bit p:位图操作。 7. HyperLogLog:基数统计。 8. Stream:消息流(5.0+)。 1. String:最基本类型,存储文本或二进制数据(如缓存键值)。 2. List:有序字符串列表,支持双向操作(如消息队列)。 3. Set:无序唯一 ,适用于去重(如标签 体系)。 4. Hash:键值对 ,适合存储对象(如用户信息)。 5. ZSet(Sorted Set):带分数的有序 ,用于排行榜。 6. 其他:Bit ps(位图)、HyperLogLog(基数统计)、Streams(流处理)。 后端,Redis
89 TCP 滑动窗口的 影响是 何? 1. 流量控制:接收方通过窗口 大致(rwnd)限制发送速率,防止淹没缓冲区 2. 进步吞吐:允许发送方连续发送多个报文(无需逐个确认),利用带宽 3. 拥塞控制:结合拥塞窗口(cwnd)动态调整发送量。 1. 流量控制:接收方通过窗口 大致(win)限制发送速率(防淹没)。 2. 可靠传输:窗口内数据可批量发送/确认(减少等待)。 3. 拥塞控制:动态调整窗口 大致(慢启动/拥塞避免)。 滑动窗口主要解决两个核心 难题: 1. 流量控制:接收方通过窗口 大致(Window Size)通知发送方可发送的数据量,防止接收方缓冲区溢出。 2. 进步传输效率:发送方无需每包等待ACK,可连续发送窗口内数据,减少网络空闲 时刻(如窗口 大致动态调整以适应网络状况)。 网络
90 介绍一下 Reactor 线程模型? 处理高并发的 I/O 事件驱动模型: 1. 单 Reactor 单线程:所有操作(accept/read/send)在同一线程 2. 单 Reactor 多线程:I/O 操作在主线程,业务处理在线程池 3. 主从 Reactor:主 Reactor 处理连接,子 Reactor 处理 I/O(Netty 默认)。 1. 核心:事件驱动+多路复用(I/O事件分发)。 2. 单Reactor单线程:所有操作(accept/read/send)在同一线程。 3. 单Reactor多线程:I/O操作在主线程,业务处理用线程池。 4. 主从Reactor:主线程处理accept,子线程处理I/O(如Netty/Nginx)。 Reactor模型是一种事件驱动的IO处理模式,核心组件: 1. Reactor:监听事件(如连接请求),分发给对应Handler。 2. Handlers:处理具体IO事件(如读写操作)。 常见变体: – 单Reactor单线程:所有操作在单线程完成,简单但性能低。 – 单Reactor多线程:Reactor线程处理IO事件,耗时操作交给线程池。 – 主从Reactor多线程:主Receptor处理连接,子Reactor处理读写,高效(如Netty实现)。 Netty,后端
91 Java 线程池核心线程数在运行 经过中能修改吗? 怎样修改? 可以。通过 ThreadPoolExecutor 的 setCorePoolSize() 技巧: 1. 若新值 > 原值:立即创建新线程 2. 若新值 < 原值:空闲线程在下次保活 时刻超时后被回收 注意:需根据业务负载动态调整(如配置中心监听变化)。 1. 可以修改:通过ThreadPoolExecutor.setCorePoolSize()。 2. 内部逻辑: – 若新值 > 旧值:立即创建新线程 – 若新值 < 旧值:空闲线程超时会被回收(需设置allowCoreThreadTimeOut=true) 3. 注意:修改后需调用prestartCoreThread()预热核心线程。 能修改。 技巧如下: 1. ThreadPoolExecutor提供setCorePoolSize(int) 技巧动态调整核心线程数。 2. 原理:新值大于旧值时,立即创建新线程;小于旧值时,空闲线程将在下次空闲时终止。 注意:需结合业务场景谨慎调整,避免频繁变更引发性能波动。 后端,Java,Java并发
92 TCP/IP 四层模型是 何? 1. 应用层:HTTP、FTP、DNS(用户交互) 2. 传输层:TCP、UDP(端到端通信) 3. 网络层:IP、ICMP(路由寻址) 4. 网络接口层:以太网、WiFi(物理传输)。对比 OSI:合并上三层为应用层,合并下两层为网络接口层。 1. 应用层:HTTP/FTP/DNS(用户数据)。 2. 传输层:TCP/UDP(端到端连接)。 3. 网络层:IP/ICMP(路由寻址)。 4. 网络接口层:Ethernet/ARP(物理传输)。 1. 应用层:提供应用服务(如HTTP、FTP)。 2. 传输层:端到端数据传输(TCP/UDP)。 3. 网络层:路由和寻址(IP协议)。 4. 网络接口层:物理网络访问(如以太网帧封装)。 网络
93 说说 MyBatis 的缓存机制? MyBatis 采用两级缓存: 1. 一级缓存(本地缓存):SqlSession 级别,默认开启。同一会话中相同查询直接从缓存返回 结局,执行更新操作(增删改)时自动清空 2. 二级缓存(全局缓存):Mapper 级别,需手动配置()。跨 SqlSession 共享,通过序列化机制存储,更新操作清空整个 Mapper 缓存 注意:分布式环境需集成 Redis 等中央缓存,避免脏读 1. 一级缓存: – 影响域:SqlSession级别(默认开启) – 失效:执行UPDATE/DELETE/INSERT或clearCache() 2. 二级缓存: – 影响域:Mapper级别(需手动开启) – 存储:序列化到磁盘/其他缓存服务(如Redis) – 注意:事务提交后才缓存 MyBatis提供两级缓存: 1. 一级缓存:默认开启, 影响域为SqlSession级别(同一会话内相同查询直接返回缓存)。 2. 二级缓存:需手动配置, 影响域为Mapper级别(跨SqlSession共享,通过序列化存储)。 注意:缓存可能导致脏读,可通过flushCache属性或@CacheNamespaceRef注解管理。 后端,MyBatis
94 何是 Spring Boot? Spring Boot 是 Spring 的脚手架框架,简化配置和部署。核心 价格: 1. 自动配置:减少 XML 配置 2. 内嵌容器:独立运行 Jar 包 3. 生产监控:Actuator 提供 健壮检查 4. 起步依赖:管理依赖版本 5. 约定优于配置:提供默认设置。 1. 定义:简化Spring应用初始搭建和开发的框架。 2. 核心 想法:约定优于配置(如默认依赖/目录结构)。 3. 核心功能: – 自动配置(Auto-Configuration) – 起步依赖(Starter Dependencies) – 内嵌容器(Embedded Container) – Actuator监控 4. 优势:快速构建独立、生产级应用。 Spring Boot是基于Spring框架的快速开发工具: 1. 目标:简化Spring应用初始搭建和配置,提供开箱即用功能。 2. 核心能力:自动配置、起步依赖、嵌入式服务器、生产监控(Actuator)。 3. 优点:减少XML配置,提升开发效率,适合微服务架构。 Spring Boot,后端
95 Java 中 怎样创建多线程? 1. 继承 Thread 类,重写 run() 2. 实现 Runnable 接口,传入 Thread 构造器 3. 实现 Callable 接口 + FutureTask(可获取返回值) 4. 线程池:ExecutorService.submit()/execute() 推荐方式:线程池管理资源,避免频繁创建销毁线程。 1. 继承Thread类:重写run() 技巧,调用start()启动。 2. 实现Runnable接口:实现run(),传入Thread构造器。 3. 实现Callable接口:结合FutureTask获取返回值。 4. 线程池:ExecutorService提交任务(推荐)。 5. 注意:直接调用run()是同步执行,非多线程。 1. 继承Thread类:重写run() 技巧,调用start()启动线程。 2. 实现Runnable接口:实现run() 技巧,将实例传入Thread对象。 3. 实现Callable接口:结合FutureTask获取异步 结局(支持返回值)。 4. 线程池:通过ExecutorService提交任务(如Executors.newFixedThreadPool())。 推荐使用Runnable或Callable,避免单继承限制。 后端,Java,Java并发
96 Redis 中跳表的实现原理是 何? ZSet 的底层结构 其中一个(元素多时使用)。原理: 1. 多层有序链表:底层全量数据,上层索引(概率性提升) 2. 查询:从顶层开始向右向下搜索(O(log n)) 3. 插入:随机生成层高(幂次定律),更新前后指针 4. 对比平衡树:实现简单,范围查询高效,内存稍多。 1. 结构:多层有序链表(底层全量数据,上层索引稀疏)。 2. 节点: – 分值(score)排序 – 后退指针(向后遍历) – 层数组(每层前进指针和跨度) 3. 查询:从最高层向下查找(类似二分)。 4. 插入:随机生成层高(概率1/4),更新前后指针。 跳表(SkipList)是ZSet的底层实现 其中一个: 1. 结构:多层有序链表,上层是下层的索引(加速查找)。 2. 查询:从顶层开始,向右或向下遍历(平均O(log n)复杂度)。 3. 插入:随机生成层数,更新前后节点指针(类似平衡树但更简单)。 4. 优势:比平衡树实现简单,支持范围查询。 后端,Redis
97 Redis 性能瓶颈时 怎样处理? 1. 内存: – 增加内存或集群分片 – 优化数据结构(如 hash 使用 ziplist) 2. CPU: – 慢查询优化(KEYS → SCAN) – 大 Key 拆分 3. 网络: – 管道/Pipeline 批量操作 – 客户端连接池 4. 持久化:AOF 重写调为后台执行。 1. 定位瓶颈: – 慢查询(slowlog get) – 监控命令耗时(redis-cli –latency) – 资源监控(CPU/内存/网络) 2. 优化手段: – 大Key拆分(hash分field) – 热Key分散(添加随机后缀) – Pipeline批量操作 – 集群分片(扩容) 1. 内存优化:使用压缩数据结构(如Hash的ziplist),设置过期 时刻。 2. 分片:通过集群或客户端分片分散数据(如Redis Cluster)。 3. 持久化调整:根据场景选择RDB(快照)或AOF(日志),避免频繁写盘。 4. 连接池:复用连接减少开销。 5. 监控:用Redis INFO命令分析慢查询、内存占用。 后端,Redis
98 OSI 七层模型是 何? 1. 物理层:比特流传输(网线、光纤) 2. 数据链路层:帧传输(MAC 地址、交换机) 3. 网络层:路由寻址(IP 协议、路由器) 4. 传输层:端到端连接(TCP/UDP) 5. 会话层:建立/管理会话 6. 表示层:数据格式转换(加密/压缩) 7. 应用层:用户接口(HTTP/FTP)。 1. 物理层:比特流传输(网线/光纤)。 2. 数据链路层:帧传输(MAC地址,交换机)。 3. 网络层:IP寻址(路由器)。 4. 传输层:端到端连接(TCP/UDP)。 5. 会话层:建立/管理会话。 6. 表示层:数据格式转换(加密/压缩)。 7. 应用层:用户接口(HTTP/ TP)。 1. 物理层:传输原始比特流(如电缆、光纤)。 2. 数据链路层:帧传输(MAC地址、交换机)。 3. 网络层:路由和IP寻址。 4. 传输层:端到端连接(TCP/UDP)。 5. 会话层:建立/维护会话。 6. 表示层:数据格式转换(加密/压缩)。 7. 应用层:用户接口(如HTTP协议)。 网络
99 说说 AQS 吧? AbstractQueuedSynchronizer:并发包锁的基石。核心: 1. 情形变量 state(volatile int) 2. CLH 队列(双向链表存储等待线程) 3. 模板 技巧:tryAcquire/tryRelease(需子类实现) 4. 获取锁:CAS 修改 state,失败则入队阻塞 5. 释放锁:修改 state 并唤醒后继节点 应用:ReentrantLock、CountDownLatch、Se phore。 1. 定义:AbstractQueuedSynchronizer(抽象队列同步器)。 2. 核心: – state:同步 情形(如ReentrantLock中0未锁,>0重入次数) – CLH队列:双向链表存储等待线程 3. 模板 技巧: – tryAcquire():尝试获取锁 – tryRelease():尝试释放锁 4. 应用:ReentrantLock/CountDownLatch/Se phore的底层实现。 AQS(AbstractQueuedSynchronizer)是Java并发包的基础框架: 1. 影响:通过CLH队列实现阻塞锁(如ReentrantLock)和同步器(如CountDownLatch)。 2. 核心:一个volatile int state表示 情形,线程竞争失败时入队阻塞。 3. 模式:独占模式(单线程访问)和共享模式(多线程共享)。 4. 扩展:子类重写tryAcquire()/tryRelease()定义获取/释放逻辑。 Java并发,Java
100 Cookie、Session、Token 之间有 何区别? Cookie: – 客户端存储(键值对) – 自动携带在请求头 Session: – 服务端存储会话数据 – 依赖 Cookie 传递 SessionID Token(如 JWT): – 客户端存储(无 情形) – 自包含用户信息(签名防篡改) – 适合分布式 体系。 1. Cookie:客户端存储(键值对),自动携带(同域请求)。 2. Session:服务端存储会话 情形(依赖Cookie传SessionID)。 3. Token:无 情形凭证(如JWT),自包含用户信息(签名防篡改)。 4. 对比: – 扩展性:Token易跨域/分布式 – 安全性:Token无CSRF风险 – 存储压力:Session在服务端,Token在客户端 1. Cookie:客户端存储的小型数据(由服务端Set-Cookie设置),每次请求自动携带。 2. Session:服务端存储的用户 情形,Session ID通过Cookie传递,依赖服务器内存。 3. Token(如JWT):自包含令牌(存储用户信息),客户端存储(如localStorage),无 情形化。 核心区别:存储位置(客户端/服务端)与 情形管理(有 情形/无 情形)。 网络
101 何是分库分表?分库分表有哪些类型(或策略)? 目的:解决单库性能瓶颈。类型: 1. 垂直分库:按业务拆分(如订单库、用户库) 2. 垂直分表:大表拆小表(如商品表拆基本信息+详情) 3. 水平分库:同一表数据分到不同库(按用户ID哈希) 4. 水平分表:同一库内分多表(如 order_001, order_002) 策略:哈希取模、范围分片、一致性哈希。 1. 定义:水平拆分数据(缓解单库性能瓶颈)。 2. 分库:按业务模块拆分(如订单库/用户库)。 3. 分表:单表数据拆分到多表(如按用户ID取模)。 4. 策略: – 哈希取模:均匀分布 – 范围分片:按 时刻/ID区间 – 一致性哈希:减少扩容迁移量 5. 工具:ShardingSphere/MyCat。 分库分表是数据库水平拆分策略: 1. 目的:解决单库性能瓶颈(高并发、大数据量)。 2. 类型: – 垂直拆分:按业务模块分库(如订单库、用户库)。 – 水平拆分:按 制度分表(如订单表按ID哈希分到多个库)。 3. 策略: – 哈希分片:均匀分布数据。 – 范围分片:按ID区间划分(如1-1000万表1)。 – 地理位置分片:就近存储。 后端,MySQL,数据库
102 MyBatis 中 #{} 和 ${} 的区别是 何? #{}: – 预编译处理(占位符 ?) – 防 SQL 注入 – 自动类型转换(如字符串加引号) ${}: – 字符串替换(直接拼 SQL) – 有注入风险 – 需手动处理类型 使用场景:动态表名/列名用 ${}(需过滤),参数值用 #{}。 1. #{}:预编译处理(生成?),防SQL注入(自动加单引号)。 2. ${}:字符串替换(原样拼入SQL),有注入风险。 3. 使用场景: – #{}:参数值(如WHERE user_id = #{id}) – ${}:动态表名/列名(如ORDER BY ${columnName}) 1. #{}:预编译处理(参数占位符?),防止SQL注入(如where name = #{value})。 2. ${}:字符串替换(直接拼接到SQL),有注入风险(如动态表名order by KaTeX parse error: Expected 'EOF', got '#' at position 20: …umn})。 优先使用#̲{},{}仅用于动态SQL片段(如非用户输入字段)。 后端,MyBatis
103 Java 中的 final 关键字是否能保证变量的可见性? 不能。final 仅保证: 1. 引用不可变(但对象内部 情形可变) 2. 构造函数初始化安全(其他线程可见 final 字段初始值) 可见性需通过 volatile 或 synchronized 保证。 1. 能:final修饰的字段在构造完成后对其他线程可见(禁止重排序)。 2. 原理:JMM(Java内存模型)保证final字段初始化前不会引用对象。 3. 注意:仅限构造 技巧内赋值( 技巧外赋值不保证)。 是的,但需满足条件: 1. 可见性保障:final字段在对象构造函数中初始化完成后,对其他线程立即可见(JVM插入内存屏障)。 2. 前提:对象必须正确发布(如避免this引用逸出),否则可能看到未初始化 情形。 3. 限制:仅适用于对象构造后的初始值,不保证引用对象内部 情形的可见性(如final数组的元素可变)。 Java并发,Java
104 Redis 的 hash 是 何? Redis Hash 是 field-value 映射表(类似 Java HashMap)。常用命令: – HSET key field value – HGET key field – HGETALL key 适用场景:存储对象(如用户信息),对比 String 存储可部分更新。底层:ziplist(小哈希)或 hashtable。 1. 数据结构:键值对 (field-value),类似Java的HashMap。 2. 底层:ziplist(元素少时)或hashtable。 3. 命令: – HSET key field value – HGET key field – HGETALL key 4. 适用场景:存储对象(如用户信息)。 1. 数据结构:键值对 (类似 Java HashMap),适合存储对象。 2. 底层实现: – 小数据时:ziplist(连续内存,节省空间)。 – 大数据时:hashtable(哈希表,O(1) 复杂度)。 3. 核心命令: – HSET user:1 name "Alice":设置字段值。 – HGET user:1 name:获取字段值。 – HGETALL user:1:获取所有字段和值。 4. 应用场景: – 存储用户信息(避免序列化整个对象)。 – 聚合统计(如 HINCRBY 累加商品点击量)。 后端,Redis
105 从网络角度来看,用户从输入网址到网页显示,期间发生了 何? 1. DNS 解析:域名 → IP 2. TCP 连接:与服务器三次握手 3. HTTPS:TLS 握手(如需) 4. HTTP 请求:发送请求报文 5. 服务器处理:后端服务 + 数据库 6. HTTP 响应:返回 HTML/CSS/JS 7. 浏览器渲染:解析资源并显示 8. TCP 断开:四次挥手。 1. DNS解析:域名→IP地址。 2. TCP连接:与服务器三次握手。 3. HTTP请求:发送请求报文(GET/POST)。 4. 服务器处理:返回响应(HTML/CSS/JS)。 5. 浏览器渲染:解析HTML构建DOM树,加载资源,渲染页面。 6. TCP断开:四次挥手。 1. DNS 解析:浏览器查询域名对应 IP(递归查询 → 根域名 → 顶级域名 → 权威 DNS)。 2. TCP 连接:与目标 IP 的 80/443 端口三次握手。 3. TLS 握手(HTTPS):协商加密算法,交换密钥(非对称加密)。 4. HTTP 请求:发送 GET/POST 请求(含 Cookie、User-Agent)。 5. 服务器处理: – Web 服务器(Nginx)转发请求到应用服务器(Tomcat)。 – 应用服务器执行业务逻辑(查询数据库)。 6. HTTP 响应:返回 情形码 + HTML/CSS/JS 资源。 7. 浏览器渲染: – 解析 HTML 构建 DOM 树,CSS 构建 CSSOM 树。 – 合并为渲染树 → 布局 → 绘制。 8. 连接关闭:TCP 四次挥手。 网络
106 Dubbo 和 Spring Cloud Gateway 有 何区别? Dubbo: – RPC 框架(服务间调用) – 基于 TCP 传输,性能高 – 需配合 ZooKeeper/Nacos 注册中心 Spring Cloud Gateway: – API (边缘服务) – HTTP 请求路由、过滤、限流 – 整合 Spring Cloud 生态(如熔断、认证) 定位不同:Dubbo 专注微服务调用,Gateway 专注入口流量管理。 1. 定位: – Dubbo:RPC框架(服务间调用) – Spring Cloud Gateway:API (请求入口路由/过滤) 2. 功能: – Dubbo:服务注册发现/负载均衡/容错 – Gateway:路由转发/限流/熔断/鉴权 3. 协议:Dubbo支持Dubbo/HTTP;Gateway基于WebFlux(HTTP)。 1. 定位不同: – Dubbo:RPC 框架(服务间调用)。 – Spring Cloud Gateway:API (统一入口、路由、过滤)。 2. 功能对比: | 特性 | Dubbo | Spring Cloud Gateway | |————–|———————|———————-| | 核心功能 | 服务调用、负载均衡 | 路由、限流、鉴权 | | 协议 | Dubbo 协议/TCP | HTTP/WebSocket | | 生态 | 微服务治理(注册中心)| Spring Cloud 全家桶 | 3. 使用场景: – Dubbo 用于微服务内部通信。 – Gateway 作为外部请求入口(如聚合多个微服务 API)。 后端,分布式,Spring Cloud,Dubbo
107 何是 Java 中的原子性、可见性和有序性? 原子性:操作不可分割(如 synchronized) 可见性:一个线程修改共享变量,其他线程立即可见(volatile 强制主内存刷新) 有序性:程序执行顺序按代码顺序(volatile 禁止指令重排,happens-before 制度)。 1. 原子性:操作不可中断(如synchronized)。 2. 可见性:一个线程修改后,其他线程立即可见(如volatile)。 3. 有序性:程序执行顺序按代码顺序(volatile/synchronized禁止指令重排序)。 4. JMM保证:happens-before 制度(如锁 制度/volatile 制度)。 1. 原子性: – 操作不可分割(如 i++ 非原子,AtomicInteger 的 incrementAndGet() 是原子)。 2. 可见性: – 线程修改共享变量后,其他线程立即可见(通过 volatile 或 synchronized 保证)。 3. 有序性: – 程序执行顺序按代码顺序(禁止指令重排序,volatile 和 final 可保证)。 4. JMM 保证: – synchronized 同时满足三者。 – volatile 保证可见性和有序性(不保证原子性)。 Java并发,Java
108 线程和进程有 何区别? 进程: – 操作 体系资源分配单位 – 独立内存空间(进程间通信需 IPC) – 切换开销大 线程: – CPU 调度单位 – 共享进程内存 – 切换开销小 关系:一个进程包含多个线程。 1. 资源:进程是资源分配单位(独立内存);线程是CPU调度单位(共享进程内存)。 2. 开销:进程创建/切换开销大;线程轻量(共享资源)。 3. 通信:进程间通信需IPC(管道/消息队列);线程可直接读写共享变量。 4. 健壮性:进程崩溃不影响其他进程;线程崩溃可能导致整个进程退出。 1. 资源占用: – 进程:独立内存空间(堆、栈、数据段),切换开销大。 – 线程:共享进程内存,私有栈空间,切换开销小。 2. 通信方式: – 进程:管道、消息队列、共享内存、Socket。 – 线程:直接读写共享变量(需同步)。 3. 健壮性: – 进程崩溃不影响其他进程。 – 线程崩溃可能导致整个进程退出。 4. 并发性: – 多线程更易实现高并发(如 Web 服务器处理请求)。 操作 体系,计算机基础
109 说说你知道的几种 I/O 模型 1. 阻塞 I/O:调用后线程阻塞等待数据 2. 非阻塞 I/O:立即返回,轮询检查就绪 3. I/O 多路复用:select/poll/epoll 监听多个 fd,就绪时通知 4. 信号驱动 I/O:内核数据就绪时发送 SIGIO 信号 5. 异步 I/O:内核完成所有操作后回调(如 AIO)。 1. 阻塞I/O:调用recv()时线程阻塞(直到数据就绪)。 2. 非阻塞I/O:轮询检查数据就绪(CPU空转)。 3. I/O多路复用:select/poll/epoll单线程监听多个fd(事件驱动)。 4. 信号驱动I/O:内核数据就绪时发SIGIO信号(少用)。 5. 异步I/O:aio_read()发起后立即返回,内核完成所有操作后通知。 1. 阻塞 I/O: – 线程等待数据就绪(如 read() 阻塞)。 2. 非阻塞 I/O: – 轮询检查数据就绪(浪费 CPU)。 3. I/O 多路复用: – select/poll/epoll 监控多个 fd,就绪时通知(高并发基石)。 4. 信号驱动 I/O: – 内核数据就绪时发送 SIGIO 信号(少用)。 5. 异步 I/O: – 体系调用后立即返回,数据就绪后回调通知(如 AIO)。 对比: – 阻塞/非阻塞:调用者是否等待。 – 同步/异步:数据就绪由谁处理(同步需调用者读写,异步由内核完成)。 Netty,后端
110 MyBatis 与 Hibernate 有哪些不同? MyBatis: – SQL 映射(半自动 ORM) – 需手写 SQL,灵活优化 – 进修曲线低 Hibernate: – 全自动 ORM(对象-关系映射) – HQL 操作,少写 SQL – 缓存、延迟加载等 高 质量特性 适用:复杂 SQL/性能优化选 MyBatis;快速开发选 Hibernate。 1. SQL控制:MyBatis需手动写SQL(灵活优化);Hibernate自动生成(HQL)。 2. 对象映射:MyBatis是半ORM(ResultMap映射);Hibernate是全ORM(对象-表直接映射)。 3. 性能:MyBatis更易优化SQL;Hibernate缓存机制强大。 4. 进修曲线:MyBatis简单;Hibernate复杂(Session管理/缓存策略)。 1. SQL 控制: – MyBatis:手动编写 SQL(灵活优化)。 – Hibernate:HQL 自动生成 SQL(开发快)。 2. 对象关系映射: – Hibernate 全自动 ORM(对象与表严格映射)。 – MyBatis 半自动(SQL 与 结局集手动映射)。 3. 性能: – MyBatis 更易优化复杂 SQL(避免 N+1 难题)。 4. 进修曲线: – MyBatis 简单易上手。 – Hibernate 需掌握 Session、缓存等概念。 5. 适用场景: – MyBatis:需精细控制 SQL 的互联网应用。 – Hibernate:快速开发的企业应用(CRM、ERP)。 后端,MyBatis
111 何是 Java 内存模型(JMM)? JMM 定义线程与主内存的交互规范: 1. 所有变量存主内存 2. 线程私有 职业内存(缓存) 3. 线程操作变量需从主内存拷贝到 职业内存 4. volatile 保证可见性(写立即刷新,读重新加载) 5. happens-before 制度解决指令重排 难题。 1. 定义:规范多线程下共享变量的访问 制度(保证跨平台一致性)。 2. 核心: – 主内存:存储共享变量 – 职业内存:线程私有(存储共享变量副本) 3. 制度: – 线程操作变量需从主内存读取/写入 – volatile保证可见性/禁止重排序 – synchronized保证原子性/可见性 1. 定义:规范线程 怎样通过主内存交互共享变量(定义 职业内存与主内存交互 制度)。 2. 核心 制度: – 可见性:volatile 写先于读(写操作刷新主内存,读操作清空 职业内存)。 – 有序性:happens-before 制度(如锁解锁先于加锁,volatile 写先于读)。 3. 内存屏障: – volatile 和 synchronized 插入屏障禁止指令重排序。 4. 目标:解决多线程可见性、有序性 难题(不解决原子性)。 Java并发,Java
112 Redis 和 Memcached 有哪些区别? Redis: – 支持多种数据结构 – 持久化(RDB/AOF) – 主从 、集群 – 单线程模型(6.0 前) Memcached: – 仅 Key-Value – 纯内存,无持久化 – 多线程模型 – 内存管理更高效(Slab 分配) 适用:需 丰盛功能选 Redis;纯缓存场景 Memcached 性能更高。 1. 数据类型:Redis支持String/Hash/List等;Memcached仅String。 2. 持久化:Redis支持RDB/AOF;Memcached纯内存。 3. 线程模型:Redis单线程;Memcached多线程。 4. 集群:Redis原生集群;Memcached需客户端分片。 5. 适用场景:Redis功能更 丰盛;Memcached更简单(纯缓存)。 1. 数据类型: – Redis:String/Hash/List/Set/ZSet 等。 – Memcached:仅 String。 2. 持久化: – Redis:支持 RDB 快照和 AOF 日志。 – Memcached:纯内存,重启数据丢失。 3. 线程模型: – Redis:单线程(6.0 后多线程 I/O)。 – Memcached:多线程(利用多核)。 4. 功能扩展: – Redis:支持 Lua 脚本、事务、发布订阅。 5. 适用场景: – Redis:复杂数据结构、持久化需求(缓存、队列)。 – Memcached:简单键值缓存(高并发读)。 后端,Redis
113 何是物理地址, 何是逻辑地址? 逻辑地址:程序使用的地址(如段地址:偏移地址) 物理地址:内存单元的实际地址 转换:CPU 通过分段单元(逻辑→线性地址)和分页单元(线性→物理地址)转换。 1. 逻辑地址:程序使用的地址(如汇编指令中的地址)。 2. 物理地址:内存单元的实际地址。 3. 转换:MMU(内存管理单元)通过页表将逻辑地址→物理地址。 4. 目的:进程地址空间隔离;支持虚拟内存(换页机制)。 1. 逻辑地址: – 程序使用的地址(如汇编中的 mov eax, [0x8048000])。 – 分段机制:段选择符 + 偏移量。 2. 物理地址: – 内存芯片上的实际地址(通过总线访问)。 3. 转换 经过: – CPU 通过分段单元(段基址 + 偏移)→ 线性地址 → 分页单元(页表)→ 物理地址。 4. 虚拟内存 影响: – 每个进程有独立逻辑地址空间(隔离性)。 – 分页机制实现物理内存扩展(Swap 空间)。 操作 体系
114 说说 何是 API ?它有 何 影响? 影响: 1. 统一入口:路由请求到后端服务 2. 认证鉴权:校验身份与权限 3. 限流熔断:保护后端服务 4. 日志监控:收集请求指标 5. 请求转换:协议/格式转换 实现:Spring Cloud Gateway、Kong、Nginx。 1. 定义:所有客户端请求的统一入口(反向代理)。 2. 影响: – 路由转发:根据URL分发到后端服务 – 认证鉴权:统一身份验证 – 限流熔断:保护后端服务 – 日志监控:集中收集请求日志 – 负载均衡:分发流量到多个实例 1. 定义: 体 体系一入口,处理所有客户端请求(类似门面模式)。 2. 核心功能: – 路由转发:将请求分发到对应微服务。 – 认证鉴权:JWT 验证、OAuth2.0。 – 限流熔断:限制 QPS、防止雪崩。 – 日志监控:记录请求指标。 – 负载均衡:轮询、权重分发。 3. 常见实现: – Spring Cloud Gateway、Kong、Nginx。 4. 优势: – 解耦客户端与微服务。 – 统一安全管控。 后端,分布式,Spring Cloud
115 何是 Java 的 CAS(Compare-And-Swap)操作? 原子操作:比较并交换。 经过: 1. 读取内存值 V 2. 比较 V 与预期值 A 3. 相等则更新为 B,否则重试 底层:Unsafe 类调用 CPU 指令(如 x86 的 LOCK CMPXCHG) 难题:ABA 难题(通过版本号 AtomicStampedReference 解决)。 1. 定义:无锁原子操作(硬件CPU指令支持)。 2. 参数:内存位置V、预期值A、新值B。 3. 操作:当V的值等于A时,将V更新为B;否则放弃。 4. 应用:AtomicInteger/并发容器实现。 5. 难题:ABA 难题(版本号解决);自旋开销。 1. 定义:无锁并发原语,基于硬件指令(如 x86 的 CMPXCHG)保证原子性。 2. 操作语义: – CAS(V, E, N):若内存值 V 等于期望值 E,则更新为 N,否则放弃。 3. Java 实现: – Unsafe.compareAndSwapInt(),AtomicInteger 基于 CAS 实现自增。 4. 优点: – 避免锁开销(非阻塞算法)。 5. 缺点: – ABA 难题(通过 AtomicStampedReference 加版本号解决)。 – 自旋消耗 CPU(竞争激烈时)。 Java并发,Java
116 Select、Poll、Epoll 之间有 何区别? Select: – 线性扫描 fd_set(O(n)) – 最大 1024 fd Poll: – 链表存储 fd,无数量限制 – 仍需遍历所有 fd Epoll: – 事件驱动:epoll_wait 返回就绪 fd(O(1)) – 支持水平触发(LT)和边缘触发(ET) – 无 fd 数量限制。 1. Select: – 轮询所有fd(O(n)) – fd数量限制(通常1024) 2. Poll: – 无fd数量限制 – 仍O(n)轮询 3. Epoll: – 事件驱动(O(1)就绪fd检查) – 无fd限制 – 支持ET/LT模式 1. Select: – 线性扫描 fd (O(n)),fd 数量受限(1024)。 2. Poll: – 链表存储 fd,无数量限制(仍 O(n) 扫描)。 3. Epoll: – 事件驱动:注册 fd 回调(无需遍历)。 – 高效:O(1) 获取就绪 fd。 – 支持模式:LT(水平触发)和 ET(边缘触发)。 4. 对比: | 特性 | Select | Poll | Epoll | |————|————-|————-|—————| | 效率 | O(n) | O(n) | O(1) | | fd上限 | 1024 | 无 | 10万+ | | 内存开销| 固定 大致 | 动态 | 高效 | 操作 体系
117 “ 何是 MyBatis-Plus?它有 何 影响?它和 MyBatis 有哪些区别?” MyBatis-Plus(MP)是 MyBatis 的增强工具包: 影响: 1. 提供通用 CRUD 接口(BaseMapper),减少 80% 样板代码 2. 强大的条件构造器(QueryWrapper/LambdaQueryWrapper) 3. 分页插件自动分页 4. 代码生成器一键生成实体/Controller/Service 区别: – MyBatis 需手写 SQL 和基础 CRUD – MP 封装常用操作,开发效率更高但灵活性略低 – MP 在 MyBatis 基础上扩展,非替代关系 1. 定义:MyBatis增强工具(简化CRUD)。 2. 影响: – 内置通用Mapper/Service(减少SQL编写) – 分页插件 – 代码生成器 – 乐观锁支持 3. 区别: – MyBatis需手动写SQL – MyBatis-Plus提供开箱即用CRUD接口 MyBatis-Plus(MP)是MyBatis的增强工具: 1. 影响:简化CRUD操作(内置通用Mapper、Service),提供分页插件、代码生成器等。 2. 区别: – MyBatis:需手动编写SQL和基础CRUD。 – MP:自动生成SQL(如save()/updateById()),减少70%代码量。 – MP兼容MyBatis,扩展其功能但不改变核心。 后端,Mybatis-plus,编程导航
118 何故 Java 中的 ThreadLocal 对 key 的引用为弱引用? 防止内存泄漏: 1. 若 key 强引用:Thread 存活时即使外部无引用,Entry 也不会回收 2. 弱引用 key:当外部无强引用时,GC 可回收 key,下次访问 ThreadLocalMap 时清除无效 Entry 注意:需及时调用 remove() 彻底避免泄漏。 1. 防内存泄漏:当ThreadLocal对象无强引用时,Entry的key可被GC回收。 2. 残留 难题:Entry的value仍强引用(需调用remove()手动清除)。 3. 最佳 操作:线程池中必须remove()(否则复用线程导致旧value残留)。 1. 内存泄漏风险: – 若 key 强引用,ThreadLocal 对象即使不再使用,Entry 仍被 Thread 的 ThreadLocalMap 强引用,导致无法回收。 2. 弱引用 影响: – 当 ThreadLocal 对象无强引用时(如置为 null),GC 可回收 key,下次调用 set()/get() 时清除无效 Entry。 3. 残留 难题: – Value 仍被 Entry 强引用(需调用 remove() 显式清除)。 4. 最佳 操作: – 使用后务必 threadLocal.remove()(尤其线程池场景)。 Java并发,Java
119 编译执行与解释执行的区别是 何?JVM 使用哪种方式? 编译执行:源代码整体编译为机器码执行(如 C++) 解释执行:逐行解释源代码执行(如早期 Python) JVM:混合模式(-Xmixed,默认) – 热点代码编译执行(JIT) – 非热点代码解释执行 – AOT 编译(GraalVM):预先编译。 1. 编译执行:源码→机器码(执行快,启动慢)。 2. 解释执行:源码→逐行解释执行(启动快,执行慢)。 3. JVM:混合模式(-Xmixed): – 热点代码编译执行(JIT) – 非热点代码解释执行 1. 编译执行: – 源码 → 编译器 → 机器码 → 直接执行(如 C++)。 – 优点:运行快。 – 缺点:跨平台差。 2. 解释执行: – 源码 → 解释器逐行翻译执行(如 Python)。 – 优点:跨平台好。 – 缺点:运行慢。 3. JVM 混合模式: – 解释器:快速启动执行字节码。 – JIT 编译器:热点代码编译为机器码(如 C1/C2 编译器)。 – AOT:GraalVM 支持提前编译(减少运行时开销)。 JVM,Java
120 Redis 支持事务吗? 怎样实现? 支持,通过 MULTI/EXEC 命令: 1. MULTI:开启事务 2. 命令入队(不立即执行) 3. EXEC:执行所有命令(原子性) 4. DISCARD:取消事务 注意: – 无回滚(命令错误时继续执行) – 非隔离性(其他客户端命令可能插队)。 1. 支持:但不保证原子性(单条命令原子,事务整体非原子)。 2. 命令: – MULTI:开启事务 – EXEC:执行事务 – DISCARD:取消事务 – WATCH:监控键(CAS乐观锁) 3. 注意:事务中命令错误(如语法错误)会导致EXEC失败;运行时错误(如类型错误)仅当前命令失败。 1. 支持事务:MULTI + EXEC 命令组合。 2. 执行流程: – MULTI:开启事务。 – 入队命令:SET/GET 等(不立即执行)。 – EXEC:原子执行所有命令(按顺序)。 3. 错误处理: – 语法错误:EXEC 时拒绝执行(如 SET key 缺参数)。 – 运行时错误:继续执行后续命令(如 INCR 对字符串操作)。 4. 无回滚:Redis 事务不支持回滚(设计哲学:错误由编程导致,应在开发阶段解决)。 后端,Redis
121 到底 何是 TCP 连接? 本质:通信双方维护的一组 情形数据,包括: 1. 源/目的 IP 和端口 2. 序列号(seq)和确认号(ack) 3. 窗口 大致(rwnd) 4. 连接 情形(ESTABLISHED 等) 通过三次握手建立 情形,四次挥手释放 情形。 1. 逻辑概念:通信双方维护的 情形信息(非物理链路)。 2. 情形信息: – 源/目的IP+端口 – 序列号/确认号 – 窗口 大致 – 连接 情形(ESTABLISHED等) 3. 建立 经过:三次握手初始化 情形。 1. 本质:通信双发的 情形记录(非物理链路)。 2. 情形信息: – 四元组:源 IP、源端口、目标 IP、目标端口。 – 序列号与窗口:发送方序列号(seq)、接收方窗口 大致(win)。 – 连接 情形:ESTABLISHED(已连接)、TIME_WAIT(等待关闭)。 3. 建立 经过:三次握手初始化序列号(ISN)和窗口参数。 4. 资源占用:内核维护连接 情形表(如 Linux 的 /proc/net/tcp)。 网络
122 怎样处理重复消息? 方案: 1. 幂等设计:业务逻辑天然支持重复执行(如 SET key value) 2. 唯一标识:消息带唯一 ID,服务端记录处理 情形(Redis 或 DB) 3. 版本号:更新数据时校验版本(CAS) 4. 数据库约束:唯一索引防重。 1. 幂等设计:业务逻辑天然幂等(如UPDATE SET a=1)。 2. 唯一标识:消息带唯一ID,消费端去重(Redis Set/数据库唯一索引)。 3. 事务表:消费前插入业务流水表(主键/唯一索引防重)。 4. 情形机:业务 情形流转(如订单 情形只能从已支付→已完成)。 1. 幂等设计: – 业务逻辑支持多次执行 结局不变(如 UPDATE table SET status=2 WHERE id=1 AND status=1)。 2. 唯一标识: – 消息携带唯一 ID(如 UUID),消费前查 Redis 是否已处理。 3. 数据库去重: – 建消息表,主键为消息 ID(重复插入报错)。 4. 消息队列特性: – RocketMQ/Kafka:支持 Exactly-Once 语义(事务消息 + 幂等)。 5. 人工干预: – 记录日志,异常时人工补偿。 消息队列,后端
123 Java 中 何情况会导致死锁? 怎样避免? 条件(全部满足): 1. 互斥 2. 占有且等待 3. 不可抢占 4. 循环等待 避免: – 顺序获取锁(破坏循环等待) – 超时释放(Lock.tryLock()) – 避免嵌套锁 – 银行家算法。 1. 死锁条件: – 互斥 – 持有并等待 – 不可剥夺 – 循环等待 2. 避免 技巧: – 顺序加锁(固定锁获取顺序) – 超时释放(tryLock(timeout)) – 死锁检测(JStack/ThreadMXBean) 1. 死锁条件: – 互斥、请求与保持、不可剥夺、循环等待。 2. 示例: java // 线程A: lock1→lock2 // 线程B: lock2→lock1 3. 避免 技巧: – 顺序加锁:所有线程按固定顺序获取锁(如先 lock1 后 lock2)。 – 超时释放:tryLock(timeout) 获取失败后退回重试。 – 死锁检测:JConsole 或 jstack 分析线程栈。 4. 预防: – 减少锁粒度(如 ConcurrentHashMap 分段锁)。 Java并发,Java
124 怎样保证消息的有序性? 1. 全局有序:单分区(Topic 仅一个 Partition/Queue),性能低 2. 局部有序:同业务 ID 的消息发到同一分区(如订单ID哈希到固定分区) 3. 消费者单线程处理同分区消息 注意:Kafka 分区内有序,RabbitMQ 需单队列单消费者。 1. 全局有序:单分区(Topic+单队列,性能低)。 2. 局部有序: – 同一业务ID哈希到同一分区(如订单ID) – 生产者同步发送(等待上一条ACK) – 消费者单线程处理(或线程内按业务ID分组处理) 1. 全局有序: – 单分区队列(如 Kafka 单 Partition),性能受限。 2. 局部有序: – 相同 Key 的消息发到同一分区(如 Kafka 指定 Key)。 3. 消费者处理: – 单线程消费(性能差)。 – 内存队列按 Key 分组处理(如线程池按 Key 哈希分配线程)。 4. 消息队列支持: – RocketMQ:顺序消息(同一 Sharding Key 发到同一队列)。 – Kafka:同一 Partition 内有序。 消息队列,后端
125 说一下 Netty 的应用场景? 1. 高性能 RPC 框架(Dubbo、gRPC) 2. 即时通讯(IM 体系) 3. 游戏服务器 4. 物联网设备接入 5. HTTP 服务器(如 Spring WebFlux) 优势:异步非阻塞、零拷贝、高并发连接处理。 1. 高性能RPC框架:Dubbo/gRPC底层通信。 2. 即时通讯:WebSocket服务器。 3. 游戏服务器:长连接/高并发。 4. 物联网:MQTT协议设备接入。 5. HTTP服务器:如Spring WebFlux。 1. 高性能 RPC 框架: – Dubbo、gRPC 底层网络通信。 2. 实时通信 体系: – 即时聊天(WebSocket 支持)。 – 游戏服务器(长连接、低延迟)。 3. 物联网 : – 海量设备接入(MQTT 协议支持)。 4. API : – Spring Cloud Gateway 基于 Netty 实现异步转发。 5. 大数据传输: – 文件服务器(零拷贝优化)。 Netty,后端
126 何是 Spring IOC? 控制反转:将对象创建和依赖注入交给容器管理。实现方式: 1. 容器管理 Bean 的 生活周期 2. 依赖注入(DI):通过构造器/setter/字段注入依赖对象 3. 配置方式:XML、注解(@Autowired)、Java Config 目的:解耦组件, 进步可测试性和可维护性。 1. 定义:控制反转(Inversion of Control)。 2. 核心:将对象创建/依赖注入交给容器管理。 3. 实现: – 容器:BeanFactory/ApplicationContext – 配置:XML/注解/JavaConfig 4. 优势:解耦(对象间依赖由容器注入)。 1. 定义:控制反转(Inversion of Control),将对象创建和依赖注入交给容器管理。 2. 核心 想法: – 容器托管 Bean:ApplicationContext 管理对象 生活周期。 – 依赖注入(DI):通过构造函数、Setter 或注解自动装配依赖(如 @Autowired)。 3. 优势: – 解耦组件(面向接口编程)。 – 便于单元测试(Mock 依赖)。 4. 实现原理: – 反射创建对象,BeanFactory 存储 Bean 定义(BeanDefinition)。 后端,Spring
127 你了解 Java 线程池的原理吗? 核心组件: 1. 职业队列(BlockingQueue):存储待执行任务 2. 线程 (HashSet):执行任务 3. 拒绝策略(RejectedExecutionHandler):队列满时处理新任务 职业流程: – 提交任务 → 核心线程未满则创建线程执行 – 核心线程满 → 任务入队 – 队列满 → 创建非核心线程(未达最大线程数) – 线程数达最大值 → 触发拒绝策略。 1. 核心参数: – corePoolSize:核心线程数 – workQueue:任务队列 – xPoolSize:最大线程数 – RejectedExecutionHandler:拒绝策略 2. 职业流程: – 任务提交→核心线程未满则创建线程 – 核心线程满则入队 – 队列满则创建非核心线程 – 线程数达 x则执行拒绝策略 3. 线程复用:线程循环从队列取任务执行。 1. 核心参数: – corePoolSize:核心线程数(常驻)。 – ximumPoolSize:最大线程数(临时线程)。 – workQueue:任务队列(如 ArrayBlockingQueue)。 – RejectedExecutionHandler:拒绝策略(AbortPolicy/CallerRunsPolicy)。 2. 职业流程: – 提交任务 → 核心线程未满?创建线程执行:入队。 – 队列满?创建临时线程执行(≤ xPoolSize)。 – 线程数达 x 且队列满?触发拒绝策略。 3. 线程复用: – 线程通过 Worker 循环从队列取任务执行。 4. 资源回收: – 临时线程空闲超时(keepAliveTime)后销毁。 Java并发,Java
128 Redis 数据过期后的删除策略是 何? 1. 惰性删除:访问 key 时检查过期则删除 2. 定期删除:随机抽取部分 key 检查过期并删除(hz 参数控制频率) 3. 内存淘汰:当内存不足时触发主动删除(LRU/LFU/TTL 等策略)。 1. 惰性删除:访问key时检查过期则删除(节省CPU,内存释放不及时)。 2. 定期删除:随机抽取部分key检查并删除(平衡CPU与内存)。 3. 内存淘汰:当内存不足时触发(如LRU/LFU/随机淘汰)。 1. 惰性删除: – 访问 key 时检查是否过期,过期则删除(节省 CPU,可能内存泄漏)。 2. 定期删除: – 随机抽取 key 检查过期(每秒 10 次,每次 20 个 key)。 3. 内存淘汰策略(当内存不足时): – volatile-lru:从已设置过期 时刻的 key 中淘汰最近最少使用。 – allkeys-lru:所有 key 中淘汰 LRU。 – volatile-ttl:淘汰剩余生存 时刻最短的 key。 – noeviction:不淘汰,写操作返回错误(默认)。 后端,Redis
129 怎样处理消息堆积? 1. 增加消费者:水平扩展消费者数量 2. 提升消费能力:批量处理、异步消费、优化业务逻辑 3. 扩大分区数(Kafka):需提前规划 4. 降级:跳过非关键消息 5. 监控:设置堆积阈值告警 预防:合理评估生产消费速率,设置消息 TTL。 1. 紧急扩容:增加消费者实例(需提前规划分区数)。 2. 批量消费:消费者批量拉取消息处理。 3. 降级处理:跳过非核心消息。 4. 定位 缘故: – 生产者流量激增(限流) – 消费者处理慢(优化业务/线程池) 1. 定位 缘故: – 消费者宕机或处理慢(监控消费延迟)。 2. 紧急扩容: – 增加消费者实例(分区数需 ≥ 消费者数)。 3. 优化消费逻辑: – 批量处理(如 Kafka 的 x.poll.records)。 – 异步处理(非核心操作异步化)。 4. 降级处理: – 跳过非关键消息(如日志)。 – 写入死信队列人工处理。 5. 预防措施: – 设置消费超时 时刻(避免单条卡死)。 – 监控堆积告警。 消息队列,后端
130 何是服务熔断? 微服务容错机制:当依赖服务故障达到阈值(如错误率>50%),熔断器打开,后续请求直接拒绝(快速失败),避免级联故障。熔断后: – 经过休眠 时刻进入半开 情形 – 试探请求成功则关闭熔断,否则重新打开。实现:Hystrix、Sentinel。 1. 定义:当下游服务故障时,上游主动中断调用(防止级联故障)。 2. 情形机: – 关闭:正常调用 – 打开:错误率超阈值,拒绝所有请求 – 半开:尝试放行部分请求探测恢复 情形 3. 实现:Hystrix/Sentinel。 1. 定义:当下游服务故障时,上游服务中断调用(快速失败),防止雪崩。 2. 情形机: – Closed:正常调用。 – Open:熔断开启(直接返回错误)。 – Half-Open:试探性放行部分请求。 3. 熔断器实现: – Hystrix:基于滑动窗口统计错误率(如 50% 错误开启熔断)。 – Sentinel:基于 QPS/响应 时刻熔断。 4. 恢复机制: – 熔断后等待 时刻窗(如 5 秒)→ Half-Open → 成功则关闭熔断。 Spring Cloud,服务熔断,后端,微服务
131 Java 线程池有哪些拒绝策略? 1. AbortPolicy(默认):抛 RejectedExecutionException 2. CallerRunsPolicy:由提交任务的线程直接执行 3. DiscardPolicy:静默丢弃任务 4. DiscardOldestPolicy:丢弃队列中最旧任务,重试提交 自定义策略:实现 RejectedExecutionHandler 接口。 1. AbortPolicy:默认,抛RejectedExecutionException。 2. CallerRunsPolicy:由提交任务的线程直接执行。 3. DiscardPolicy:静默丢弃新任务。 4. DiscardOldestPolicy:丢弃队列中最旧任务,重试提交。 1. AbortPolicy(默认): – 抛 RejectedExecutionException。 2. CallerRunsPolicy: – 由提交任务的线程直接执行任务(同步阻塞)。 3. DiscardPolicy: – 静默丢弃任务(不通知)。 4. DiscardOldestPolicy: – 丢弃队列中最老的任务,重试提交。 5. 自定义策略: – 实现 RejectedExecutionHandler,如记录日志或持久化存储。 Java并发,Java
132 HTTP 1.0 和 2.0 有 何区别? HTTP/1.0: – 短连接(每请求新建 TCP 连接) – 无 情形 HTTP/2.0: – 多路复用:一个连接并行处理多个请求 – 二进制分帧(提升解析效率) – 头部压缩(HPACK) – 服务端推送(主动推送资源)。 1. 多路复用:2.0一个连接并行处理多个请求(解决1.x队头阻塞)。 2. 二进制分帧:2.0数据以二进制帧传输(1.x是文本)。 3. 头部压缩:2.0用HPACK压缩header(减少重复)。 4. 服务器推送:2.0主动推送资源(如CSS/JS)。 1. 多路复用: – 1.0:每个请求独立 TCP 连接(队头阻塞)。 – 2.0:一个 TCP 连接并行处理多个请求(二进制分帧)。 2. 头部压缩: – 2.0 使用 HPACK 压缩头部(减少冗余)。 3. 服务器推送: – 2.0 支持主动推送资源(如 CSS/JS)。 4. 优先级: – 2.0 可设置请求优先级(优先加载关键资源)。 5. 协议格式: – 1.0 文本协议(可读性好)。 – 2.0 二进制协议(解析高效)。 网络
133 怎样保证消息不丢失? 1. 生产者: – 事务消息(如 Kafka) – 确认机制(ACK,如 RabbitMQ 的 publisher confirm) 2. Broker: – 持久化(磁盘写入) – 副本机制(主从同步) 3. 消费者: – 手动提交 offset(处理完业务再 commit) – 幂等处理。 1. 生产者: – 同步发送+重试 – 事务消息(如RocketMQ) 2. Broker: – 同步刷盘(非异步) – 主从同步(多副本) 3. 消费者: – 手动提交offset(业务处理成功后ACK) 1. 生产者确认: – RabbitMQ:事务或 Confirm 模式(等待 Broker ACK)。 – Kafka:acks=all(所有副本确认)。 2. Broker 持久化: – Kafka:消息刷盘(flush.interval.ms)。 – RabbitMQ:队列声明为持久化(durable=true)。 3. 消费者手动提交: – Kafka:关闭 auto.commit,业务成功后 commitSync()。 – RabbitMQ:手动 Ack(basic.ack)。 4. 备份与高可用: – Kafka 多副本(ISR 机制)。 – RabbitMQ 镜像队列。 消息队列,后端
134 Spring AOP默认用的是 何动态代理,两者的区别? 默认策略: – 目标类实现接口 → JDK 动态代理(基于接口) – 未实现接口 → CGLIB 代理(基于子类) 区别: – JDK 代理:InvocationHandler 接口,依赖目标接口 – CGLIB:MethodInterceptor 接口,可代理普通类,通过字节码生成子类。 1. 默认策略: – 实现接口:JDK动态代理(基于接口) – 无接口:CGLIB代理(基于子类) 2. 区别: – JDK代理:InvocationHandler接口,反射调用 技巧 – CGLIB:MethodInterceptor接口,字节码生成子类 3. 强制CGLIB:@EnableAspectJAutoProxy(proxyTargetClass=true) 1. 默认策略: – 有接口:JDK 动态代理(基于 InvocationHandler)。 – 无接口:CGLIB 字节码增强(生成子类)。 2. 区别: 特性
135 怎样合理地设置 Java 线程池的线程数? 依据任务类型: 1. CPU 密集型:N_cpu + 1(避免过多线程上下文切换) 2. I/O 密集型:N_cpu * (1 + 平均等待 时刻/平均计算 时刻) 通用公式:线程数 = N_cpu * U_cpu * (1 + W/C) 其中 U_cpu 为 CPU 利用率(0.5~1),W/C 为等待 时刻与计算 时刻比。 1. CPU密集型:N_cpu + 1(避免上下文切换)。 2. I/O密集型:N_cpu * (1 + 等待 时刻/计算 时刻)。 3. 经验值: – 普通业务:核心线程数=2N_cpu – 高并发:压测调整 4. 监控:根据ThreadPoolExecutor的活跃线程数动态调整。 1. CPU 密集型: – 线程数 = CPU 核数 + 1(避免上下文切换)。 2. I/O 密集型: – 线程数 = CPU 核数 × (1 + 平均等待 时刻/平均计算 时刻)。 – 经验值:2 × CPU 核数 ~ 5 × CPU 核数。 3. 混合型: – 拆分为 CPU 密集和 I/O 密集任务,分别设置线程池。 4. 动态调整: – 监控线程池指标(队列堆积、活跃线程数),实时调整。 5. 公式参考: – N_threads = N_cpu * U_cpu * (1 + W/C) – N_cpu:CPU 核数 – U_cpu:目标 CPU 利用率(0.8) – W/C:等待 时刻与计算 时刻比 Java并发,Java
136 Redis 中有哪些内存淘汰策略? 配置 xmemory-policy: 1. noeviction:不淘汰,写操作报错 2. allkeys-lru:全体 key 中淘汰最近最少使用 3. allkeys-lfu:全体 key 中淘汰最不经常使用 4. volatile-lru:仅过期 key 中淘汰 LRU 5. volatile-lfu:仅过期 key 中淘汰 LFU 6. random:随机淘汰(allkeys 或 volatile)。 1. noeviction:默认,不淘汰(写操作返回错误)。 2. allkeys-lru:全体key中淘汰最近最少使用。 3. allkeys-random:全体key中随机淘汰。 4. volatile-lru:设过期 时刻的key中淘汰LRU。 5. volatile-ttl:设过期 时刻的key中淘汰剩余TTL小的。 6. volatile-random:设过期 时刻的key中随机淘汰。 1. 过期键淘汰: – volatile-lru:从已设置过期 时刻的 key 中淘汰 LRU。 – volatile-ttl:淘汰剩余生存 时刻最短的 key。 – volatile-random:随机淘汰有过期 时刻的 key。 2. 全局淘汰: – allkeys-lru:所有 key 中淘汰 LRU(推荐)。 – allkeys-random:随机淘汰任意 key。 3. 不淘汰: – noeviction:写操作返回错误(默认)。 4. LFU 策略(Redis 4.0+): – volatile-lfu/allkeys-lfu:淘汰访问频率最低的 key。 后端,Redis
137 MySQL 中 如果我 select * from 一个有 1000 万行的表,内存会飙升么? 不会。InnoDB 通过缓冲池(Buffer Pool)管理数据: 1. 查询按需加载数据页(Page),非全表加载 2. 结局集分批返回(游标机制) 3. 内存不足时淘汰旧页 但若客户端处理慢(如 ORM 全量映射对象),可能导致客户端 OOM。 1. 可能不会:MySQL采用流式传输(边读边发,不缓存全 结局)。 2. 风险点: – 客户端缓存(如JDBC默认全加载到内存) – 大字段(BLOB/TEXT)占用内存 3. 优化: – 分页查询 – 避免SELECT * – 使用游标(StreamingResultSet) 取决于查询方式和客户端: 1. 服务端内存:MySQL逐行返回 结局,但需维护临时 结局集(可能占用大量内存)。 2. 客户端内存:若一次性获取全部数据(如JDBC的fetchSize过大),JVM堆可能OOM。 优化: – 分页查询(LIMIT)。 – 流式读取(JDBC设置ResultSet.TYPE_FORWARD_ONLY)。 – 避免SELECT *,指定必要列。 后端,场景题
138 消息队列设计成推消息还是拉消息?推拉模式的优缺点? 推模式(如 RabbitMQ): – 优点:实时性高 – 缺点:可能压垮消费者 拉模式(如 Kafka): – 优点:消费者控制速率 – 缺点:增加延迟 折中:长轮询(如 RocketMQ PULL 模式)。 1. 推模式(如Kafka Consumer Pull): – 优点:低延迟(Broker主动推) – 缺点:消费者可能过载(需背压机制) 2. 拉模式(如RocketMQ): – 优点:消费者控制速率(避免堆积) – 缺点:轮询开销(空轮询) 3. 混合:长轮询(如Kafka Consumer阻塞拉取)。 1. 推模式(Push): – Broker 主动推送消息到 Consumer(如 RabbitMQ)。 – 优点:实时性好,延迟低。 – 缺点:Consumer 可能过载(需背压机制)。 2. 拉模式(Pull): – Consumer 主动从 Broker 拉取消息(如 Kafka)。 – 优点:Consumer 按能力消费(可控)。 – 缺点:实时性差(需轮询)。 3. 混合模式: – 长轮询(Long Polling):Consumer 拉取,Broker 无消息时等待(如 RocketMQ)。 消息队列,后端,场景题
139 你使用过哪些 Java 并发工具类? 1. CountDownLatch:等待多线程完成 2. CyclicBarrier:线程到达屏障点等待 3. Se phore:控制并发线程数 4. Exchanger:线程间交换数据 5. Phaser:分阶段协同 6. CompletableFuture:异步编程 7. ForkJoinPool:并行计算。 1. 锁:ReentrantLock/StampedLock 2. 同步器:CountDownLatch/CyclicBarrier/Se phore 3. :ConcurrentHashMap/CopyOnWriteArrayList 4. 线程池:ThreadPoolExecutor/ScheduledThreadPool 5. 原子类:AtomicInteger/LongAdder 6. Future:CompletableFuture 1. 同步工具: – CountDownLatch:等待多个任务完成(如启动初始化)。 – CyclicBarrier:多线程相互等待(如分布式计算)。 – Se phore:控制并发资源数(如数据库连接池)。 2. 线程安全容器: – ConcurrentHashMap:分段锁实现的并发 Map。 – CopyOnWriteArrayList:写时 List(读多写少)。 3. 原子类: – AtomicInteger:CAS 实现的原子操作。 – LongAdder:高并发下性能优于 AtomicLong。 4. 执行框架: – ThreadPoolExecutor:自定义线程池。 – CompletableFuture:异步编程(链式调用)。 Java并发,Java
140 何是设计模式?请简述其 影响。 设计模式是解决软件设计中常见 难题的可复用方案。 影响: 1. 代码复用:避免重复设计 2. 进步可维护性:规范代码结构 3. 团队协作:提供通用术语 4. 解耦:分离变化与不变部分 分类:创建型(如工厂)、结构型(如代理)、行为型(如观察者)。 1. 定义:解决软件设计中常见 难题的可复用方案。 2. 影响: – 代码复用(避免重复造轮子) – 提升可维护性(标准结构易 领会) – 保证代码可靠性(经过验证的方案) 3. 分类:创建型(对象创建)、结构型(对象组合)、行为型(对象交互)。 1. 定义: – 针对软件设计中反复出现 难题的通用可复用解决方案模板。 2. 核心 影响: – 代码复用:避免重复造轮子(如单例模式)。 – 解耦:降低模块依赖(如观察者模式)。 – 可维护性:统一设计规范(如 MVC 模式)。 – 可扩展性:方便功能扩展(如策略模式)。 3. 分类: – 创建型(5种):对象创建(工厂、单例)。 – 结构型(7种):类/对象组合(代理、适配器)。 – 行为型(11种):对象交互(观察者、 职责链)。 设计模式
141 何是 AOP? 面向切面编程:将横切已关注点(如日志、事务)从业务逻辑中分离。核心概念: 1. 切面(Aspect):模块化横切逻辑 2. 连接点(Joinpoint): 技巧执行/异常抛出点 3. 切点(Pointcut):匹配连接点的表达式 4. 通知(Advice):切面在连接点的动作(前置/后置/环绕) 5. 织入(Weaving):将切面应用到目标对象的 经过。 1. 定义:面向切面编程(Aspect-Oriented Programming)。 2. 核心:将横切已关注点(日志/事务/安全)与业务逻辑分离。 3. 关键概念: – Aspect:切面(模块化横切逻辑) – JoinPoint:连接点( 技巧执行/异常处理) – Advice:增强(切面在连接点的动作) – Pointcut:切入点(匹配连接点的表达式) 1. 定义:面向切面编程(Aspect-Oriented Programming),将横切已关注点(如日志、事务)与业务逻辑分离。 2. 核心概念: – 切面(Aspect):模块化横切已关注点(如 @Aspect 类)。 – 连接点(Join Point):程序执行点(如 技巧调用)。 – 通知(Advice):切面在连接点的动作(@Before/@After)。 – 切入点(Pointcut):匹配连接点的表达式(如 @Pointcut("execution(* com.service.*.*(..))"))。 3. 实现原理: – 动态代理(JDK/CGLIB)在运行时增强目标 技巧。 4. 应用场景:日志记录、事务管理、权限校验。 后端,Spring
142 何是服务降级? 在 体系高负载时,暂时关闭非核心服务,保障核心功能可用。方式: 1. 熔断降级:依赖服务不可用时返回兜底数据 2. 限流降级:拒绝部分请求 3. 功能降级:关闭次要功能(如推荐模块) 实现:Hystrix fallback 技巧、Sentinel 降级 制度。 1. 定义: 体系过载时,暂时关闭非核心服务(保障核心功能可用)。 2. 场景: – 熔断后返回降级 结局(如默认值) – 限流触发时拒绝部分请求 3. 实现: – Hystrix:@HystrixCom nd(fallbackMethod) – Sentinel:配置降级 制度 1. 定义: 体系过载时,暂时关闭非核心服务,保障核心功能可用。 2. 触发条件: – 高并发流量(如秒杀场景)。 – 依赖服务故障(如第三方接口超时)。 3. 降级策略: – 返回兜底数据:如缓存默认值(“服务繁忙”)。 – 功能屏蔽:关闭次要功能(如商品详情页隐藏评论)。 – 熔断降级联动:Hystrix 熔断后自动降级。 4. 实现方式: – 配置中心动态开关(如 Apollo)。 – 注解 @HystrixCom nd(fallbackMethod = "fallback")。 Spring Cloud,微服务,后端,服务降级
143 Synchronized 和 ReentrantLock 有 何区别? 1. 语法:synchronized 是关键字;ReentrantLock 是类 2. 公平锁:synchronized 非公平;ReentrantLock 可选公平/非公平 3. 条件变量:ReentrantLock 支持多个 Condition 4. 超时:ReentrantLock 支持 tryLock(timeout) 5. 中断:ReentrantLock 支持 lockInterruptibly() 6. 性能:JDK6 后 synchronized 优化后接近。 1. 本质:Synchronized是JVM关键字;ReentrantLock是API(java.util.concurrent)。 2. 功能: – ReentrantLock支持公平锁 – 可中断锁(lockInterruptibly) – 条件变量(newCondition) – 限时等待(tryLock) 3. 性能:JDK1.6后Synchronized优化(偏向锁/轻量级锁)后接近。 1. 底层实现: – synchronized:JVM 层面实现(monitorenter/monitorexit)。 – ReentrantLock:JDK 代码实现(基于 AQS)。 2. 功能对比: 特性
144 Redis 的 Lua 脚本功能是 何? 怎样使用? Lua 脚本功能: 1. 原子性执行多个 Redis 命令(脚本整体执行,不会被其他命令打断) 2. 减少网络开销(多个命令合并为一个脚本传输) 使用方式: 1. EVAL “脚本内容” key数量 key1 key2… arg1 arg2… 2. EVALSHA(执行缓存脚本) 示例(原子计数器): EVAL “return redis.call(‘INCRBY’, KEYS[1], ARGV[1])” 1 counter 5 注意:脚本不宜过长(阻塞 Redis),且需处理脚本哈希缓存 1. 影响:原子执行多个Redis命令。 2. 命令:EVAL script numkeys key [key …] arg [arg …] 3. 示例: EVAL “return redis.call(‘set’, KEYS[1], ARGV[1])” 1 name Tom 4. 优势: – 减少网络开销(批量操作) – 原子性(单线程执行) 1. 影响: – 原子执行多个命令(避免事务缺陷)。 – 减少网络开销(合并操作)。 2. 使用方式: – 执行脚本: lua EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 key1 value1 – 预加载脚本: bash SCRIPT LOAD "return redis.call('GET', KEYS[1])" EVALSHA <sha1> 1 key1 3. 优势: – 原子性:脚本执行期间不会被其他命令打断。 – 高性能:脚本在服务端解析执行。 4. 注意: – 避免长 时刻阻塞(Lua 脚本单线程执行)。 后端,Redis
145 HTTP 2.0 和 3.0 有 何区别? 核心区别: HTTP/2.0: 1. 二进制分帧(提升解析效率) 2. 多路复用(一个连接并行处理请求) 3. 头部压缩(HPACK) 4. 服务端推送 HTTP/3.0: 1. 传输层改用 QUIC 协议(基于 UDP) 2. 解决队头阻塞(丢包只影响单个流) 3. 0-RTT 快速建连(减少握手延迟) 4. 连接迁移(IP 变化无感切换) 性能:HTTP/3 在高丢包网络下性能提升 50%+ 1. HTTP/2: – 二进制分帧 – 多路复用 – 头部压缩(HPACK) 2. HTTP/3: – 传输层改用QUIC(基于UDP) – 解决队头阻塞(独立流控制) – 0-RTT建连(加速首次访问) – 连接迁移(IP切换无感) 1. HTTP/2: – 二进制分帧(Binary Framing)。 – 多路复用(Multiplexing)。 – 头部压缩(HPACK)。 2. HTTP/3: – 传输层协议:QUIC(基于 UDP)替代 TCP。 – 0-RTT 连接:首次访问即可加密传输(降低延迟)。 – 改进拥塞控制:内置 BBR 算法。 – 无队头阻塞:数据包独立传输(解决 TCP 重传阻塞)。 3. 性能对比: – HTTP/3 在高丢包网络下延迟降低 30%~50%。 网络
146 单例模式有哪几种实现? 怎样保证线程安全? 实现方式: 1. 饿汉式:类加载时创建(线程安全但可能浪费资源) 2. 懒汉式:双重检查锁(DCL)+ volatile(需防止指令重排) 3. 静态内部类:利用类加载机制保证懒加载和线程安全 4. 枚举:天然线程安全,防反射攻击 线程安全保证: – 关键操作加锁(synchronized) – 使用 volatile 禁止重排序 – 避免构造函数非原子操作 1. 饿汉式:类加载时初始化(线程安全)。 2. 懒汉式: – 同步 技巧(性能差) – DCL双重检查锁(volatile+同步块) 3. 静态内部类:Holder类延迟加载(推荐)。 4. 枚举:天然线程安全(防反射破坏)。 1. 饿汉式: java public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } } – 线程安全(类加载时初始化)。 2. 懒汉式(DCL): java public class Singleton { private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) instance = new Singleton(); } } return instance; } } – volatile 防止指令重排序。 3. 静态内部类: java public class Singleton { private static class Holder { static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return Holder.instance; } } – 懒加载 + 线程安全(JVM 类加载机制)。 4. 枚举: java public enum Singleton { INSTANCE; } – 完全防止反射破坏(《Effective Java》推荐)。 设计模式
147 Java 的 synchronized 是 如何实现的? 实现原理: 1. 修饰代码块:通过 monitorenter/monitorexit 字节码指令,基于对象监视器(Monitor)实现 2. 修饰 技巧:ACC_SYNCHRONIZED 标志位 底层机制: – 锁升级 经过:无锁 → 偏向锁 → 轻量级锁(CAS)→ 重量级锁(OS 互斥量) – 依赖对象头 Mark Word 存储锁 情形 – 重量级锁使用操作 体系互斥量(Mutex),涉及用户态/内核态切换 1. 字节码:monitorenter/monitorexit指令。 2. 锁升级: – 无锁 → 偏向锁(单线程访问) – 偏向锁 → 轻量级锁(多线程竞争) – 轻量级锁 → 重量级锁(自旋失败) 3. 重量级锁:依赖操作 体系mutex(用户态到内核态切换开销大)。 1. 字节码层面: – 同步代码块:monitorenter 和 monitorexit 指令。 – 同步 技巧: 技巧标志位 ACC_SYNCHRONIZED。 2. JVM 层面: – 每个对象关联一个 Monitor(管程)。 – 获取锁: – 无竞争:通过 CAS 将对象头 Mark Word 指向当前线程。 – 有竞争:线程进入 EntryList 阻塞(内核态切换)。 3. 锁升级 经过: – 无锁 → 偏向锁(单线程重入)→ 轻量级锁(CAS 自旋)→ 重量级锁(阻塞)。 4. 优化机制: – 自适应自旋(JDK 6+)。 – 锁消除(逃逸分析)。 – 锁粗化(合并连续锁操作)。 Java并发,Java
148 怎样设计一个秒杀功能? 关键点: 1. 流量削峰: – 答题验证 – 消息队列缓冲请求 2. 读优化: – 缓存商品库存(Redis) 3. 写优化: – Redis 预减库存(原子操作) – 异步下单(消息队列) 4. 防作弊: – 限流(IP/用户) – 隐藏秒杀接口 5. 降级:服务不可用时返回兜底页。 1. 架构分层: – 前端:静态资源CDN;按钮防重复点击 – :限流(令牌桶) – 服务:库存缓存(Redis扣减);MQ削峰 – 数据库:最终库存持久化 2. 关键点: – 预扣库存(防超卖) – 异步下单(MQ解耦) – 热点数据隔离(如Redis分片) 1. 架构分层: – 前端:静态资源 CDN 加速,按钮防重复点击。 – :限流(令牌桶)、黑名单过滤。 2. 流量削峰: – 消息队列缓冲请求(如 RocketMQ 顺序消息)。 – 答题验证码(分散峰值)。 3. 库存管理: – Redis 预减库存(DECR 原子操作)。 – 数据库最终扣减(异步同步)。 4. 防作弊: – 用户限购(Redis 记录购买 情形)。 – 风控 体系(设备指纹、IP 限制)。 5. 降级预案: – 超时未支付库存回滚。 – 故障时降级到排队页面。 后端, 体系设计
149 何故不选择使用原生的 NIO 而选择使用 Netty 呢? 原生 NIO 缺点: 1. API 复杂(需处理 Selector、Buffer 等) 2. 需处理断连重连、网络闪断等 难题 3. 需解决 NIO 空轮询 Bug 4. 需自行实现协议编解码 Netty 优势: – 封装底层细节,提供易用 API – 高性能(零拷贝、对象池) – 社区活跃,文档 丰盛。 1. API复杂性:Netty封装NIO细节(简化开发)。 2. 健壮性:Netty处理了NIO BUG(如空轮询)。 3. 性能优化: – 内存池(减少GC) – 零拷贝 – 高效线程模型(Reactor) 4. 生态: 丰盛的编解码器(HTTP/WebSocket等)。 1. API 复杂性: – NIO 需手动处理 Selector、Buffer 情形(易出错如空轮询 Bug)。 – Netty 封装 ChannelHandler 简化开发。 2. 内存管理: – Netty 的 ByteBuf 支持池化(减少 GC)和零拷贝。 3. 协议支持: – Netty 内置 HTTP/WebSocket/Protobuf 等编解码器。 4. 健壮性: – 心跳检测、断线重连等完善机制。 5. 性能优化: – Reactor 线程模型优化(主从多线程)。 – FastThreadLocal 提升并发性能。 6. 社区生态: – 广泛验证(如 Dubbo、RocketMQ 使用)。 Netty,后端
150 看过源码吗?说下 Spring 由哪些重要的模块组成? 核心模块: 1. spring-core:IoC 容器基础(BeanFactory) 2. spring-beans:Bean 定义与装配 3. spring-context:应用上下文(ApplicationContext) 4. spring-aop:切面编程 5. spring-jdbc:数据库访问 6. spring-web:Web 支持 7. spring-test:测试支持。 1. Core Container:BeanFactory/IoC容器。 2. AOP:面向切面编程支持。 3. Data Access:JDBC/ORM/事务管理。 4. Web:Spring MVC/WebFlux。 5. Test:单元测试/集成测试支持。 6. Context:ApplicationContext(事件传播/国际化)。 1. 核心容器: – spring-beans:Bean 定义与装配(BeanFactory)。 – spring-core:IoC 基础(Resource 抽象)。 2. AOP 模块: – spring-aop:动态代理实现切面。 – spring-aspects:集成 AspectJ。 3. 数据访问: – spring-jdbc:JDBC 模板。 – spring-tx:事务管理(PlatformTransactionManager)。 4. Web 模块: – spring-web:基础 Web 功能(Servlet )。 – spring-webmvc:DispatcherServlet 实现 MVC。 5. 测试模块: – spring-test:JUnit 集成(MockMvc)。 6. 上下文: – spring-context:ApplicationContext 扩展(事件、国际化)。 后端,Spring
151 怎样优化 Java 中的锁的使用? 1. 减小锁粒度:如 ConcurrentHashMap 分段锁 2. 缩短持有 时刻:锁内代码尽量少 3. 读写分离:ReentrantReadWriteLock 4. 无锁化:CAS 操作(Atomic 类) 5. 锁粗化:连续操作合并加锁 6. 使用线程本地变量:ThreadLocal 7. 避免嵌套锁。 1. 减少锁范围:同步块而非同步 技巧。 2. 减小锁粒度:拆分锁(如ConcurrentHashMap分段锁)。 3. 读写分离:ReadWriteLock(读多写少)。 4. 无锁化:CAS(Atomic类)/ThreadLocal。 5. 锁升级:synchronized偏向锁/轻量级锁优化。 1. 减小锁粒度: – 分段锁(如 ConcurrentHashMap 分 16 段)。 – 锁拆分(读写锁 ReentrantReadWriteLock)。 2. 降低锁竞争: – 无锁数据结构(AtomicLong)。 – ThreadLocal 避免共享。 3. 缩短锁持有 时刻: – 同步块内只包含必要代码。 – 用局部变量替代同步块内耗时操作。 4. 锁升级策略: – 优先偏向锁/轻量级锁(减少内核切换)。 5. 避免死锁: – 按固定顺序获取锁。 – tryLock 超时释放。 Java并发,Java
152 Redis 的 Pipeline 功能是 何? 批量发送命令: 1. 客户端将多个命令打包发送 2. 服务端顺序执行后批量返回 结局 3. 减少 RTT(往返 时刻)和 体系调用次数 注意: – 非原子性(命令间可能插入其他请求) – 需合理控制批量 大致(避免客户端缓冲区溢出)。 1. 影响:批量发送命令(减少RTT 时刻)。 2. 原理:客户端缓存多个命令→一次性发送→一次性接收所有响应。 3. 注意: – 非原子性(命令间可能插入其他客户端命令) – 管道内命令有序执行 4. 性能提升:高延迟网络下效果显著。 1. 影响:批量发送多个命令,减少网络往返 时刻(RTT)。 2. 使用方式: java try (Pipeline pipeline = jedis.pipelined()) { pipeline.set("key1", "v1"); pipeline.get("key2"); List<Object> results = pipeline.syncAndReturnAll(); } 3. 注意事项: – Pipeline 非原子操作(只是批量发送)。 – 命令数不宜过多(避免客户端内存溢出)。 4. 性能对比: – 10 条命令:1 次 Pipeline ≈ 1 次普通请求耗时。 后端,Redis
153 让你设计一个分布式 ID 发号器, 如何设计? 要求:全局唯一、 动向递增、高可用。方案: 1. UUID:简单但无序,存储大 2. 数据库自增 ID:分库分表需设置步长 3. Redis INCR:集群需分片 4. Snowflake: 位 = 时刻戳(41位)+ 机器ID(10位)+ 序列号(12位) 5. 美团 Leaf:基于 Snowflake 或数据库号段(双 Buffer 预取)。 1. 要求:全局唯一、 动向递增、高性能。 2. 方案: – 雪花算法: 时刻戳+机器ID+序列号( 位) – Redis INCR:原子递增(需持久化) – 数据库分段:号段缓存(如Leaf-segment) – UUID:无序(索引效率低) 3. 容灾:多机房机器ID分配;时钟回拨处理(雪花算法)。 1. 需求分析: – 全局唯一、 动向递增、高可用、低延迟。 2. 方案对比: – UUID:无序,索引性能差(不推荐)。 – 数据库自增:性能瓶颈,扩展难。 – Redis INCR:集群下需分片(如 INCR user_id_shard1)。 3. 雪花算法(Snowflake): – 位 ID = 符号位(0) + 时刻戳(41ms) + 机器ID(10) + 序列号(12)。 – 单机每秒 409.6 万个 ID。 4. 优化点: – 机器 ID 分配:ZooKeeper 或配置中心管理。 – 时钟回拨:缓存历史 时刻戳或等待。 5. 开源方案: – 百度 UidGenerator、美团 Leaf。 后端, 体系设计
154 何是服务雪崩? 微服务中,一个服务故障引发依赖服务连锁故障,最终导致整个 体系瘫痪。 缘故: 1. 服务不可用(如宕机) 2. 重试风暴(超时重试放大流量) 3. 资源耗尽(线程池满) 预防:熔断降级、限流、异步调用、服务隔离。 1. 定义:服务A故障→服务B调用A超时→B资源耗尽→B故障→连锁故障(如多米诺骨牌)。 2. 诱因: – 高并发 – 重试风暴 – 同步阻塞调用 3. 防御: – 熔断降级 – 限流 – 异步调用 1. 定义: – 单个服务故障引发级联失败,导致整个 体系崩溃。 2. 形成 缘故: – 服务依赖:A → B → C,C 故障导致 B 阻塞 → A 阻塞。 – 线程池耗尽:下游延迟升高,上游调用线程全部阻塞。 3. 解决方案: – 熔断降级:Hystrix 快速失败。 – 限流控制:Sentinel 限制 QPS。 – 超时设置:避免无限等待。 – 异步调用:线程池隔离(Bulkhead 模式)。 服务容灾,Spring Cloud,微服务,后端
155 JVM 由哪些部分组成? 1. 类加载器(ClassLoader):加载字节码 2. 运行时数据区: – 技巧区(元数据) – 堆(对象实例) – 栈(线程私有,存栈帧) – 程序计数器 – 本地 技巧栈 3. 执行引擎:解释器、JIT 编译器、GC 4. 本地库接口(JNI)。 1. 类加载器:加载.class文件。 2. 运行时数据区: – 技巧区(类信息/常量) – 堆(对象实例) – 栈(局部变量/操作数栈) – 程序计数器(当前指令地址) – 本地 技巧栈(Native 技巧) 3. 执行引擎:解释器/JIT编译器。 4. 本地库接口(JNI)。 1. 类加载子 体系: – 加载 .class → 验证 → 准备 → 解析 → 初始化。 2. 运行时数据区: – 线程私有:程序计数器、虚拟机栈、本地 技巧栈。 – 线程共享:堆、 技巧区(元空间)。 3. 执行引擎: – 解释器:逐行执行字节码。 – JIT 编译器:热点代码编译为机器码。 – GC:垃圾回收器(如 CMS、G1)。 4. 本地库接口(JNI): – 调用 Native 技巧(如 System.currentTimeMillis())。 后端,Java,JVM
156 Redis 通常应用于哪些场景? 1. 缓存:加速读请求 2. 会话存储:分布式 Session 3. 排行榜:ZSET 4. 计数器:INCR(阅读量) 5. 消息队列:List/Stream 6. 分布式锁:SETNX 7. 社交关系:Set(共同已关注) 8. 布隆过滤器:防缓存穿透。 1. 缓存:加速数据访问(减轻DB压力)。 2. 会话存储:分布式Session。 3. 排行榜:有序 (ZSET)。 4. 消息队列:List/Stream。 5. 分布式锁:SETNX命令。 6. 计数器:INCR(阅读量)。 1. 缓存: – 数据库查询缓存(减轻 MySQL 压力)。 2. 会话存储: – 分布式 Session(如 Spring Session)。 3. 排行榜: – ZSET 实现实时排名。 4. 计数器: – INCR 实现阅读量/点赞数。 5. 消息队列: – List 实现简单队列(LPUSH/BRPOP)。 6. 分布式锁: – SETNX 实现跨进程互斥。 7. 地理位置: – GEO 存储附近的人(GEORADIUS)。 后端,Redis
157 让你设计一个短链 体系, 如何设计? 核心: 1. 发号器:生成唯一 ID(Snowflake 或 数据库自增) 2. 进制转换:10 进制 ID → 62 进制短码(a-zA-Z0-9) 3. 存储映射:短码 → 原 URL(Redis + MySQL) 4. 跳转:301/302 重定向 5. 过期清理:设置 TTL 6. 防攻击:限流、黑名单。 1. 短链生成: – 哈希算法(MurmurHash)→ 62进制转短码 – 发号器(自增ID转62进制) 2. 存储: – KV数据库(如Redis):短码→原URL – 持久化(MySQL):防丢失 3. 跳转: – 301永久重定向(SEO友好) – 302临时重定向(统计访问量) 4. 优化: – 缓存热点短链 – 防攻击(短码随机性) 1. 短链生成: – 哈希算法:MD5(长链) 取前 6 位(冲突时加盐重试)。 – 自增 ID 转 62 进制(0-9a-zA-Z)。 2. 存储设计: – 映射关系:短链码 → 原始 URL(Redis 缓存 + MySQL 持久化)。 3. 跳转流程: – 用户访问 t.cn/abc123 → 服务端 302 重定向到原地址。 4. 优化点: – 缓存预热:高频短链预加载 Redis。 – 过期清理:设置 TTL 自动清理。 – 防攻击:限制同一 IP 生成频率。 5. 扩展功能: – 访问统计(Redis HyperLogLog 统计 UV)。 后端, 体系设计
158 何是循环依赖(常问)? Bean A 依赖 B,同时 B 依赖 A。Spring 通过 缓存解决: 1. 一级缓存:存放完整 Bean(singletonObjects) 2. 二级缓存:存放半成品 Bean(earlySingletonObjects) 3. 电影缓存:存放 Bean 工厂(singletonFactories) 经过:A 创建 → 暴露工厂到电影缓存 → 填充属性 B → B 创建 → 从电影缓存获取 A 的早期引用 → 完成 B → 完成 A。 1. 定义:Bean A依赖B,同时B依赖A(形成闭环)。 2. Spring解决: – 电影缓存: singletonFactories(早期暴露工厂) – 流程:A创建→注入B→B创建→注入A(从 缓存获取A的半成品) 3. 限制:仅支持单例 影响域的Setter/Field注入(构造器注入无法解决)。 1. 定义: – Bean A 依赖 B,同时 Bean B 依赖 A(形成闭环)。 2. Spring 解决流程(以构造器注入为例): – 一级缓存(单例池)→ 二级缓存(早期曝光对象)→ 电影缓存(ObjectFactory)。 – 流程: 1. 创建 A → 放入电影缓存。 2. A 发现依赖 B → 创建 B。 3. B 发现依赖 A → 从电影缓存获取 A 的 ObjectFactory(得到 A 的代理)。 4. B 完成创建 → 放入一级缓存。 5. A 注入 B 完成创建 → 移入一级缓存。 3. 限制: – 只支持单例 影响域的循环依赖。 – 构造器注入无法解决(需改用 Setter 注入)。 后端,Spring
159 JVM 垃圾回收调优的主要目标是 何? 1. 低延迟:减少 STW(Stop-The-World) 时刻(CMS/G1/ZGC) 2. 高吞吐:单位 时刻处理更多请求(Parallel Scavenge/Old) 3. 内存占用:合理分配堆 大致,避免 OOM 平衡点:根据业务选择(如 Web 服务已关注延迟,计算服务已关注吞吐)。 1. 低延迟:减少STW停顿 时刻(CMS/G1/ZGC)。 2. 高吞吐:单位 时刻处理更多请求(ParallelGC)。 3. 内存占用:合理分配堆 大致(避免频繁GC)。 4. 平衡策略:根据业务选择(如Web服务侧重低延迟,计算服务侧重高吞吐)。 1. 降低停顿 时刻: – 减少 STW(Stop-The-World) 时刻(如 G1 的 MaxGCPauseMillis)。 2. 进步吞吐量: – 单位 时刻内业务运行 时刻占比(如 Parallel GC 调大堆)。 3. 减少内存占用: – 避免堆过大导致 FGC 时刻过长。 4. 避免 OOM: – 合理设置堆 大致(-Xmx)。 – 排查内存泄漏(如 MAT 分析堆转储)。 5. 调优手段: – 选择合适 GC 器(低延迟选 ZGC/Shenandoah)。 – 调整分代比例(-XX:NewRatio)。 JVM,Java
160 Redis 中的 Big Key 难题是 何? 怎样解决? Big Key 难题: 1. 内存不均(单个 Key 过大) 2. 操作阻塞(删除/序列化耗时) 3. 网络拥堵(传输延迟) 4. 集群迁移失败 解决方案: 1. 拆分:Hash 拆分为多个 Key(如 user:1000 → user:1000:base, user:1000:contact) 2. 压缩:使用 Gzip 或 MessagePack 3. 过期设置:自动清理 4. 分片存储:使用 Redis Cluster 5. 渐进式删除:UNLINK 替代 DEL,或 SCAN 分批删除 1. 定义: – String类型 > 10KB – 元素 > 5000个 2. 危害: – 阻塞操作(如del大key) – 网络流量激增 – 内存不均(集群) 3. 解决: – 拆分:hash分field;set分片 – 压缩:序列化优化 – 异步删除:unlink代替del 1. 定义: – String 类型 > 10KB, 类型元素 > 5000 个或总 大致 > 10MB。 2. 危害: – 操作阻塞(删除 1MB Key 耗时约 1ms)。 – 网络拥塞(传输大 Key 占带宽)。 – 内存不均(集群数据倾斜)。 3. 解决方案: – 拆分:大 Hash 拆分为多个小 Key(如 user:1:info → user:1:base + user:1:contact)。 – 压缩:序列化压缩(如 Protobuf)。 – 异步删除:UNLINK 替代 DEL。 – 监控:redis-cli --bigkeys 扫描大 Key。 后端,Redis
161 HTTP 和 HTTPS 有 何区别? 1. 安全性:HTTP 明文传输;HTTPS = HTTP + SSL/TLS 加密 2. 端口:HTTP 80;HTTPS 443 3. 证书:HTTPS 需 CA 证书验证身份 4. 性能:HTTPS 有加密开销(可通过 TLS 1.3 优化) 5. SEO:搜索引擎优先索引 HTTPS 站点。 1. 安全性: – HTTP明文传输 – HTTPS = HTTP + SSL/TLS(加密) 2. 端口:HTTP(80);HTTPS(443)。 3. 证书:HTTPS需CA证书验证身份。 4. 性能:HTTPS握手开销大(可通过会话复用优化)。 1. 安全性: – HTTP:明文传输(易被 )。 – HTTPS:SSL/TLS 加密(防 、篡改、冒充)。 2. 端口: – HTTP:80。 – HTTPS:443。 3. 职业流程: – HTTPS 需 CA 证书验证身份 → 非对称加密交换密钥 → 对称加密传输数据。 4. 性能: – HTTPS 增加 1~2 个 RTT(握手延迟)。 – 硬件加速减少性能损耗(如 TLS 硬件加速卡)。 5. SEO 影响: – 谷歌优先索引 HTTPS 网站。 网络
162 分布式锁一般都 如何实现? 1. 数据库:唯一索引 + 乐观锁 2. Redis:SET key value NX PX timeout(Redlock 算法) 3. ZooKeeper:创建临时有序节点,最小节点获锁 4. Etcd:租约(Lease)+ 事务(TXN) 选型:Redis 性能高;ZK 可靠性强。 1. 数据库:唯一索引(insert锁记录)。 2. Redis:SETNX + Lua原子操作(Redisson看门狗续期)。 3. Zookeeper:临时有序节点(最小节点获锁)。 4. 对比: – 性能:Redis > ZK > DB – 可靠性:ZK(CP) > Redis(AP) 1. 基于数据库: – 唯一索引实现(INSERT INTO lock_table (lock_name) VALUES ('order_lock'))。 – 缺点:性能差,无自动释放。 2. 基于 Redis: – SET lock_name unique_value NX PX 30000(推荐 Redisson 看门狗续期)。 3. 基于 ZooKeeper: – 创建临时有序节点(最小节点获锁),监听前序节点释放。 4. 基于 Etcd: – 租约(Lease)机制 + Revision 版本号。 5. 对比: | 方案 | 性能 | 可靠性 | 实现复杂度 | |———–|——|——–|————| | Redis | 高 | AP | 低 | | ZK | 中 | CP | 高 | 后端, 体系设计
163 怎样对 Java 的垃圾回收进行调优? 步骤: 1. 监控:GC 日志(-Xloggc)、JVisualVM、Prometheus 2. 分析:停顿 时刻、吞吐量、内存占用 3. 参数调整: – 堆 大致:-Xms/-Xmx – 新生代比例:-XX:NewRatio – 收集器:-XX:+UseG1GC – 停顿目标:-XX:MaxGCPauseMillis 4. 避免内存泄漏。 1. 参数调整: – 堆 大致:-Xms/-Xmx – 新生代比例:-XX:NewRatio – 收集器:-XX:+UseG1GC 2. 监控工具: – jstat查看GC统计 – GC日志分析(-Xloggc) 3. 策略: – 减少Full GC(增加老年代空间) – 减少STW(G1替代CMS) 1. 参数调优: – 堆 大致:-Xms4g -Xmx4g(避免动态扩展)。 – 新生代比例:-XX:NewRatio=2(老年代:新生代=2:1)。 – 晋升阈值:-XX:MaxTenuringThreshold=15。 2. GC 器选择: – 高吞吐:Parallel GC(-XX:+UseParallelGC)。 – 低延迟:G1(-XX:+UseG1GC)或 ZGC(-XX:+UseZGC)。 3. 监控工具: – jstat -gcutil 实时 GC 统计。 – GC 日志分析(-Xloggc:gc.log -XX:+Print etails)。 4. 优化策略: – 减少对象分配速率(对象池复用)。 – 避免大对象直接进入老年代(-XX:PretenureSizeThreshold)。 JVM,Java
1 怎样设计一个点赞 体系? 需求:防刷、计数、按 时刻排序。方案: 1. 存储: – 点赞关系:Redis Set(文章ID+用户ID) – 点赞数:Redis Hash(文章ID → 计数) 2. 防刷: – 用户频率限制(Redis INCR) – IP 限制 3. 持久化:异步写入 DB 4. 排行榜:ZSET 按 时刻排序。 1. 存储: – Redis:Hash存储用户点赞 情形(key:内容ID, field:用户ID) – MySQL:持久化点赞总数(异步更新) 2. 性能: – 批量写入(合并DB操作) – 缓存总数(Redis原子INCR) 3. 防刷:用户ID/IP限流。 1. 存储设计: – Redis 计数:INCR 统计点赞数(Hash 存储用户-对象关系)。 – 数据库持久化:异步同步到 MySQL(用户ID+内容ID+ 时刻)。 2. 去重与限额: – 用户维度:SADD user:1:likes post:100 防重复点赞。 – 内容维度:每日/总点赞数限制(Redis Lua 脚本原子检查)。 3. 数据统计: – 热帖排行:ZSET 按点赞数排序。 – 最近点赞:List 存储最新 N 个点赞用户。 4. 性能优化: – 批量写入:定时合并 Redis 数据到 DB。 – 缓存穿透:布隆过滤器过滤无效内容ID。 体系设计,场景题
165 Spring 怎样解决循环依赖? 电影缓存: 1. singletonFactories(电影):存 ObjectFactory(可返回早期代理对象) 2. earlySingletonObjects(二级):存半成品 Bean 3. singletonObjects(一级):存完整 Bean 流程(以 A→B→A 为例): – A 实例化 → 加入 缓存 – A 填充属性 B → 触发 B 创建 – B 填充属性 A → 从电影缓存获取 A 的早期引用 → B 完成 → A 完成。 1. 电影缓存: – singletonObjects:完整Bean – earlySingletonObjects:早期Bean(未注入属性) – singletonFactories:Bean工厂(可生成早期Bean) 2. 流程: – A创建→放入 缓存→注入B→B创建→注入A(从电影缓存获取A的工厂→生成早期A)→B完成→A完成 3. 限制:仅支持单例 影响域的Setter/Field注入。 1. 电影缓存机制: – singletonObjects(一级):完整 Bean。 – earlySingletonObjects(二级):提前曝光的半成品 Bean(已构造未注入属性)。 – singletonFactories( ):Bean 工厂(可生成代理对象)。 2. 解决流程(以 Setter 注入为例): – 创建 A → 放入电影缓存(ObjectFactory)。 – A 发现依赖 B → 创建 B。 – B 发现依赖 A → 从电影缓存获取 A 的代理对象(提前曝光)→ B 创建完成放入一级缓存。 – A 注入 B → 完成初始化 → 移入一级缓存。 3. 限制: – 仅支持单例 影响域。 – 构造器注入无法解决(需改用 Setter 注入)。 后端,Spring
166 HTTP 与 RPC 之间的区别? HTTP: – 应用层协议,基于文本(可读性好) – 通用性强(跨语言) – 性能较低(头较大) RPC: – 远程 经过调用协议(可基于 TCP/HTTP) – 高性能(二进制序列化) – 服务治理(熔断、负载均衡) 定位:HTTP 适合开放接口;RPC 适合内部微服务。 1. 协议:HTTP是应用层协议;RPC是框架(可基于TCP/HTTP)。 2. 性能:RPC通常定制协议(二进制序列化)比HTTP+JSON高效。 3. 服务治理:RPC框架内置负载均衡/熔断(HTTP需 )。 4. 跨语言:HTTP更通用(RPC需跨语言支持如gRPC)。 1. 协议层: – HTTP:应用层协议(文本头 + 可读性强)。 – RPC:可基于 TCP/HTTP 的自定义协议(如 Dubbo 二进制协议)。 2. 性能: – HTTP/1.1 有队头阻塞,RPC 协议通常更高效(如 gRPC/Protobuf)。 3. 服务治理: – RPC 框架内置负载均衡、熔断、链路定位(如 Dubbo + Sentinel)。 – HTTP 需配合 (Spring Cloud Gateway)+ 服务网格(Istio)。 4. 适用场景: – HTTP:跨平台、浏览器兼容(如 RESTful API)。 – RPC:微服务内部高性能调用。 微服务,Spring Cloud,远程调用,后端
167 常用的 JVM 配置参数有哪些? 1. 堆 大致:-Xms(初始堆)、-Xmx(最大堆) 2. 新生代:-Xmn( 大致)、-XX:SurvivorRatio(Eden/Survivor 比例) 3. 元空间:-XX:MetaspaceSize、-XX:MaxMetaspaceSize 4. GC 日志:-Xloggc:file -XX:+Print etails 5. 收集器:-XX:+UseG1GC 6. OOM 时 dump:-XX:+HeapDumpOnOutOfMemoryError。 1. 堆内存:-Xms(初始堆)、-Xmx(最大堆)。 2. 新生代:-Xmn( 大致)、-XX:SurvivorRatio(Eden/Survivor)。 3. 技巧区:-XX:MetaspaceSize(元空间初始)、-XX:MaxMetaspaceSize(最大)。 4. GC日志:-Xloggc:gc.log、-XX:+Print etails。 5. 收集器:-XX:+UseG1GC(启用G1)。 1. 堆内存: – -Xms4g:初始堆 大致。 – -Xmx4g:最大堆 大致。 2. 元空间: – -XX:MetaspaceSize=256m:初始 大致。 – -XX:MaxMetaspaceSize=512m:上限。 3. 垃圾回收: – -XX:+UseG1GC:启用 G1 回收器。 – -XX:MaxGCPauseMillis=200:目标停顿 时刻。 4. OOM 处理: – -XX:+HeapDumpOnOutOfMemoryError:自动 Dump 堆。 – -XX:HeapDumpPath=/logs/dump.hprof:Dump 路径。 5. 监控: – -XX:+Print etails:打印 GC 日志。 – -Xloggc:/logs/gc.log:GC 日志路径。 JVM,Java
168 怎样解决 Redis 中的热点 key 难题? 热点 key:高频访问的 key。方案: 1. 本地缓存:客户端缓存热点 key(需解决一致性) 2. 分散热点: – 拆 key:将 key 拆为 key1、key2… – 哈希分片:同一业务不同 key(如 suffix 取模) 3. 备份 key:多副本 key_{1…N},随机访问。 1. 本地缓存:客户端缓存热key(如Caffeine)。 2. 分片:为热key添加随机后缀(分散到多个节点)。 3. 备份:在多个节点存副本(读写分流)。 4. 限流:针对热key接口限流。 1. 识别热点: – redis-cli --hotkeys 或监控客户端调用统计。 2. 解决方案: – 本地缓存:客户端缓存热点 Key(如 Caffeine)。 – 分片打散:为 Key 添加随机后缀(如 key_{1..10})。 – 读写分离:从节点处理读请求。 – 限流降级:熔断保护(如 Sentinel)。 3. 预防措施: – 避免大 Value(压缩或拆分)。 – 设置合理过期 时刻。 后端,Redis
169 TCP 是用来解决 何 难题? 解决 IP 协议不可靠传输 难题: 1. 数据包丢失:通过超时重传和快速重传机制 2. 数据包乱序:通过序列号和重组机制 3. 流量控制:滑动窗口协议防止接收方被淹没 4. 拥塞控制:慢启动、拥塞避免、快恢复算法 5. 连接管理:三次握手建立连接,四次挥手释放连接 本质:在不可靠的 IP 层上实现可靠的数据流传输 1. 可靠传输:解决IP层不可靠(丢包/乱序)。 2. 流量控制:接收方控制发送速率(滑动窗口)。 3. 拥塞控制:避免网络过载(慢启动/拥塞避免)。 4. 端到端连接:抽象应用层通信(端口号标识应用)。 1. 核心目标:在不可靠的 IP 网络上提供可靠的数据传输。 2. 解决的关键 难题: – 数据丢失:超时重传机制。 – 乱序到达:序列号重组数据包。 – 流量控制:滑动窗口匹配接收方处理能力。 – 拥塞控制:慢启动、拥塞避免、快重传算法。 3. 适用场景: – 需要可靠传输的应用(HTTP、FTP、数据库连接)。 4. 代价: – 连接建立开销(三次握手)。 – 协议头额外开销(至少 20 字节)。 网络
170 让你设计一个 RPC 框架, 如何设计? 核心模块设计: 1. 传输层:Netty 实现 TCP 通信 2. 协议层:自定义二进制协议(魔数+长度+序列化方式+数据) 3. 序列化:Protobuf/Kryo/Hessian 4. 服务注册发现:ZooKeeper/Nacos 5. 动态代理:JDK/CGLib 生成客户端代理 6. 负载均衡:随机/轮询/一致性哈希 7. 容错机制:超时重试、熔断降级 8. 监控:调用链路跟踪(TraceID) 1. 通信层:Netty实现TCP传输(编解码)。 2. 序列化:Protobuf/JSON(高效跨语言)。 3. 服务治理: – 注册中心(Zookeeper/Nacos) – 负载均衡(随机/轮询) – 容错(重试/熔断) 4. 动态代理:客户端生成服务接口代理(透明调用)。 5. 异步:支持Future/回调。 1. 通信层: – 基于 Netty 实现 NIO 网络传输。 – 自定义二进制协议(消息头 + 体)。 2. 序列化: – 支持 JSON/Protobuf/Hessian(可配置)。 3. 服务治理: – 注册中心(ZooKeeper/Nacos)管理服务提供者。 – 负载均衡(随机/轮询/一致性哈希)。 4. 代理层: – 动态代理(JDK/CGLIB)透明化远程调用。 5. 容错机制: – 超时重试、熔断降级(集成 Sentinel)。 6. 扩展点: – SPI 机制支持插件化(如 Filter 链)。 后端, 体系设计
171 Java 中常见的垃圾收集器有哪些? 新生代收集器: 1. Serial:单线程, 算法 2. ParNew:多线程版 Serial 3. Parallel Scavenge:吞吐量优先 老年代收集器: 1. Serial Old:标记-整理 2. Parallel Old:多线程版 Serial Old 3. CMS:标记-清除,低停顿(四阶段) 整堆收集器: 1. G1:分区域收集,可预测停顿 2. ZGC:<10ms 停顿,染色指针 3. Shenandoah:低停顿,并发整理 1. 新生代: – Serial:单线程STW – ParNew:多线程版Serial – Parallel Scavenge:吞吐优先 2. 老年代: – Serial Old – Parallel Old – CMS:并发标记清除(低停顿) 3. 整堆: – G1:分区收集(JDK9默认) – ZGC:TB级堆,<10ms停顿 – Shenandoah:低停顿 1. 新生代收集器: – Serial:单线程 STW(Client 模式默认)。 – ParNew:Serial 的多线程版(配合 CMS)。 – Parallel Scavenge:吞吐量优先(Server 模式默认)。 2. 老年代收集器: – Serial Old:Serial 的老年代版。 – Parallel Old:Parallel Scavenge 的老年代搭档。 – CMS:低延迟(标记-清除算法,已废弃)。 3. 全堆收集器: – G1:分 Region 回收(JDK 9+ 默认)。 – ZGC:亚毫秒延迟(TB 级堆)。 – Shenandoah:低延迟(Red Hat 贡献)。 JVM,Java
172 何是限流?限流算法有哪些? 如何实现的? 限流:控制 体系单位 时刻内的请求量,防止服务过载 算法: 1. 计数器:固定窗口(实现简单,但临界突变) 2. 滑动窗口:细分 时刻片(更平滑) 3. 漏桶:恒定速率流出(队列缓冲) 4. 令牌桶:定时添加令牌,突发流量适应(Guava RateLimiter) 5. 分布式限流:Redis + Lua(原子操作) 实现: – 层:Nginx limit_req – 应用层:Spring Cloud Gateway/Sentinel 1. 定义:控制单位 时刻请求量(防 体系过载)。 2. 算法: – 计数器:固定窗口(简单,临界 难题) – 滑动窗口:多个小窗口统计(解决临界) – 漏桶:恒定速率流出(平滑流量) – 令牌桶:按速率放令牌(允许突发) 3. 实现:Guava RateLimiter(令牌桶)/Sentinel(滑动窗口)。 1. 定义:控制单位 时刻内的请求量(防 体系过载)。 2. 常见算法: – 固定窗口: – 实现:计数器统计每秒请求数(AtomicInteger)。 – 缺点:窗口切换时突发流量超限。 – 滑动窗口: – 实现:将秒拆分为 N 个子窗口(如 10 个 100ms),统计最近 1 秒请求。 – 漏桶算法: – 实现:队列缓冲请求,恒定速率处理(BlockingQueue)。 – 令牌桶: – 实现:定时添加令牌(ScheduledThreadPool),请求获取令牌执行(Guava RateLimiter)。 3. 工具: – 层:Nginx limit_req。 – 应用层:Sentinel、Resilience4j。 后端, 体系设计
173 Netty 性能 何故这么高? 高性能 缘故: 1. 异步非阻塞:基于 NIO 事件驱动模型 2. 零拷贝: – FileRegion 实现 sendfile – CompositeByteBuf 合并缓冲区 3. 内存池:重用 ByteBuf 减少 GC 4. 高效线程模型:主从 Reactor 多线程 5. 无锁化设计:串行化处理 Channel 事件 6. 优化协议:支持 Protobuf 等高效序列化 1. 线程模型:主从Reactor(bossGroup处理连接,workerGroup处理I/O)。 2. 内存管理:池化DirectByteBuffer(减少GC)。 3. 零拷贝:FileRegion减少内核态拷贝。 4. 无锁化:串行化处理Channel事件(避免锁竞争)。 5. 高效序列化:Protobuf等二进制协议。 1. Reactor 线程模型: – 主从多线程:Boss 组处理连接,Worker 组处理 I/O。 2. 零拷贝优化: – FileRegion 支持 sendfile 体系调用。 – CompositeByteBuf 减少内存拷贝。 3. 内存管理: – 池化 ByteBuf(Recycler 对象池)。 – 堆外内存避免 GC 压力。 4. 高效并发: – FastThreadLocal 优于 JDK ThreadLocal。 – 无锁化设计(如 MPSC 队列)。 5. 协议优化: – 预置编解码器(如 ProtobufVarint32FrameDecoder)。 Netty,后端
174 何故 Spring 循环依赖需要电影缓存,二级不够吗? 缓存必要性: 1. 一级缓存(singletonObjects):存放完整 Bean 2. 二级缓存(earlySingletonObjects):存放半成品 Bean(已实例化未初始化) 3. 电影缓存(singletonFactories):存放 Bean 工厂(可生成代理对象) 关键点:当存在 AOP 代理时,二级缓存无法处理代理对象创建。电影缓存通过 ObjectFactory 延迟生成代理对象,保证代理的唯一性 1. 二级缓存(earlySingletonObjects) 难题:无法处理AOP代理对象。 2. 电影缓存优势:singletonFactories存储ObjectFactory,可返回代理对象。 3. 流程: – 普通Bean:工厂直接返回原始对象 – AOP代理:工厂返回代理对象(确保依赖注入的是代理) 1. 二级缓存 难题: – 若只有二级缓存(earlySingletonObjects),无法处理 AOP 代理对象循环依赖。 2. 电影缓存必要性: – 电影缓存存储 ObjectFactory(() -> getEarlyBeanReference(beanName, mbd, bean))。 – 在需要提前曝光时,通过 ObjectFactory 生成代理对象(解决 AOP 代理 难题)。 3. 示例流程: – 若 A 需生成代理: 1. A 工厂放入电影缓存。 2. B 依赖 A 时,通过电影缓存工厂创建代理对象 → 放入二级缓存。 3. A 初始化完成后替换为完整代理对象。 4. 结论:电影缓存确保代理对象被正确创建和复用。 后端,Spring
175 JVM 的内存区域是 怎样划分的? 线程私有: 1. 程序计数器:当前指令地址 2. 虚拟机栈:栈帧(局部变量表/操作数栈等) 3. 本地 技巧栈:Native 技巧 线程共享: 1. 堆:对象实例(新生代 Eden/S0/S1 + 老年代) 2. 技巧区(元空间):类信息/常量/静态变量 3. 直接内存:NIO 使用的堆外内存 注意:JDK8 移除永久代, 技巧区由本地内存实现的元空间替代 1. 线程私有: – 程序计数器:当前指令地址 – 虚拟机栈:Java 技巧栈帧(局部变量表/操作数栈) – 本地 技巧栈:Native 技巧 2. 线程共享: – 堆:对象实例 – 技巧区:类信息/常量/静态变量(JDK8后为元空间) 1. 线程私有: – 程序计数器:当前指令地址(唯一无 OOM 区域)。 – 虚拟机栈: 技巧调用的栈帧(局部变量表、操作数栈),可能 StackOverflowError。 – 本地 技巧栈:Native 技巧调用。 2. 线程共享: – 堆:对象实例存储区(GC 主战场),可 OOM。 – 技巧区:类信息、常量、静态变量(JDK 8 后为元空间,使用本地内存)。 3. 直接内存: – NIO 的 DirectBuffer 分配的内存(不归 JVM 管,但可能 OOM)。 JVM,Java
176 Redis 的持久化机制有哪些? 1. RDB(快照): – 定时生成数据快照(二进制) – 恢复快 – 可能丢失 最后一次快照后的数据 2. AOF(日志): – 记录写命令(文本) – 支持每秒同步/每命令同步 – 恢复慢但数据更完整 3. 混合持久化(4.0+):AOF 包含 RDB 头 + 增量 AOF 命令。 1. RDB: – 定时生成数据快照(二进制文件) – 命令:S E(阻塞)/BGS E(后台) – 优点:恢复快 – 缺点:可能丢数据 2. AOF: – 记录写命令(追加日志) – 刷盘策略:always/everysec/no – 优点:数据安全 – 缺点:文件大,恢复慢 3. 混合:AOF重写时用RDB格式(Redis 4.0+)。 1. RDB(快照): – 定时生成数据集的二进制快照(S E 阻塞 / BGS E 后台)。 – 优点:恢复快(适合备份)。 – 缺点:可能丢失 最后一次快照后的数据。 2. AOF(追加日志): – 记录每个写命令(appendonly yes)。 – 刷盘策略: – always:每条命令刷盘(强一致,性能差)。 – everysec:每秒刷盘(推荐)。 – no:由 体系决定。 – 优点:数据完整性高。 – 缺点:文件大(需重写优化)。 3. 混合持久化(Redis 4.0+): – AOF 包含 RDB 头 + 增量 AOF 命令(结合两者优势)。 后端,Redis
177 如果发现 Redis 内存溢出了?你会 如何做?请给出排查思路和解决方案 排查: 1. info memory:分析 used_memory 2. 扫描大 Key:redis-cli –bigkeys 3. 查看 Key 数量:dbsize 4. 检查过期策略:ttl key 解决: 1. 清理大 Key:拆分或删除 2. 设置过期 时刻 3. 增加内存或集群分片 4. 优化数据结构(如 Hash 用 ziplist) 5. 升级 Redis 版本(6.0 多线程优化)。 1. 排查: – info memory查看内存分布 – redis-cli –bigkeys找大Key – 检查过期策略(ttl key) 2. 解决: – 删除大Key(unlink异步) – 增加内存/集群分片 – 调整 xmemory-policy(如LRU) – 优化数据结构(如hash用ziplist) 1. 定位 缘故: – info memory 查看内存分布(used_memory_hu n)。 – redis-cli --bigkeys 扫描大 Key。 – slowlog get 分析慢查询。 2. 解决方案: – 清理大 Key:分拆或删除(UNLINK key)。 – 调整淘汰策略:config set xmemory-policy allkeys-lru。 – 增加内存:集群分片或升级机器。 – 优化数据结构: – 小 用 ziplist(hash- x-ziplist-entries 512)。 – HyperLogLog 替代 Set 统计 UV。 3. 预防措施: – 监控内存使用率(超过 80% 告警)。 – 设置 xmemory 限制内存。 后端,场景题
178 负载均衡算法有哪些? 1. 轮询(Round Robin):依次分配 2. 加权轮询:按权重比例分配 3. 随机(Random) 4. 加权随机 5. 最少连接(Least Connections):选连接数最少的 6. IP 哈希(IP Hash):同一 IP 固定到同一服务 7. 一致性哈希:分布式场景减少 rehash 影响。 1. 轮询(Round Robin):依次分配。 2. 加权轮询:按权重分配。 3. 随机:随机选择。 4. 最少连接:选当前连接数最少的。 5. IP哈希:按客户端IP哈希固定分配。 6. 一致性哈希:扩缩容影响小。 1. 静态算法: – 轮询(Round Robin):按顺序分配请求。 – 加权轮询:根据服务器权重分配(性能高的多分)。 – 源地址哈希(IP Hash):相同 IP 固定访问某服务器(保持会话)。 2. 动态算法: – 最少连接(Least Connections):优先选当前连接数少的服务器。 – 加权最少连接:结合权重和连接数。 – 响应 时刻加权:优先选响应快的(需监控探测)。 3. 独特算法: – 一致性哈希:节点增减时影响最小(分布式缓存)。 4. 实现工具: – Nginx、Spring Cloud Ribbon、LVS。 负载均衡,Spring Cloud,微服务,后端
179 Java 中有哪些垃圾回收算法? 1. 标记-清除:碎片多 2. 算法:分两块,存活对象 到另一块(新生代) 3. 标记-整理:移动存活对象(老年代) 4. 分代收集:新生代( )、老年代(标记-清除/整理) 5. G1:分区收集,可预测停顿 6. ZGC:着色指针、读屏障,低延迟。 1. 标记-清除:标记可达对象→清除未标记(内存碎片)。 2. :内存分两块,存活对象 到另一块(无碎片,空间浪费)。 3. 标记-整理:标记→存活对象向一端移动→清理边界外(无碎片)。 4. 分代收集:新生代( 算法),老年代(标记-清除/整理)。 1. 标记-清除: – 标记可达对象 → 清除未标记对象。 – 缺点:内存碎片。 2. 算法: – 内存分两块,存活对象 到另一块(新生代 Eden/S0/S1)。 – 优点:无碎片。 – 缺点:空间浪费。 3. 标记-整理: – 标记后存活对象向一端移动(老年代常用)。 4. 分代收集: – 新生代: 算法(Minor GC)。 – 老年代:标记-清除/整理(Full GC)。 5. 分区算法(G1/ZGC): – 堆划分为多个 Region,优先回收 价格高的 Region。 JVM,Java
180 Redis 中的缓存击穿、缓存穿透和缓存雪崩是 何? 缓存击穿:热点 key 过期,大量请求直击 DB 缓存穿透:查询不存在的数据(如恶意请求),绕过缓存 缓存雪崩:大量 key 同时过期,DB 压力骤增 解决: – 击穿:热点数据永不过期 + 互斥锁 – 穿透:布隆过滤器 + 空值缓存 – 雪崩:随机过期 时刻 + 集群高可用。 1. 缓存击穿:热key过期瞬间大量请求直达DB(解决:永不过期/互斥锁重建)。 2. 缓存穿透:查询不存在的数据(解决:布隆过滤器/空值缓存)。 3. 缓存雪崩:大量key同时过期(解决:随机过期 时刻/集群部署)。 1. 缓存穿透: – 难题:查询不存在的数据(绕过缓存击穿数据库)。 – 解决:布隆过滤器拦截无效请求;缓存空值(SET null 5s)。 2. 缓存击穿: – 难题:热点 Key 失效瞬间大量请求压垮数据库。 – 解决:永不过期(逻辑过期 时刻);互斥锁重建缓存(Redis SETNX)。 3. 缓存雪崩: – 难题:大量 Key 同时失效或 Redis 宕机。 – 解决: – 过期 时刻加随机值(避免同时失效)。 – 集群高可用(哨兵/Cluster)。 – 多级缓存(本地缓存 + Redis)。 后端,Redis
181 线上发现 Redis 机器爆了, 怎样优化? 1. 紧急扩容:增加内存或节点 2. 分析内存:redis-cli –bigkeys 找大 Key 3. 清理数据: – 删除非关键数据 – 设置过期 时刻 4. 优化数据结构: – 压缩存储(hash 用 ziplist) – 拆分大 Key 5. 升级集群:分片存储。 1. 紧急: – 扩容(增加实例/集群分片) – 删除非关键数据 2. 优化: – 大Key拆分 – 热Key分散(添加随机后缀) – 内存淘汰策略调整 – 数据压缩(如snappy) 3. 监控:设置内存阈值告警。 1. 紧急处理: – 清理大 Key(UNLINK)。 – 调整淘汰策略(CONFIG SET xmemory-policy allkeys-lru)。 – 重启释放碎片(MEMORY PURGE)。 2. 扩容: – 垂直扩容:升级更大内存机器。 – 水平扩容:集群分片(如 Codis/Redis Cluster)。 3. 内存优化: – 小数据用 ziplist(调整 hash- x-ziplist-entries)。 – 数据压缩(如 Snappy)。 4. 预防措施: – 监控内存告警(超过 80% 预警)。 – 定期扫描大 Key(redis-cli --bigkeys)。 – 设置 xmemory 限制。 后端,场景题
182 说下 Spring Bean 的 生活周期? 1. 实例化:调用构造函数 2. 属性赋值:注入依赖(@Autowired) 3. BeanPostProcessor.postProcessBeforeInitialization 4. 初始化: – 实现 InitializingBean.afterPropertiesSet() – 自定义 init-method 5. BeanPostProcessor.postProcessAfterInitialization 6. 使用中 7. 销毁: – 实现 DisposableBean.destroy() – 自定义 destroy-method。 1. 实例化:new对象。 2. 属性赋值:populateBean()注入依赖。 3. 初始化: – Aware接口(BeanNameAware) – BeanPostProcessor前置处理 – InitializingBean.afterPropertiesSet() – 自定义init-method – BeanPostProcessor后置处理(AOP代理在此生成) 4. 销毁:DisposableBean.destroy() + 自定义destroy-method。 1. 实例化: – 通过构造函数或工厂 技巧创建 Bean 实例。 2. 属性赋值: – 注入依赖(@Autowired 或 XML 配置)。 3. 初始化: – 调用 BeanPostProcessor.postProcessBeforeInitialization()。 – 执行 InitializingBean.afterPropertiesSet() 或 init-method。 – 调用 BeanPostProcessor.postProcessAfterInitialization()。 4. 使用中: – Bean 可被应用使用。 5. 销毁: – 执行 DisposableBean.destroy() 或 destroy-method。 – 调用 DestructionAwareBeanPostProcessor.postProcessBeforeDestruction()。 后端,Spring
183 JVM 有那几种情况会产生 OOM(内存溢出)? 1. Java 堆溢出:对象过多(-Xmx 不足) 2. 元空间溢出:加载类过多(-XX:MaxMetaspaceSize 不足) 3. 栈溢出:递归过深(-Xss 不足) 4. 直接内存溢出:NIO 操作(-XX:MaxDirectMemorySize 不足) 5. GC 开销超限:垃圾回收效率过低。 1. 堆溢出:对象过多(java.lang.OutOfMemoryError: Java heap space)。 2. 栈溢出:递归过深(StackOverflowError)。 3. 技巧区溢出:类过多(OOM: Metaspace)。 4. 直接内存溢出:NIO使用不当(OOM: Direct buffer memory)。 5. GC效率低下:频繁Full GC但回收少(OOM: GC Overhead limit exceeded)。 1. 堆内存溢出: – 缘故:对象过多或大对象(java.lang.OutOfMemoryError: Java heap space)。 2. 元空间溢出: – 缘故:加载类过多(java.lang.OutOfMemoryError: Metaspace)。 3. 栈溢出: – 缘故:无限递归(java.lang.StackOverflowError)。 4. 直接内存溢出: – 缘故:NIO DirectBuffer 分配过多(java.lang.OutOfMemoryError: Direct buffer memory)。 5. 线程溢出: – 缘故:创建线程数超过限制(java.lang.OutOfMemoryError: unable to create new native thread)。 后端,Java,JVM
184 Redis 在生成 RDB 文件时 怎样处理请求? 1. fork 子进程:父进程继续处理请求(写时 ) 2. 子进程遍历内存生成 RDB 快照 3. 父进程的写操作会 内存页(修改数据时) 4. RDB 生成后替换旧文件 注意: – fork 可能阻塞主线程(内存大时) – 写操作多时内存占用翻倍。 1. 写时 (Copy-On-Write): – 主进程fork子进程(共享内存) – 子进程遍历内存生成RDB 2. 写请求处理: – 父进程修改数据时 内存页(保证子进程数据一致性) – 新数据写入新内存页 3. 结局:RDB生成期间可继续处理请求(但写操作触发内存 影响性能)。 1. 写时 (Copy-On-Write): – 父进程(主线程)继续处理请求。 – 子进程遍历内存数据写入 RDB 文件(不阻塞主线程)。 2. 数据一致性保证: – 子进程基于 fork 瞬间的内存快照生成 RDB。 – 父进程修改的数据在内存中 副本(不影响子进程)。 3. 性能影响: – 写操作增加(需 修改的页)。 – 内存占用翻倍(极端情况下)。 4. 配置建议: – 低峰期执行 BGS E(避免频繁 fork)。 后端,Redis
185 TCP 和 UDP 有 何区别? TCP: – 面向连接(三次握手) – 可靠传输(确认重传) – 有序交付 – 流量控制 – 头部大(20 字节) UDP: – 无连接 – 不可靠(可能丢包) – 无序 – 头部小(8 字节) 适用:TCP 文件传输;UDP 音视频、DNS。 1. 连接:TCP面向连接(三次握手);UDP无连接。 2. 可靠性:TCP可靠(确认重传/有序);UDP可能丢包乱序。 3. 效率:TCP头部大(20字节);UDP头部小(8字节)。 4. 场景:TCP(文件传输/网页);UDP(视频流/DNS)。 1. 可靠性: – TCP:有连接、可靠交付(确认重传)。 – UDP:无连接、尽最大努力交付(可能丢包)。 2. 传输效率: – TCP:效率低(头部至少 20 字节,建立连接慢)。 – UDP:效率高(头部 8 字节,无连接开销)。 3. 数据边界: – TCP:字节流(无消息边界,需应用层处理粘包)。 – UDP:数据报(有明确边界)。 4. 应用场景: – TCP:文件传输、Web 浏览(HTTP)。 – UDP:视频流、DNS、实时游戏。 网络
186 线上 CPU 飙高 怎样排查? 步骤: 1. top 定位高 CPU 进程 2. top -Hp pid 定位高 CPU 线程 3. printf “%x” tid 转线程 ID 为十六进制 4. jstack pid | grep nid -A 30 查看线程栈 5. 分析代码: – 死循环? – 频繁 GC? – 锁竞争? 工具:Arthas(thread -n 3)。 1. 定位线程:top -Hp pid(找高CPU线程)。 2. 线程转储:jstack pid > thread.log(查线程堆栈)。 3. 分析: – 将线程ID(十进制)转16进制 – 在thread.log中匹配nid 4. 缘故: – 死循环 – 频繁GC – 锁竞争 1. 定位高 CPU 进程: – top -c 查看进程列表(按 P 排序)。 2. 定位高 CPU 线程: – top -Hp <PID> → 记录线程 ID(转为十六进制)。 3. 分析线程栈: – jstack <PID> > stack.log。 – 查找对应线程栈(nid=0x<hex>)。 4. 常见 缘故: – 死循环(如 while(true))。 – 频繁 GC(jstat -gcutil 查看 GC 情况)。 – 锁竞争(BLOCKED 情形线程)。 5. 工具辅助: – Arthas:thread -n 3 查看最忙线程。 后端,线上 难题排查
187 Java 中 volatile 关键字的 影响是 何? 1. 可见性:写操作立即刷新到主内存,读操作重新从主内存加载 2. 禁止指令重排:内存屏障(LoadLoad/StoreStore) 注意:不保证原子性(如 i++ 需配合 synchronized 或 Atomic)。 1. 可见性:写操作立即同步到主存;读操作从主存读取。 2. 有序性:禁止指令重排序(内存屏障)。 3. 注意:不保证原子性(如i++需配合synchronized/CAS)。 1. 可见性: – 写操作刷新到主内存,读操作从主内存读取(禁用 职业内存缓存)。 2. 有序性: – 禁止指令重排序(内存屏障)。 3. 非原子性: – 不保证复合操作原子性(如 i++ 需配合 synchronized 或 AtomicInteger)。 4. 实现原理: – 写操作后加 StoreLoad 屏障(强制刷内存)。 5. 应用场景: – 情形标志(volatile boolean running)。 – 双重检查锁单例(DCL 需 volatile 防重排序)。 Java并发,Java
188 如何分析 JVM 当前的内存占用情况?OOM 后 如何分析? 分析工具: 1. j p -heap pid:堆概要 2. j p -histo pid:对象直方图 3. jstat -gcutil pid:GC 统计 4. VisualVM:图形化分析 OOM 后: 1. 配置 -XX:+HeapDumpOnOutOfMemoryError 自动生成 dump 文件 2. 用 MAT(Memory Analyzer Tool)或 VisualVM 分析 dump 文件定位泄漏对象。 1. 分析工具: – j p -heap pid(堆概要) – j p -histo pid(对象统计) – jstat -gcutil(GC统计) 2. OOM分析: – -XX:+HeapDumpOnOutOfMemoryError(自动生成dump) – MAT/JVisualVM分析dump文件(找内存泄漏对象) 1. 实时监控: – j p -heap <PID>:堆概要。 – jstat -gc <PID> 1000:每秒 GC 统计。 2. OOM 分析: – 自动 Dump:启动参数加 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./。 – 手动 Dump:j p -dump:for t=b,file=heap.hprof <PID>。 3. 工具分析: – MAT:解析堆转储,查找 Retained Heap 最大的对象。 – VisualVM:实时监控内存泄漏(对象创建跟踪)。 4. 常见 缘故: – 内存泄漏(如静态 持有对象)。 – 大对象分配(如一次性加载大文件)。 后端,场景题,JVM
Spring MVC 具体的 职业原理? 流程: 1. 用户请求 → DispatcherServlet 2. DispatcherServlet 调用 HandlerMapping 获取 Handler(Controller 技巧) 3. 通过 HandlerAdapter 执行 Handler 4. Handler 返回 ModelAndView 5. ViewResolver 解析视图 6. 渲染视图(填充 Model)→ 返回响应。 1. 请求流程: – DispatcherServlet接收请求 – HandlerMapping找Controller – HandlerAdapter调用Controller – Controller返回ModelAndView – ViewResolver解析视图 – View渲染响应 2. 关键组件: – (Interceptor):预处理/后处理 – 参数解析器(HandlerMethodArgumentResolver) – 返回值处理器(HandlerMethodReturnValueHandler) 1. 请求流程: – 用户请求 → DispatcherServlet(前端控制器)。 2. 处理器映射: – HandlerMapping 根据 URL 找到对应 Controller。 3. 处理器适配: – HandlerAdapter 执行 Controller 技巧(反射调用)。 4. 模型处理: – Controller 返回 ModelAndView(包含数据和视图名)。 5. 视图解析: – ViewResolver 将视图名解析为 View 对象(如 JSP)。 6. 渲染响应: – View 渲染模型数据 → 返回客户端。 7. 关键组件: – (Interceptor):AOP 式预处理请求。 – 参数解析器(HandlerMethodArgumentResolver):处理 @RequestParam 等。 后端,Spring
190 分布式和微服务有 何区别? 分布式: – 体系拆分部署在不同节点 – 解决单机性能瓶颈 – 技术异构性(不同服务不同语言) 微服务: – 分布式的一种实现 – 按业务拆分独立服务 – 强调服务自治(独立开发、部署、扩展) – 依赖轻量通信(HTTP/RPC)。 1. 分布式: 体系拆分部署在不同节点(解决单机瓶颈)。 2. 微服务: – 更细粒度拆分(按业务模块) – 独立开发/部署/扩展 – 强调去中心化治理(如独立数据库) 3. 关系:微服务是分布式的一种实现方式。 1. 分布式: – 体系拆分到不同机器(解决单机性能瓶颈)。 – 可能包含多个单体服务(如订单、库存服务独立部署)。 2. 微服务: – 服务按业务边界细粒度拆分(单一 责任)。 – 强调独立开发、部署、扩展(如用户服务、支付服务)。 3. 核心区别: | 维度 | 分布式 | 微服务 | |————|————————–|—————————-| | 拆分粒度 | 较粗(模块级) | 极细(功能单元) | | 技术栈 | 可统一(如 Java) | 异构(不同服务不同语言) | | 治理能力 | 弱(依赖基础设置) | 强(服务发现、熔断等) | 4. 联系: – 微服务是分布式架构的一种最佳 操作。 Spring Cloud,微服务,后端,分布式
191 何是 Java 中的 ABA 难题? CAS 操作时,值从 A→B→A,检查仍为 A 但已变化。解决方案: 1. 版本号:AtomicStampedReference(携带 stamp) 2. 时刻戳:记录修改 时刻 3. 布尔标记:AtomicMarkableReference。 1. 场景:CAS操作时,值从A→B→A(版本未变但实际被修改过)。 2. 危害:可能导致数据不一致。 3. 解决: – 版本号:AtomicStampedReference(带戳引用) – 时刻戳:记录修改 时刻 1. 难题描述: – 线程 1 读取值 A → 线程 2 改 A 为 B 再改回 A → 线程 1 CAS 成功(但值已被修改过)。 2. 风险: – 链表操作中可能导致数据丢失(如原头节点被复用)。 3. 解决方案: – 版本号:AtomicStampedReference 维护(值,版本号)。 – 时刻戳:类似版本号机制。 4. 示例: java AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0); ref.compareAndSet("A", "B", 0, 1); // 需版本号匹配 Java并发,Java
192 Redis 的哨兵机制是 何? 哨兵(Sentinel):Redis 高可用解决方案 功能: 1. 监控:定期检测主从节点 情形 2. 自动故障转移:主节点宕机时选举新主 3. 通知:向客户端推送主节点变更 职业流程: 1. 主观下线(SDOWN):单个 Sentinel 判定节点不可用 2. 客观下线(ODOWN):多个 Sentinel 确认节点失效 3. 选举 Leader Sentinel 4. Leader 执行故障转移(选最优从节点升级为主) 1. 影响:监控主从节点,自动故障转移。 2. 功能: – 监控:定期检测节点 健壮 – 通知:向管理员发送告警 – 自动故障转移:主节点宕机时选新主 – 配置中心:提供新主地址 3. 部署:至少3个哨兵(避免误判)。 1. 影响:实现 Redis 主从架构的高可用(自动故障转移)。 2. 核心功能: – 监控:Sentinel 节点定期检查主/从 情形。 – 通知:服务 情形变化时发送告警。 – 自动故障转移: – 主节点失效时,选举新主(从节点中选)。 – 更新客户端配置(通过 Pub/Sub 通知)。 3. 部署要求: – 至少 3 个 Sentinel 节点(避免脑裂)。 4. 客户端集成: – 连接 Sentinel 获取当前主节点地址(SENTINEL get- ster-addr-by-name)。 后端,Redis
193 TCP 的粘包和拆包能说说吗? 粘包:多个数据包被合并成一个 TCP 报文发送 拆包:一个数据包被拆分成多个 TCP 报文 缘故: 1. 应用层报文大于 TCP 发送缓冲区 大致 2. MSS(最大报文段)限制 3. 滑动窗口机制导致合并发送 解决方案: 1. 固定长度:浪费空间 2. 分隔符:如 (需转义) 3. 长度字段:最常用(如协议头中定义 length 字段) 1. 粘包:多个数据包被合并成一个TCP包发送。 2. 拆包:一个数据包被拆成多个TCP包发送。 3. 缘故:TCP是字节流协议(无消息边界)。 4. 解决: – 固定长度 – 分隔符 – 消息头包含长度字段(如LengthFieldBasedFrameDecoder) 1. 粘包: – 发送方多次发送的小数据包被合并为一个包接收(如 send("AB") + send("CD") → 收到 "ABCD")。 2. 拆包: – 大数据包被拆分为多个包接收(如 send("12345") → 收到 "12" + "345")。 3. 缘故: – TCP 是字节流协议,无消息边界。 – 网络 MTU 限制(1500 字节)。 4. 解决方案: – 固定长度:FixedLengthFrameDecoder。 – 分隔符:DelimiterBasedFrameDecoder(如 )。 – 长度字段:LengthFieldBasedFrameDecoder(头 4 字节表长度)。 网络
194 线程的 生活周期在 Java 中是 怎样定义的? 通过 Thread.State 枚举定义: 1. NEW:新建未启动 2. RUNNABLE:可运行(包括就绪和运行中) 3. BLOCKED:等待监视器锁(synchronized) 4. WAITING:无限期等待(Object.wait()/LockSupport.park()) 5. TIMED_WAITING:限期等待(Thread.sleep()/Object.wait(timeout)) 6. TERMINATED:线程终止 注意:RUNNABLE 包含操作 体系层面的就绪(Ready)和运行中(Running) 1. NEW:新建未启动。 2. RUNNABLE:可运行(就绪/运行中)。 3. BLOCKED:等待监视器锁(synchronized)。 4. WAITING:无限期等待(Object.wait())。 5. TIMED_WAITING:限期等待(Thread.sleep())。 6. TERMINATED:终止。 1. NEW: – 创建未启动(new Thread())。 2. RUNNABLE: – 可运行 情形(含就绪和运行中)。 3. BLOCKED: – 等待监视器锁(如 synchronized 竞争失败)。 4. WAITING: – 无限期等待(Object.wait() / LockSupport.park())。 5. TIMED_WAITING: – 限期等待(Thread.sleep(1000) / Object.wait(1000))。 6. TERMINATED: – 线程执行完毕。 情形转换: – start():NEW → RUNNABLE。 – synchronized 竞争失败:RUNNABLE → BLOCKED。 – wait():RUNNABLE → WAITING。 – notify():WAITING → BLOCKED(需重新竞争锁)。 Java并发,Java
195 在 MySQL 中建索引时需要注意哪些事项? 1. 选择区分度高的列(Cardinality 大) 2. 避免过长字段(前缀索引) 3. 联合索引最左前缀 制度 4. 排序/分组字段加索引 5. 避免过多索引(影响写性能) 6. 使用覆盖索引减少回表 7. NULL 值处理(IS NULL 可能不走索引)。 1. 选择列:高区分度列(如ID);常查询/排序的列。 2. 避免冗余:联合索引覆盖多个查询。 3. 前缀索引:对长字符串建索引(节省空间)。 4. 控制数量:索引过多影响写性能。 5. 最左前缀:合理设计联合索引顺序。 1. 选择合适列: – WHERE/JOIN/ORDER BY/GROUP BY 的列。 2. 避免冗余索引: – 联合索引 (a,b) 可替代单列索引 (a)。 3. 控制索引数量: – 过多索引降低写性能(每次写更新索引)。 4. 前缀索引: – 长字符串用前缀(INDEX(e il(10)))。 5. 区分度 制度: – 选择区分度高列(如 ID > 情形标志)。 6. 避免索引失效: – 函数转换(WHERE YEAR(create_time)= 2024 → 失效)。 – 隐式类型转换(WHERE name=123 → 失效)。 后端,MySQL,数据库
196 Netty 采用了哪些设计模式? 1. 职责链:ChannelPipeline(Inbound/Outbound Handler) 2. 观察者:Future-Listener(异步回调) 3. 工厂:ChannelFactory 4. 单例:EventLoopGroup 共享资源 5. 适配器:ChannelHandlerAdapter(空实现) 6. 策略:EventExecutorChooser(选择 EventExecutor)。 1. 职责链:ChannelPipeline(事件处理器链)。 2. 观察者:Future.addListener()(异步回调)。 3. 工厂:ChannelFactory创建Channel。 4. 单例:全局共享的EventLoopGroup。 5. 适配器:ChannelInboundHandlerAdapter(空实现接口)。 1. Reactor 模式: – 主从多线程处理 I/O 事件(事件驱动)。 2. 职责链模式: – ChannelPipeline 由多个 ChannelHandler 组成(如编解码、业务处理)。 3. 观察者模式: – Future 监听操作 结局(ChannelFuture.addListener())。 4. 单例模式: – 共享的 ChannelHandler(如 SharedEventExecutorGroup)。 5. 建造者模式: – ServerBootstrap.group().channel().childHandler() 链式构建。 6. 工厂模式: – EventLoopGroup 创建 EventLoop。 Netty,后端
197 Spring 中的 DI 是 何? 依赖注入(Dependency Injection):容器动态注入对象所需依赖。实现方式: 1. 构造器注入:@Autowired 在构造器上 2. Setter 注入:@Autowired 在 setter 技巧 3. 字段注入:@Autowired 在字段(不推荐) 目的:解耦组件,方便测试(Mock 依赖)。 1. 定义:依赖注入(Dependency Injection)。 2. 核心:容器将依赖对象自动注入目标对象(无需new)。 3. 方式: – 构造器注入 – Setter注入 – 字段注入(@Autowired) 4. 目的:解耦(对象只已关注接口,不已关注具体实现)。 1. 定义:依赖注入(Dependency Injection),对象通过外部传入依赖而非自己创建。 2. 实现方式: – 构造器注入: java @Autowired public UserService(UserRepository repo) { this.repo = repo; } – Setter 注入: java @Autowired public void setRepo(UserRepository repo) { this.repo = repo; } – 字段注入(不推荐): java @Autowired private UserRepository repo; 3. 优点: – 解耦组件(依赖接口而非实现)。 – 方便单元测试(Mock 依赖)。 后端,Spring
198 Redis 主从 的实现原理是 何? 1. 从节点执行 SL EOF 命令 2. 主节点 fork 生成 RDB 文件发送给从节点 3. 从节点加载 RDB 4. 主节点将写命令缓存并发送给从节点( 缓冲区) 5. 从节点执行命令保持同步 6. 增量同步:通过 offset 断点续传。 1. 全量同步: – 从节点发送psync ? -1 – 主节点执行BGS E生成RDB→发送给从 – 从加载RDB 2. 增量同步: – 主维护 积压缓冲区(repl_backlog) – 从断线重连后发送psync [offset] – 主发送offset后的命令 3. 命令传播:主持续发送写命令给从。 1. 建立连接: – 从节点执行 REPLICAOF < ster_ip> <port> 向主节点发起同步请求。 2. 全量同步: – 主节点执行 BGS E 生成 RDB → 发送给从节点。 – 从节点清空旧数据 → 加载 RDB。 3. 增量同步: – 主节点写命令存入 缓冲区(Repl Backlog Buffer)。 – 从节点从缓冲区获取增量命令执行。 4. 持续同步: – 主节点每执行写命令,异步发送给从节点。 5. 断线重连: – 从节点用 PSYNC <runid> <offset> 请求增量同步(若 offset 在缓冲区则增量,否则全量)。 后端,Redis
199 在 何情况下,不推荐为数据库建立索引? 1. 数据量小(全表扫描更快) 2. 写多读少(索引维护代价高) 3. 列区分度低(如性别,索引效果差) 4. 频繁更新的字段 5. 查询中很少涉及的列 6. 长文本字段(可考虑前缀索引)。 1. 数据量小:全表扫描更快。 2. 写多读少:索引维护开销大。 3. 区分度低:如性别(索引效率低)。 4. 频繁更新的列:更新需维护索引。 5. 长文本:全文索引替代普通索引。 1. 数据量极小: – 全表扫描更快(如配置表仅 100 行)。 2. 写多读少: – 索引降低写性能(每次 INSERT/UPDATE 维护索引)。 3. 区分度极低: – 如性别列(值只有男/女),索引效果差。 4. 频繁更新的列: – 维护索引代价过高(如 情形标志位)。 5. 长文本列: – 全文索引替代普通索引(如 FULLTEXT 索引)。 6. 不参与查询的列: – 避免无效索引占用空间。 MySQL,后端,数据库
200 何是 Seata? 阿里开源的分布式事务解决方案。提供: 1. AT 模式(默认):基于 SQL 解析自动生成回滚日志(undo_log) 2. TCC 模式:需实现 Try/Confirm/Cancel 接口 3. Saga 模式:长事务补偿 4. XA 模式:两阶段提交 核心组件:TC(事务协调者)、TM(事务管理器)、RM(资源管理器)。 1. 定义:分布式事务解决方案(Simple Extensible Autonomous Transaction Architecture)。 2. 模式: – AT:自动补偿型事务(基于SQL解析) – TCC:手动补偿事务(Try-Confirm-Cancel) – Saga:长事务( 情形机+补偿) 3. 核心组件:TC(事务协调器)、TM(事务管理器)、RM(资源管理器)。 1. 定义:开源的分布式事务解决方案(Simple Extensible Autonomous Transaction Architecture)。 2. 核心模式: – AT 模式(默认): – 一阶段:解析 SQL 生成快照(undo_log)。 – 二阶段:提交时异步删除 undo_log,回滚时用 undo_log 补偿。 – TCC 模式: – Try:预留资源(如冻结库存)。 – Confirm:确认操作(扣减库存)。 – Cancel:取消操作(解冻库存)。 – Saga 模式:长事务补偿(逆向服务调用)。 3. 架构组件: – TC(Transaction Coordinator):事务协调器(独立部署)。 – TM(Transaction Manager):事务管理器(发起全局事务)。 – RM(Resource Manager):资源管理器(管理分支事务)。 Seata,微服务,Spring Cloud,后端