后端一面高频题:MySQL索引、Redis缓存与分布式锁实战
发布时间:2026/8/30 21:12:36 作者:尧图编辑部 阅读量:1,286

周一上午十点端点的面试官准时进了会议。一开始就是常规的自我介绍然后是项目深挖、基础知识、场景设计、手撕算法一环扣一环节奏比我想象中紧凑。整场下来两个小时出头整体感受是这家公司的一面没有太多偏题怪题几乎全是后端岗位的高频基础题但他追问的深度和方式确实能筛掉一批只背答案不理解的候选人。这篇面经不打算写成流水账。比起“问了什么”我更想复盘“为什么这么问”以及“怎么答才不虚”。我会把那场面试里印象最深的题目、我当时的大致思路、事后反思出来的更好答法都整理出来给接下来要面类似企业数字化、中台业务方向后端岗位的朋友一个参考。1. 一面到底在筛什么考察逻辑和方向判断先说结论一面考察的不是你有多厉害而是你的技术底子扎不扎实、能不能正常沟通、遇到不确定的问题时有没有自己的排查思路。端点科技的业务方向偏企业数字化转型、数字供应链、电商中台这类场景技术栈以Java为主Spring Cloud微服务体系、Mysql、Redis、MQ这些基本绕不开。所以一面问的东西大概率会围绕这些技术展开但不会一上来就追问特别深的源码级细节更多是看你在日常开发中是否真的用过、是否理解背后的原理。我当时把一面定位成“基础广度面试”准备重心也就放在了这几个方向Java基础与JVM内存区域、垃圾回收、并发工具类Mysql索引结构、事务隔离级别、SQL执行过程Redis缓存穿透/击穿/雪崩、分布式锁、数据一致性微服务与分布式服务治理、分布式事务、接口幂等算法链表、数组、字符串、二分、动态规划入门面试前我做了个简单判断企业数字化项目往往要处理大量业务单据、审批流、库存、订单这类数据一致性敏感的场景所以并发控制和分布式事务大概率会成为考查重点。事后看这个判断是准确的面试官确实花了不少时间在“库存扣减如何防超卖”这种场景题上。另外一个容易被忽略的点是一面其实也在考察你的沟通方式。遇到不会的问题直接说“不知道”和“我隐约记得是XXX但我不确定我的理解是……”给人的感觉完全不同。后者至少能展示逻辑推导能力面试官也更愿意引导你。这个我后面单独说。2. 面试前的着力点简历项目和八股文怎么分配精力很多人在面试前特别纠结到底是花时间刷题还是背八股我的做法是分阶段。距离面试还有一周以上的时候以刷题和补原理为主到只剩两三天重点则放在梳理简历项目和模拟问答上。因为一面虽然是基础面但面试官一定会拿着你的简历问项目项目回答得好不好直接决定了他对你的第一印象。2.1 项目深挖面试官想听的不是功能是决策我在简历上写了一个库存管理系统的项目原本以为一面不会问太多项目细节结果面试官在自我介绍之后第一个问题就是“介绍一下你在这个项目里主要负责的模块以及你在技术选型上做过什么决策”。这个问题背后的潜台词是他想知道你是在“执行需求”还是真的“参与设计”。单纯说“我用了Redis缓存商品信息”是不够的得能说清楚为什么要用缓存不用行不行用了之后怎么保证一致性万一缓存和数据库不一致业务上能不能接受能接受多久我当时把项目里“订单创建时的库存扣减”这段逻辑完整讲了一遍包括库存表结构、预占库存和实际扣减的区分、怎么用乐观锁防止超卖、超时未支付的订单怎么释放库存。面试官顺着这个点追问了好几个问题后面我单独展开写。所以准备项目的时候不要只背自己做了什么要模拟面试官从你每句话里抠细节。最好的方式是画一条线项目背景 - 你负责的模块 - 遇到的最大难点 - 你是怎么解决的 - 为什么这么解决 - 有没有更好的方案。这条线理清楚项目关基本就稳了。2.2 八股文的优先级排序面经里经常出现“八股文”这个词但你要是真把八股文当成死记硬背的题库面试官几追问就会露馅。我的经验是每个高频考点至少要能回答三层——是什么、原理是什么、实际业务中怎么用。拿Mysql索引举个例子只答“索引是B树结构能加速查询”显然不够。面试官大概率会接着问为什么用B树不用红黑树什么情况下索引会失效联合索引的最左前缀原则是什么这些问题背后其实都是同一个问题你有没有真正理解索引的数据结构和优化器的行为。我复习的时候给自己定了个清单每个主题都用“三层法”过一遍JVM内存区域划分、堆内存结构、GC算法演进、类加载过程、OOM排查思路并发Synchronized锁升级、Volatile可见性、AQS、线程池核心参数、CASMysql索引结构、事务隔离级别、MVCC、锁、redo/undo log、explainRedis数据结构、持久化RDB/AOF、过期策略、内存淘汰、分布式锁、缓存一致性SpringBean生命周期、循环依赖、事务失效场景这个清单看着多但一面真正会深入追问的范围没那么大通常集中在两三个主题里。我这次被重点问到的是Mysql和RedisJVM和并发反而问得比较浅。3. 完整还原一面流程里每一步的感受和应对这一节我把整场面试的时间线走一遍。每个环节我都会写清楚面试官问了什么、我当时怎么想的、回头看哪些地方答得还可以、哪些地方可以更好。3.1 自我介绍别把简历复读一遍自我介绍我准备了大概两分半钟。结构是“我是谁 - 技术栈 - 最有代表性的项目 - 为什么投这个岗位”。很多人容易犯的错是把自己的项目经历从第一家公司到第三家公司全部讲一遍面试官手上有简历这些他能看到你要做的是把最有亮点、最匹配岗位的内容提炼出来。我当时是这么说的先一句话说明自己的技术栈和年限然后重点讲库存管理项目里我独立负责的模块包括库存预占、分布式锁、对账补偿这三个点最后说希望深入了解企业数字化方向。这样既展示了技术能力也暗示了自己和岗位方向的匹配度。面试官听完之后的第一个追问就是项目细节所以自我介绍里提到的每个点都必须是你能扛住深挖的内容。宁可少说三个点也不要说一个自己没有深入想过的点。3.2 项目追问库存扣减这个场景的超卖坑面试官对库存模块的追问是整场面试里时间最长、最有价值的环节。他就着一个问题反复深挖用户同时下单库存只有十件十一人买怎么保证不超卖?我给的方案分了三层第一层是数据库乐观锁。更新库存时带上版本号update ... set stock stock - 1, version version 1 where id ? and version ?如果影响行数为零说明版本冲突下单失败或重试。这个方案的优点是简单可靠缺点是并发高时经常有请求失败体验不好。第二层是Redis预扣减。先把库存加载到Redis用 decr 命令扣减只有扣减成功并且剩余库存不小于零才继续走数据库落单。这样做的好处是Redis单线程原子操作不会有超卖扛得住高并发。但引入了新的问题缓存和数据库的一致性。面试官马上追问Redis扣减成功了、数据库订单没生成成功怎么办我答的是把Redis扣减视为预占订单状态是待支付如果后续订单创建失败则调用补偿逻辑把Redis里的库存加回去。同时通过定时任务对比Redis剩余库存和数据库实际销量发现不一致就先恢复Redis库存再对账处理异常订单。这个回答面试官看起来比较满意因为他顺着这个思路又问了缓存一致性问题而不是直接打断换下一题。第三个点他问的是你们当时为什么不用Redis分布式锁把整个下单流程锁住我说锁粒度太大这个方案能保证不超卖但把所有请求串行化了吞吐量上不去作为兜底方案可以作为主方案不合适。其实这也是一个很常见的思路对比题他想看的是你能不能在不同场景下做出合理的取舍。3.3 基础题阶段Mysql事务隔离级别和MVCC项目聊完面试官切到了基础题阶段。问的第一个问题是“Mysql默认的隔离级别是什么RR级别下MVCC是怎么解决不可重复读的”说实话这个问题本身不难但如果只背“默认是REPEATABLE_READ通过MVCC解决不可重复读”很容易被追问到卡壳。我当时的回答思路是这样的先说明Mysql InnoDB引擎默认隔离级别是可重复读RR然后解释MVCC靠三件套工作隐藏字段DB_TRX_ID、DB_ROLL_PTR、undo log版本链、read view。普通查询是快照读事务第一次执行select时生成read view之后一直用这个read view判断行的可见性所以同一个事务里多次查询看到的结果一致也就解决了不可重复读。他说“那当前读呢”我说当前读走的是加锁读比如select ... for update、update、delete最新版本才能读所以不存在MVCC的可见性判断问题靠的是记录锁、间隙锁和Next-Key Lock解决幻读。RR级别下InnoDB的间隙锁机制已经能解决大部分幻读场景。回头想这个环节我做得比较好的是没有停留在“是什么”而是主动把原理链条讲出来了。面试官追问“当前读”时我最后还补了一句“这也是为什么RR级别下用for update能锁住范围查询的间隙”算是把当前读和锁机制串起来了。3.4 场景扩展缓存穿透、击穿、雪崩接下来面试官问Redis。他在白板上写了个场景一个热点商品详情接口QPS很高Redis缓存失效之后瞬间大量请求打进数据库怎么办。这是很经典的缓存击穿问题。我的回答是分三步第一步热点数据不过期后台异步更新第二步加互斥锁同一时间只允许一个请求去查数据库并回填缓存其他请求等待或快速返回旧值第三步设置逻辑过期时间在value里存一个过期时间戳读的时候发现过期就异步刷新。然后他追问如果是恶意请求一直查一个不存在的key呢这就是缓存穿透了。我当时说了三个手段接口层做参数校验、缓存空值并设置短过期时间、布隆过滤器做前置拦截。布隆过滤器我用了一句通俗的话解释“它跟你说这个key一定不存在那肯定不存在说可能存在可能真的存在也可能误判。”这个回答带上了一点对原理的理解面试官应该能感觉到你是真的用过。这场基础题阶段我没有被问到底层源码级别的东西但每个点都往“业务场景”里切了一下。事后复盘我觉得这正是面经里常说的“一面考广度”他需要用这些高频问题快速判断这个候选人有没有基本的后端素养。4. 几个高频题目的答题思路拆解这一节我把这次面试里几个值得展开的题目拆得更细。题目本身不算难但每一道背后都有考察点和更好的回答策略。4.1 索引失效问题为什么最左前缀原则这么重要面试官问的是“我在联合索引 (a, b, c) 上查询 where b 1 and a 2这个索引会不会失效为什么”这题看似简单但很能看出一个人对索引结构的理解。我当时的回答是这样拆的第一这个查询不会失效因为Mysql优化器有查询重写的能力会调整where条件的顺序把a2放到前面所以依然能走到联合索引。看到面试官点头我又补了一句但如果你写的是 where b 1a没有等值条件那只能用上索引的b吗也没有因为联合索引是先按a排序再按b排序的缺少a的情况下b的全局有序性是不成立的。第二真正让索引失效的场景是对索引列做函数运算或隐式类型转换比如 where a 1 2 或 where mobile 13800138000mobile是varchar后者Mysql会隐式把字符串转成数字导致索引失效。还有前导模糊 like %xxx因为B树无法从中间开始匹配有序链表。最后我总结了一句判断索引有没有效核心就看优化器能不能利用B树的有序性来做精确或范围定位。任何破坏有序性的写法都会让优化器放弃索引。这个结论不是背出来的是理解了B树的查找过程自然推出来的所以能扛得住追问。4.2 分布式锁Redis锁和Zookeeper锁怎么选面试官的问题比较直接“你们项目里的分布式锁用的Redis为什么不用Zookeeper如果Redis主节点挂了锁怎么办”这个问题很实在。我当时说选Redis主要是因为它性能高、实现简单配合Redisson的看门狗机制能解决业务没执行完锁就过期的问题Zookeeper的锁更可靠因为临时顺序节点天然支持客户端宕机自动释放但性能上比Redis差几个数量级适合对一致性要求极高、并发量不高的场景。然后他强调了一个我差点忽略的点“Redis锁真的安全吗”我顺着说如果主节点在加锁后、还没同步到从节点时就宕机从节点升级为主节点后锁就丢了。这就是经典的Redis分布式锁缺陷。解决方案是Redlock但 Redlock本身也有争议更稳妥的做法是在极端重要的场景里直接用Zookeeper或数据库唯一约束。这一问一答其实就是面试官在考察你对分布式系统“CP还是AP”的取舍是否清楚。4.3 线程池参数设置为什么CPU密集和IO密集不一样线程池这个东西简历里写了面试官一定会问。他问的是“一个接口做的是文件解析然后写入数据库这个场景的线程池核心线程数和最大线程数怎么设置为什么”我的回答是这个接口本质是IO密集因为文件解析可能涉及磁盘读写写入数据库更是网络IO。通常IO密集型的线程数设置为CPU核心数的两倍左右核心目的是让CPU在等待IO的时间里还能处理其他任务。如果是CPU密集型任务线程数设置为CPU核数1就够了因为线程再多CPU也未必能分到执行时间反而增加上下文切换开销。这里有个容易说错的点很多面试者会把“线程数越多越好”当成结论。面试官就等着这句话他追问了一句“线程数是不是越大吞吐量越高”。我用了个比喻解释“就像一个奶茶店只有两个员工你硬安排十个收银员大家挤在一起反而谁都干不好。”在线上线程数过高时会出现频繁的上下文切换和锁竞争响应时间反而变长。4.4 算法手撕链表题和边界条件一面算法题通常不会很难但非常看重边界条件。我这次遇到的是一道链表题删除链表的倒数第N个节点。这题本身很常规双指针一遍遍历就能做。我先和面试官确认了入参和边界链表可能为空、N可能等于链表长度、N是正整数。然后讲了思路快指针先走N步慢指针再从头出发快指针走到尾时慢指针正好在倒数第N1个节点。但我犯了一个几乎所有第一次写这题的人都会犯的错没有处理“删除头节点”的情况。如果链表的长度恰好是N快指针走到末尾时正好指向null此时慢指针还在头节点直接删会出问题。我当时的处理是加了一个dummy虚拟头节点让慢指针从dummy出发这样删除倒数第N个节点时slow.next slow.next.next 就能统一处理。面试官其实是看着我在编辑器里先写了第一版然后又补了判断逻辑最后才改成dummy节点方案。他没有打断我等写完才问了一句“你第一次写的时候用了if判断后面为什么改统一方式”我说if判断能过但代码不够简洁而且边界条件多了之后容易漏dummy节点方案让代码在所有情况下逻辑一致更不容易出问题。这个回答我觉得比默写一遍答案加分。5. 复盘比答题重要我卡壳过的瞬间和知识盲区面试完别急着松气趁记忆还热赶紧复盘。这次面试里我倒是有两个瞬间明显感觉到了卡顿这里也把反思写出来。5.1 被问到“Redis内存满了怎么办”时的停顿面试官问“如果Redis的内存被打满了你会怎么处理”我当时第一反应是“设置maxmemory”但紧接着他追问“设置策略之后会发生什么”我愣了一下才想起是淘汰策略。我报了几个volatile-lru、allkeys-lru、volatile-ttl、noeviction。但在说 noeviction 的时候我嘴瓢说成了“不淘汰直接报错”其实应该是“不再执行写命令返回错误”。这个细节让我后来反思了很久。原因在于我背的是策略名字但没有去理解每个策略在真实场景下触发时的表现。事后我自己推了一遍如果你的Redis存的是热点商品、令牌、验证码用allkeys-lru合适如果有些key你明确希望不淘汰那用volatile-lru如果完全不能接受淘汰任何key那就noeviction但这时候需要监控和告警机制否则业务会报错。背答案最大的风险就是这个——你记得住术语但记不住术语背后的行为。面试官追问得稍微深一点卡壳是必然的。5.2 项目里“对账”细节被深挖时的暴露我在项目里提到“定时任务做对账”面试官顺口问“对账的周期是多少如果对账本身也失败了怎么办你怎么确认对账任务自己执行成功”我当时的回答是对账任务每五分钟跑一次如果自己失败了会通过日志告警发现。这个回答其实不算错但明显不够严谨。他其实想听的是“对账任务本身要具备幂等性和闭环确认”比如任务执行状态落到数据库、失败重试、人工介入补偿。复盘时我意识到这类问题的本质是面试官想判断你有没有理解“分布式系统里一切都需要最终一致和闭环反馈”。项目里如果写了“对账”这个词就一定要准备清楚对账的具体逻辑是什么、对账失败怎么发现、对账成功的标准是什么。这些细节比“我用了定时任务”更能体现你的经验。6. 从这场一面里提炼的通用准备清单面完端点这场一面我最大的感受是这家公司一面不追求难题怪题但对基础知识的底层理解要求比较高同时对项目经验的真实性和思考深度有持续的追问。把这次经历提炼成清单的话大概是下面这些内容不管面哪家后端岗位我觉得都适用。6.1 高频考点自查清单Mysql索引为什么是B树、最左前缀原则、覆盖索引、索引失效场景事务与锁隔离级别、MVCC、当前读和快照读、死锁排查Redis线程模型、持久化、过期删除、淘汰策略、缓存击穿/穿透/雪崩、分布式锁并发Synchronized原理、Volatile、ThreadLocal、线程池参数、CAS和ABAJVM内存区域、对象创建过程、G1和CMS、类加载、OOM排查场景设计防超卖、幂等、分布式事务、接口限流、异步消息补偿每个知识点我都建议用“是什么、底层原理、业务中使用场景”三层结构来准备。不要只背结论要把结论推导出来。6.2 面试中稳住节奏的三个技巧第一个技巧是遇到不会的问题先说“这块我了解得不够深但我目前的理解是……”然后尽量把相关的知识讲出来。面试官通常愿意听的并不是标准答案而是你的思考方式。第二个技巧是回答项目问题时多用“当时为什么这么选”的表述少用“领导让我这样做”。这能让你从执行者变成设计者。第三个技巧是算法题写完之后自己主动说一遍时间复杂度和空间复杂度并检查边界条件。这能向面试官传递“我写代码是有完整思考的”这个信息。6.3 后续面试的方向调整一面过了之后如果有二面通常会考察系统设计、架构思维和团队协作能力。准备重心要从“点”变成“面”。比如一个下单系统从客户端到数据库全链路怎么设计服务之间怎么做服务发现和配置管理线上突然CPU飙升怎么排查——这些都是比一面更顶层的思路题。根据我整理的面经端点科技这类偏企业数字化服务的公司技术面和业务侧结合得比较紧密。所以如果有机会进入下一轮我会把数字供应链常见的几个场景订单履约、库存协同、结算对账都过一遍从业务角度想清楚技术方案的价值而不只是停留在技术实现本身。面经这东西最大的价值不是让你押中题目而是帮你校准复习方向。我写这篇复盘的时候其实又把自己卡壳的两个点重新过了一遍这才发现很多当时觉得“我好像会”的东西落到嘴上根本讲不透。所以也建议正在准备面试的你面完一家不管结果如何立刻把能回忆起的题目和你的回答写下来然后对着镜子重新讲一遍。讲不顺的地方就是你下一次面试前最该补的地方。