资讯动态

MySQL面试真题精讲:索引优化、事务锁与SQL实战

发布时间:2026/10/13 14:07:22 来源:尧图企业网站定制
最近在帮几个朋友突击大厂OD技术岗的面试发现数据库这块基本是必考项尤其MySQL。很多人项目经验不少真被问到底层原理和实战场景时就容易发虚。这篇就结合我手头整理的第二批真题把MySQL相关的考点掰开揉碎讲一遍既有题目拆解也有我平时排查问题总结出来的思路。内容主要面向准备大中厂OD或外包转正面试的开发者后端、测试、运维方向都可以参考已经工作几年但没系统整理过MySQL知识的人也能查漏补缺。1. 面试流程与考察重点概览1.1 OD岗MySQL考察的目的和范围OD岗的面试逻辑和校招不太一样。校招更看重基础是否扎实而OD面试官通常默认你有一定的项目经验所以MySQL的题目不会只问概念而是会结合你简历里写的业务场景去深挖。比如你说做过订单系统那索引设计、事务隔离级别、锁竞争就是高频问题你说维护过报表系统那慢SQL优化、分页查询性能就逃不掉。从我了解到的真题来看MySQL考察范围基本集中在四块索引原理与优化、事务与锁机制、SQL执行计划和慢查询排查、日志与主从复制。看起来知识点不多但每个都可以无限往下追问直到你答不上来为止。第二篇真题主要以索引、事务、SQL优化为主会穿插一些日志和主从复制的高频问法。面试官出题还有个特点喜欢让你“手写一个场景然后分析”。比如给你一张订单表要求统计某个时间段内每个用户的消费金额让你写SQL并解释索引怎么建。这种题表面考SQL编写实际上考的是你对group by、range查询、覆盖索引和执行计划的理解是否成体系。1.2 从真题看面试官的出题逻辑我复盘过几轮面试记录发现面试官常见的套路是“概念—场景—延伸—反推”。先问一个基础概念比如“什么是回表”等你能答上来立刻切换到“如果这张表数据量上亿怎么避免回表”再往上深入就是“如果索引也优化不了你会怎么设计分库分表”这个过程考察的不只是知识记忆更考验你在高并发真实业务里的权衡能力。另一类常见套路是“给一段执行计划让你挑毛病”。比如在联合索引(a,b,c)下分别给出几条SQL让你判断哪些会走索引哪些不会。这类题其实就是在考察最左前缀匹配但它换了一个工程化的外壳很多人一紧张就容易答乱。所以准备OD面试不能死记硬背。每复习一个知识点最好都问自己三个问题这个机制解决什么问题设计它的代价是什么换一个场景我还敢不敢这么用带着这种思路去应对即便遇到没见过的题目也能顺着逻辑推个八九不离十。2. 核心真题拆解索引优化2.1 最左前缀原则到底在考什么真题里见过一道很典型的题目。假设有一张用户表user_info建立了联合索引(idx_user_status, create_time)其中user_status是用户状态create_time是注册时间。面试官给出下面三条SQLSELECT * FROM user_info WHERE user_status 1 AND create_time 2024-01-01; SELECT * FROM user_info WHERE create_time 2024-01-01 AND user_status 1; SELECT * FROM user_info WHERE create_time 2024-01-01;题目问这三条SQL哪些能用到联合索引。很多人第一眼觉得第一条能用到第二条不行因为条件顺序变了。但MySQL的优化器会做条件重排所以第一条和第二条其实效果一样都能用到idx_user_status这个前缀。第三条虽然也有create_time但跳过了前面的user_status所以无法命中联合索引只能走全表扫描。这里想提醒一下所谓“最左前缀原则”并不是指SQL里条件写的顺序而是索引定义时的字段顺序。优化器会自动调整条件顺序我们真正要关心的的是索引列在WHERE子句中是否以连续、不中断的方式出现。如果你背了“必须左边第一个字段”却没理解本质很容易在这类题上翻车。面试追问还有一个高频点联合索引(a,b,c)WHERE a1 AND c2这个能走索引吗答案是能用到a这个前缀但c因为中间断了b只能作为普通过滤条件无法完全走索引下推。如果数据量特别大可能还要考虑把索引调整为(a,c,b)或者(c,a,b)。这种场景就需要你结合数据分布去讲而不是单纯答能或不能。2.2 回表与覆盖索引的经典场景回表是MySQL面试里出镜率非常高的概念。所谓回表就是通过二级索引找到主键id再拿着主键id去主键索引树里查完整行数据。面试官很喜欢举一个例子表user_info有主键id、name、age、phone几个字段在name上建了普通索引然后执行SELECT * FROM user_info WHERE name 张三;如果name索引命中会先查到主键id再每行都回表一次把完整的user_info数据拿到。当匹配行数很多时回表次数就多性能自然会差。而如果改成SELECT name, age FROM user_info WHERE name 张三;假设在(name, age)上建立了联合索引那么这次查询要的数据都包含在索引树里就不需要回表这就是覆盖索引。回答覆盖索引时不要只背概念最好能讲一个真实优化案例。比如我之前处理过一个分页查询慢的问题原SQL是SELECT * FROM orders WHERE status0 ORDER BY create_time LIMIT 10000, 20。这个SQL深分页时非常慢因为要回表1万次。我的优化办法是先查出满足条件的id集合再做连接查详情SELECT * FROM orders o INNER JOIN ( SELECT id FROM orders WHERE status 0 ORDER BY create_time LIMIT 10000, 20 ) t ON o.id t.id;这种方案让内层子查询只扫描id索引避免大量回表整体耗时从原来的2.2秒降到了0.06秒。面试时如果能主动讲到这个案例效果要比单纯背书好得多。2.3 索引失效案例与排查方法关于索引失效高频真题会问你“哪些情况会导致索引失效”常见答案包括对索引列使用函数、隐式类型转换、like以%开头、or连接非索引条件、not in/not exists等。但需要注意这些结论不是绝对的。比如对索引列使用函数在MySQL 8.0的部分版本里如果函数结果能够计算也可能走索引or连接条件时如果每个条件都走索引优化器也可能合并索引访问。所以回答的时候不要一刀切最好加一句“具体还是要看执行计划”。我现在排查索引失效习惯直接看explain输出中的type和key字段。type从系统到全表扫描的级别大致有system、const、eq_ref、ref、range、index、ALL。如果看到type是ALL那大概率没走索引如果type是index说明扫描了整棵索引树虽然没回表但不代表高效。只看key是否为空其实不够因为优化器可能选择了一个区分度很低的索引导致效率比全表扫描还差。另外有个很容易被忽略的点隐式类型转换。字段是varchar类型查询条件传了一个整数MySQL虽然不会报错但会尝试把字段转成数值这就相当于对索引列做了函数操作索引就失效了。以前线上出过一个案例用户表phone字段是char(11)查询用了WHERE phone 13800001111结果走了全表扫描。后来应用层把查询参数改成字符串问题秒解决。这种细节面试官很喜欢拿来当陷阱建议答完基础失效场景后主动提一句隐式转换会显得你踩过坑。3. 核心真题拆解事务与锁3.1 事务隔离级别与MVCC的关系事务这块MySQL面试题基本绕不开隔离级别和MVCC。默认隔离级别是REPEATABLE READ而不是很多数据库默认的READ COMMITTED。这个差异是高频考点经常有人被问到“MySQL为什么默认用RR”我从实际经验来看一个原因是为了兼容binlog。在主从复制场景下如果使用READ COMMITTED某些事务的执行顺序在binlog里无法准确还原可能导致主从数据不一致而REPEATABLE READ配合间隙锁能在一定程度上保证顺序一致性。MVCC可以说是InnoDB实现高并发读的核心。Undo log里保存了多个版本的数据快照每行还有一个隐藏的版本链事务的ReadView用来判断当前事务能看到哪个版本。面试时建议把ReadView的生成时机讲清楚RC隔离级别下每次普通SELECT都会生成一个新的ReadView所以能看到其他事务已提交的最新数据RR隔离级别下事务内第一次SELECT生成ReadView之后就一直复用所以整个事务内看到的数据是一致的。如果面试官继续往深挖会问你“快照读和当前读有什么区别”。普通SELECT是快照读不走锁而UPDATE、DELETE、INSERT以及SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MODE都是当前读需要加锁。这个区别特别重要因为很多死锁问题的根因就是当前读和快照读混在一起导致你预判不了别人会持有哪个锁。3.2 间隙锁与死锁的实战分析间隙锁通常和RR隔离级别绑定出现。它锁的是一个区间而不是具体的记录。比如一张订单表order_no有唯一索引执行SELECT * FROM orders WHERE order_no 100 FOR UPDATE如果100这条记录不存在InnoDB会锁住(上一个值, 100]这段间隙防止其他事务插入100。这时另一个事务想插入100就会被阻塞。很多人在面试时只想到行锁忽略了间隙锁导致在“为什么不存在的记录也能锁住别人”这种题上卡壳。死锁题是面试里最能拉开差距的部分。我遇到过一道真题两个事务分别做如下操作。事务A先UPDATE id1的行再UPDATE id2的行。 事务B先UPDATE id2的行再UPDATE id1的行。这种就是典型的交叉加锁死锁。但实际面试更常见的是“条件范围加锁导致的死锁”。比如两个事务都执行UPDATE orders SET status1 WHERE status0 AND amount100如果这条SQL扫描到相同的间隙就会相互等待间隙锁。我前公司在处理类似问题时会让DBA统一在事务里对参与的业务主键进行排序保证所有事务都按相同顺序加锁。这个方案简单但对复杂的绑定条件场景不一定适用。更稳妥的办法是减少大事务事务里尽量让行数少必要时降低隔离级别到READ COMMITTED因为读取已提交不会用到间隙锁能明显降低死锁概率。面试官问死锁时除了答原理和应对最好提一下死锁检测机制。InnoDB默认开启了死锁检测通过等待图判断循环等待一旦发现就回滚代价较小的事务。很多生产环境死锁日志里会出现“deadlock found when trying to get lock”这时候不仅要看锁是什么还要结合SHOW ENGINE INNODB STATUS去分析两个事务分别持有和等待了哪些锁。3.3 如何向面试官展示你的锁理解深度锁这块如果只背“共享锁、排他锁、意向锁”三个名词基本拿不到高分。面试官想听的是你能否说清锁和索引的关系、锁粒度怎么选择、锁如何影响并发度。我建议回答时用一条索引情况来展开。比如你说InnoDB的行锁是针对索引记录的锁如果查询没有走索引那锁就会退化为表锁或锁多条记录因为引擎无法定位到唯一目标行。接着可以补充生产环境经常出现“明明只是更新一条数据却把整张表堵死”排查后往往就是UPDATE条件里的列没有索引导致锁范围扩大。另外一个可以加分的方向是意向锁的作用。它虽然不直接锁行但它表明事务准备在表上加某种锁。比如事务A要对表加排他锁如果某行已经有共享锁它必须等待而意向锁可以让MySQL快速判断是否有冲突不用逐行检查。这样就把表锁和行锁之间的协同机制带出来了显得你对引擎内部设计有整体认知。还能主动引出“锁等待超时”的处理。一般设置innodb_lock_wait_timeout默认50秒如果等待锁的时间超过阈值就会报错。线上如果经常锁等待首先要定位是不是有长事务其次看是否有固定的慢SQL把资源占用住了。我处理过一个案例一个统计任务每天晚上跑一次长查询导致这个时间段所有订单更新都变得很慢后来通过调整查询时间窗口和使用读副本才彻底解决。这种实战场合是面试官最想听到的。4. SQL优化与执行计划实战4.1 一条慢SQL的完整优化过程面试里经常给一个场景有一个订单明细表order_detail字段有id、order_no、user_id、product_id、status、create_time数据量约2000万。某天线上出现一条慢SQLSELECT order_no, user_id, status FROM order_detail WHERE create_time BETWEEN 2024-01-01 00:00:00 AND 2024-01-31 23:59:59 AND status 1 ORDER BY create_time DESC LIMIT 20;这条SQL跑一次要12秒。面试官让你优化。很多人一开始就想加索引但其实需要先分析已有的索引和数据分布。如果create_time有索引但查询范围太大走时间范围会扫出几十万条记录再加上status过滤和排序效率并不一定高如果只建(status, create_time)联合索引因为status区分度低也可能优化器不买账。我的优化思路是先缩小范围。如果业务允许可以增加user_id等更常用的筛选条件如果不允许就在原表上改成先查最内层小范围数据再回原表取详情像前面说的深分页优化一样。还可以考虑按时间分表把按月数据拆开因为这类明细表通常是按月归档的。面试时建议你分步骤讲先看执行计划再根据结果调整索引或SQL形态最后验证效果而不是一上来就给结论。如果面试官要求“不能改SQL、只能用索引优化”那就要思考覆盖索引。比如这条SQL只查order_no、user_id、status三个字段那可以建立一个包含create_time、status、order_no、user_id的复合索引让查询直接在索引树上完成避免回表。这里需要注意排序字段create_time如果已经在索引里可以避免filesort要重点强调。4.2 explain执行计划关键字段解读讲SQL优化就绕不开explain。很多面试者能背出type字段的几种级别但不会结合实际执行计划解读。我习惯先用一个例子演示。EXPLAIN SELECT * FROM users WHERE name 张三 AND age 18;假设users上建了(name, age)联合索引。explain输出里的key会显示用了联合索引rows会显示预估扫描行数extra可能显示Using index condition。如果我把name条件换成LIKE %张%type变成ALLkey为空extra往往显示Using where。这就能非常直观地看到索引失效对执行计划的影响。Extra字段里几个高频值也需要弄明白Using index说明使用了覆盖索引不回表。Using index condition说明用到了索引下推先对索引字段做初步过滤再回表。Using where表示存储引擎返回数据后MySQL服务层做了再过滤。Using filesort说明无法利用索引排序需要额外排序数据量大时是性能黑洞。Using temporary说明查询中使用了临时表比如group by或去重操作不当。面试时如果能准确解读extra字段再结合一个实例说明“我通过查看extra发现filesort然后调整索引排序字段后消除”那基本就是面试官想要的深度。4.3 优化器选错索引的应对选错索引的经典问题就是一个有100万行的表某列区分度很高但优化器偏偏走了另一列的低区分度索引导致SQL变慢。面试官会问你怎么办。首先要知道优化器选索引的参考因素扫描行数、回表成本、排序成本、是否覆盖索引。它并不是每次都能算出最优解尤其在数据分布不均、统计信息不准时容易偏差。常规解决办法有几种。第一种是使用FORCE INDEX强制指定索引SELECT * FROM orders FORCE INDEX(idx_user_id) WHERE user_id 123 AND status 1;这种方式对SQL的侵入性较强而且如果索引名被修改SQL就会报错需要谨慎。第二种是更新统计信息执行ANALYZE TABLE orders让优化器重新评估。第三种是改写SQL让优化器更偏向你想要的选择比如加一个直方图让统计信息更准确。另外MySQL 8.0新增了优化器提示比如USE INDEX、IGNORE INDEX也可以用来引导优化器。但我在生产环境里一般先分析为什么优化器会误判比如是不是因为某个字段的值分布很不均匀有大量用户只有一条订单少数用户有几十万条。这种情况下强制索引未必是好事反而是给数据模型加一个分区键更合理。5. 高频考点日志、主从复制与高可用5.1 binlog/redolog/undolog的作用与区别面试官问到日志时经常会让区分binlog、redo log、undo log。这题我见很多人答错原因在于他们把“恢复”和“复制”混在一起。简单来说redo log是InnoDB存储引擎的日志主要保证崩溃恢复能力。当一条记录更新时InnoDB会先把数据页的改动记录到redo log buffer然后在适当时候刷到磁盘上的redo log文件。如果数据库突然宕机系统重启后会根据redo log把已经提交但还没刷进数据页的修改重新应用这样才能保证已提交事务不丢失。这种机制叫WAL即先写日志再写磁盘。binlog是MySQL Server层的日志主要服务于主从复制和数据归档。它记录的是逻辑SQL或行变更信息所有存储引擎共用。主库把binlog传给从库从库执行这些日志内容从而保持数据一致。redo log和binlog还有个区别redo log是物理逻辑日志记录“在哪个数据页上做了什么修改”而binlog记录的是“执行了什么语句或哪一行怎么变化了”。undo log则用于事务回滚和MVCC多版本控制。它记录的是数据操作的反向内容比如插入对应删除、修改对应旧值。事务回滚时通过undo log撤销操作MVCC读旧版本时也依赖undo log构造历史快照。我在面试时一般会用一个更新语句来串联这三种日志。UPDATE users SET age20 WHERE id1执行时先把id1的旧age值写入undo log然后修改内存中的数据页并写redo log最后在事务提交时写入binlog。一旦中途崩溃可以用redo log保证已提交事务持久化用undo log回滚未提交事务。把这个流程讲出来面试官基本就会认为你真正理解了日志体系。5.2 主从复制的延迟与数据一致性主从复制是线上系统标配所以MySQL面试必问“从库延迟怎么办”。我从运维角度总结的经验是先区分延迟是结构性的还是偶发性的。偶发性延迟常见原因是大事务在主库执行时间长从库只能串行回放导致主备落后或从库本身有慢查询、锁等待卡住了复制线程。结构性延迟高发于单线程复制时期现在MySQL 5.7以后支持并行复制但并行度在某些场景下依然受制于主库事务提交的并行度。面试时建议提到可以通过SHOW SLAVE STATUS查看Seconds_Behind_Master、Relay_Log_File、Relay_Log_Pos判断延迟量和具体复制到哪个位置。关于数据一致性常见方案是主库和从库都用binlog配合半同步复制。半同步复制和异步复制的最大区别在于主库提交事务时需要等待至少一个从库接收并写入中继日志才向客户端返回成功。这样能显著降低事务丢失概率。但半同步也不是万无一失如果网络分区主库等待超时后会退化为异步复制这时依然有数据丢失窗口。我处理过一个比较隐蔽的从库延迟案例业务里有个定时任务每小时在主库批量删除过期token一次删好几万行。这个操作在从库回放时要执行同样的删除占了大量时间。我后面改成分批删除每次几百行交叉执行延迟一下就降下来了。这个案例很简单但很能说明主从延迟未必是架构问题可能是写操作本身不够克制。5.3 半同步复制与MySQL高可用方案如果面试官问到“如何保障数据库高可用”你可以从复制模式和高可用组件两个层面来解答。复制模式包括异步复制、半同步复制、组复制高可用组件常见的是MHA或基于分布式共识的MySQL Group Replication、InnoDB Cluster。面试中追问比较多的点是“半同步复制它到底保证了什么不保证什么”。半同步保证事务在主库提交后binlog至少被一个备节点接收但并没有保证这个事务已经在备库应用完成。所以如果主库在等待期间崩溃备库虽然拿到了binlog但可能还没应用仍然会有少量事务丢失或延迟应用。因此严格来说半同步减少了丢失窗口但没有做到强一致。如果聊到高可用方案我觉得最好结合一个实际演变过程。早期我维护过一套主从加MHA的架构MHA监控主库存活发生故障时自动选新主、补齐缺失的binlog并把虚拟IP切到新主。这个方案成熟但需要额外部署管理节点而且集群本身就容易成为单点。后来逐步迁移到Group Replication通过Paxos协议保证成员间状态一致故障检测和自动选主更自动化处理脑裂方面也更好。不过它对网络稳定性要求更高并不适合所有场景。面试时不用说得太深能理清“异步、半同步、强同步”三个层次的取舍就够了重点是让面试官觉得你有线上经验而不是只会背PPT上的架构图。6. 常见问题与避坑指南6.1 我在面试中被问过的陷阱题这里列几个我实际遇到、容易答错的陷阱题给大家做一个速查。第一个陷阱“一个事务里先SELECT后UPDATE会不会读到其他事务已提交的新数据”很多人说不会因为RR隔离级别下整个事务快照一致。但如果这个SELECT是当前读比如SELECT ... FOR UPDATE它读取的是最新已提交数据并不受ReadView控制。更准确地说普通快照读和当前读是两套逻辑不能混答。第二个陷阱“MySQL索引能提高查询性能是不是建的越多越好”显然不是因为每次写入除了更新数据行还要同时更新所有二级索引。如果表有6个索引插入一条记录的代价会成倍上升。此外优化器要评估的索引路径也会变多反而可能导致选错索引。正确的做法是结合业务查询模式把高频查询覆盖到尽量少的联合索引上。第三个陷阱“为什么某条SQL明明建了索引执行计划却显示不走索引”原因可能有很多比如统计信息过期、查询返回结果集超过全表20%左右优化器判定走全表更划算、函数操作或隐式转换。回答时不要只看索引是否创建要先看执行计划里的type、rows和extra再决定是否强制索引或改写SQL。第四个陷阱“删除表数据后磁盘空间为什么没释放”其实就是delete只是标记删除磁盘空间复用需要后续插入才能覆盖如果需要释放空间得用OPTIMIZE TABLE或重建表。这个属于InnoDB碎片管理问题面试官问出来往往是想考察你理解了数据页的组织方式。6.2 回答MySQL问题时最容易犯的错我复盘过一些模拟面试发现大家最常犯的三个错误是概念之间混淆、只讲结论不讲场景、不说代价。混淆最典型的是把“索引覆盖”和“联合索引”混为一谈。覆盖索引本质是一种查询优化的结果指查询列都包含在索引中联合索引只是多列组成的索引。联合索引不一定能覆盖查询覆盖索引也不一定是联合索引。回答时最好用一条具体SQL做例子区分开。只讲结论不讲场景也很常见。问“为什么分页查询慢”如果只回答“因为offset太大要扫描很多行”这只是结论面试官听着没意思。更好的回答是LIMIT 100000,20本质上要取出前面100020行再丢弃前100000行而且每行都要回表IO代价随offset增大而线性增长。你可以再补一个“用延迟关联把查询推迟到主键索引上”的优化方案这样才完整。第三是忽略代价。比如聊到“覆盖索引很好”却不讲它占用了额外空间、写放大增加。面试官问“有没有什么副作用”大概率就是希望听到这类平衡性思考。我一般会这样答索引本质上是空间换时间覆盖索引尤其明显多个大字段组成的宽索引会显著拖慢写入和增加内存开销所以只在最核心的热点查询上建。6.3 给准备OD面试的几句经验最后从准备角度说几句实在话。不要以为只要背面试题就能稳过现在很多面试官会拿着你的项目记录一条条追问甚至会出一个你项目里根本没遇到过的极端场景让你现场设计解决思路。这背后考察的是实际解决问题的框架。我的建议是准备一张MySQL知识地图。以索引、事务、锁、日志为主线每个主线再挂两到三个真实案例。比如索引下面挂“深分页优化”“隐式转换导致索引失效”“联合索引设计原则”等。复习的时候不是让你念case而是讲清楚问题背景、排查过程、为什么这么做、最后效果如何。有条件的话自己在一台测试库上复现一下慢SQL和死锁比看十篇文章都有用。面试过程中如果真遇到不会的题可以先说“我平时处理这类问题时一般这样排查”把思路拆开讲即使最终答案没完全对面试官也会觉得你有方法。最怕的是沉默卡壳或者硬编一个不确定的答案这在技术面里会被直接扣分。我在实际整理真题的过程中最大的体会就是MySQL面试里没有偏题怪题所谓“难题”大多是把基础概念和工程实践绑在一起考。只要把底层原理吃透再积累几个自己真正排查过的案例应对绝大多数追问都够了。如果你正准备面试建议优先把索引和事务部分再过一遍这两块基本可以决定数据库环节的上限。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑