交易系统平台工程师面试宝典 - 第4节
第4章 技术基础:数据库与缓存:索引优化、事务隔离级别、Redis高可用与缓存策略
面试问题1
问题:MySQL索引有很多种,你在交易订单场景中如何设计索引?请结合联合索引的最左前缀原则说明优化思路。
考察点:是否理解B+树索引结构;能否根据业务查询模式设计联合索引;是否清楚索引下推、覆盖索引等优化。
回答框架:
1. 确认核心查询 WHERE 字段(如 userId + status + createTime)。
2. 按最左前缀原则建立联合索引 (userId, status, createTime),字段区分度由高到低。
3. 利用覆盖索引避免回表,例如索引包含 orderAmount 即可免除回表。
示例回答:
“订单详情页最常见查询是userId+status+createTime排序。我建立联合索引(userId, status, createTime),遵循最左前缀,userId区分度最高放最左。同时把orderAmount作为覆盖索引列,EXPLAIN显示Using index,避免回表。分库分表后还设计了全局主键索引。”
可能追问:索引下推是如何减少回表次数的?联合索引字段顺序调换后性能会如何?
面试问题2
问题:MySQL的RR(可重复读)和RC(读已提交)隔离级别在交易系统中如何选择?MVCC如何解决幻读?
考察点:是否理解隔离级别差异;MVCC实现机制;能否根据业务冲突场景权衡。
回答框架:
1. 区分RR与RC:RR可重复读,RC读已提交。
2. MVCC通过ReadView + undo log实现非锁定读;RR的ReadView在事务第一个快照读时生成,RC每次生成新ReadView。
3. RR利用Next-Key Lock防止幻读,RC只加Record Lock。
示例回答:
“交易中库存扣减等写密集场景,我选择RC避免间隙锁导致死锁,配合乐观锁或行锁保证并发。查询报表用RR保证重复读一致性。MVCC在RC下每次快照读都生成新ReadView,能看到已提交更新;RR则在事务开始就固定ReadView,再配合Next-Key Lock防止幻读。”
可能追问:什么情况下RR也会出现幻读?间隙锁对性能有什么影响?
面试问题3
问题:项目中使用Redis作为缓存层,如何保证高可用?遇到缓存穿透/击穿/雪崩时如何解决?
考察点:Redis高可用方案(主从、哨兵、集群)的选型;缓存失效场景的防护策略。
回答框架:
1. 高可用:常用主从 + Sentinel(自动故障转移)或Redis Cluster(分布式、自带高可用)。
2. 缓存穿透:布隆过滤器判断key是否存在 + 缓存空值(短过期)。
3. 缓存击穿:热点key永不过期 + 互斥锁(SETNX)。
4. 缓存雪崩:过期时间加随机值 + 本地缓存兜底 + 限流降级。
示例回答:
“订单量高场景使用Redis Cluster 6分片3副本,自动故障转移。穿透:布隆过滤器拦截非法key,同时缓存空值5秒。击穿:热点商品key不设过期时间,后台异步更新,配合互斥锁防止并发重建。雪崩:基础key过期时间加3~5分钟随机偏移,读不到时加载本地缓存并异步刷新Redis。”
可能追问:布隆过滤器如何移除元素?如何解决缓存与数据库一致性问题?
项目案例:订单中心缓存与索引优化实战
| 项目名称 | 交易订单系统缓存与索引改造 | ||
|---|---|---|---|
| 业务背景 | 订单中心日均千万笔订单,查询维表(订单列表、详情页)QPS高峰5万+,数据库成为瓶颈。 | ||
| 规模指标 | 日订单量 1200万+,峰值 QPS 8万,DB 慢查询占20%,TP99 200ms。 | ||
| 技术栈 | MySQL 8.0 + Redis Cluster 6.0 + Sentinel + MyBatis + ShardingSphere | ||
| 核心难点 | 1. 联合索引字段顺序难以覆盖所有查询条件;2. 热点商品缓存击穿导致DB雪崩;3. 高并发下死锁频发(RR级别间隙锁)。 | ||
| 面试高频追问 |
· 为什么选择RC隔离级别?死锁如何排查?(用 SHOW ENGINE INNODB STATUS 分析) · 布隆过滤器误判率如何控制?(设置合适的bit大小和hash函数) · Redis主从同步延迟导致缓存不一致怎么办?(读取从库前判断主从延迟,延迟大于阈值走DB) |
||
| 可迁移话术 | “面对高并发查询,我首先分析业务查询模式,建立联合索引并用覆盖索引减少回表。对于热点数据,采用‘缓存+布隆过滤器+本地缓存’多层防护,将缓存击穿影响降到最低。同时,将DB隔离级别改为RC并结合乐观锁,死锁率降低90%。这套思路在后续的促销活动订单查询中也成功复用。” | ||