Skip to content

【面试题】1. 事务的 ACID 四大特性分别指什么?实际开发中哪个最容易被人误解? ​

【标准答案】ACID 是原子性、一致性、隔离性、持久性。原子性靠 undo log 回滚,要么全做要么全不做;一致性是事务前后数据都满足约束,但这是业务目标,不是数据库单方面负责的;隔离性通过 MVCC 和锁来保证并发互不干扰;持久性靠 redo log 确保提交不丢。最容易误解的是一致性——有人觉得数据库会帮你兜底,实际上代码写错了数据库照样"一致地"执行错误逻辑。一致性需要业务和数据库共同守护。

【面试题】2. 为什么 InnoDB 选 B+Tree 而不是 Hash 或二叉树?"+"到底加了什么? ​

【标准答案】三个理由:第一,B+Tree 是多路平衡树,矮胖结构,树高 2-4 层就能存千万级数据,磁盘 IO 极少;第二,数据全在叶子节点且叶子间用双向链表串联,范围查询顺着链表扫,不像 Hash 只能等值查;第三,数据按 key 有序,天然支持排序和 group by。B+Tree 的"+"指真实数据只存叶子层,非叶子节点纯放索引 key,一个节点塞更多 key,树更矮。二叉树在磁盘场景深度灾难,B+Tree 就是最优解。

【面试题】3. 看 EXPLAIN 结果时最关注哪几个字段?type=ALL 一定有问题吗? ​

【标准答案】关注四个:type(访问方式)、key(实际用到的索引)、rows(预估扫描行数)、Extra(额外信息)。type 从好到差:system > const > eq_ref > ref > range > index > ALL。type=ALL 不一定是坏事——表只有几百行时全表扫描比走索引回表还快,或者确实要读大部分数据,优化器不傻。要警惕的是 rows 很大但 type 还差,说明索引没走对。Extra 里出现 Using filesort、Using temporary 要重点关注,这两个对性能影响大。

【面试题】4. 线上突然出现大量慢查询,你的排查思路是? ​

【标准答案】第一步,show full processlist 看正在执行的慢 SQL,判断是突然卡住还是持续慢。第二步,开慢查询日志或查 performance_schema,定位具体 SQL 和执行频率。第三步,explain 看执行计划,关注是否走了预期索引、扫描行数是否异常。第四步,查常见原因:索引失效(统计信息过期、隐式类型转换)、锁等待(看 innodb_trx)、buffer pool 命中率骤降。第五步,紧急情况先 kill 慢查询止血,然后改 SQL 或加索引。工具上 pt-query-digest 分析慢日志、show profile 看耗时分布都很实用。

【面试题】5. 间隙锁(Gap Lock)是什么?什么场景下容易引发死锁? ​

【标准答案】间隙锁锁的是索引记录之间的空隙,不是具体行。它存在的根本原因是在 RR 隔离级别下防止幻读——如果不锁间隙,同一个事务里两次范围查询之间可能被别的事务插入新行。但间隙锁也容易引发死锁,典型场景是两个事务都对同一区间做范围操作但加锁顺序不同。比如事务 A 先锁 id<10 再想锁 id=15,事务 B 先锁 id=15 再想锁 id<10,就死锁了。解决办法:尽量缩小锁范围,统一加锁顺序,或者业务能接受的话降到 RC 级别,RC 不需要间隙锁。

【面试题】6. Redis 做缓存,穿透、击穿、雪崩分别怎么解决? ​

【标准答案】穿透:查不存在的 key,请求直接打到 DB。解决:缓存空值设短过期时间,或者用布隆过滤器拦掉不存在的 key。击穿:热点 key 过期瞬间大量请求到 DB。解决:互斥锁只让一个线程重建缓存,其他等待;或者用逻辑过期,发现过期后异步更新,旧值继续用。雪崩:大量 key 同时过期。解决:过期时间加随机值打散、多级缓存兜底、限流熔断。三个问题本质都是缓存和 DB 之间的保护层没做好,核心思路就一条——能不查 DB 就不查,必须查就少查。

【面试题】7. Spark 的 RDD 宽依赖和窄依赖有什么区别?为什么影响性能? ​

【标准答案】窄依赖是父 RDD 的每个分区最多被一个子分区用,比如 map、filter,不需要 shuffle,可以 pipeline 执行,高效。宽依赖是父分区被多个子分区消费,比如 groupByKey、join,必然触发 shuffle——数据跨节点重分布,涉及磁盘 IO 和网络传输,是 Spark 作业的主要瓶颈。面试官想听的就是你能区分二者,做调优时尽量用 reduceByKey 代替 groupByKey,因为它在 map 端先做本地聚合,大幅减少 shuffle 数据量,本质就是把宽依赖的开销尽可能压低。

【面试题】8. 分库分表后,跨分片的 ORDER BY 分页查询怎么做? ​

【标准答案】这是个经典难题。最简单的"全局排序再截取":每个分片各取前 N 页数据,汇总到内存排序——页数越深越慢,第 100 页可能要从所有分片拉前 100×pageSize 条数据。改进方案:禁用跳页,改用游标分页,记录上一页最后一条的排序字段值,下一页 where field > lastValue limit pageSize,每个分片只取 pageSize 条,复杂度大幅降低。ShardingSphere 支持这种流式归并。终极方案:换 ES 或 ClickHouse 做查询层,数据库只管写入,搜索分析走专门引擎,这是生产上最靠谱的架构。

【标准答案】Exactly-Once 靠两个机制:Checkpoint 做状态快照保证 Flink 内部精确一次;端到端依赖 Source 可重放(Kafka offset 回退)和 Sink 两阶段提交。以 Kafka→Flink→Kafka 为例:checkpoint 时先写事务数据但不提交,checkpoint 完成后统一提交。任何环节挂了就从上次成功 checkpoint 重放,consumer 回退 offset,producer 未提交事务回滚,最终不重不丢。Checkpoint 是自动容错用的,Savepoint 是手动触发的带业务语义的状态备份,用于版本升级、代码改动后的状态迁移。记法:Checkpoint 保命,Savepoint 运维。

【面试题】10. 强一致性、最终一致性、强最终一致性分别适用什么场景?你怎么选? ​

【标准答案】强一致性(CP):写完后立刻能读到最新数据,适合金融交易、库存扣减这种错了要赔钱的场景,用 Paxos/Raft 或 XA 两阶段提交,代价是牺牲可用性。最终一致性(AP):允许短暂不一致但最终一致,适合点赞数、评价数等可容忍误差的场景,靠异步消息或 CDC 同步。强最终一致性:保证在可预期的短时间内(几百毫秒)达到一致,适合订单状态流转、支付结果通知。我的选型逻辑很简单:涉及钱的一律强一致,成本太高就用 TCC 柔性事务保证最终一致但业务可感知;不涉及核心资产就最终一致,简单可靠。