2026/10/12 0:58:22

MySQL笔试题详解:从索引、锁到MVCC的高频考点与避坑指南

MySQL笔试题详解:从索引、锁到MVCC的高频考点与避坑指南 简介在数据库技术面试与笔试中MySQL的索引机制、执行计划分析、事务隔离级别和锁策略始终是区分候选人的关键分水岭。理解索引如何通过EXPLAIN优化查询效率掌握共享锁、排他锁与间隙锁在并发场景下的行为以及通过ReadView理解MVCC的可见性规则都是开发者应对数据库岗位考察的核心能力。从基础SQL优化到线上故障排查这些知识不仅决定笔试成绩更直接影响复杂业务系统的性能与数据一致性。本文将围绕MySQL索引、事务隔离级别、日志机制等高频考点结合典型笔试题目中的易错细节梳理一套从原理到实战的复习路径帮助开发者在准备数据库笔试题时建立完整知识框架。1. 一份mysql数据库笔试题PDF我建议你先啃索引再谈刷题很多人在拿到一份mysql数据库笔试题的时候第一反应是从第一道选择题开始背答案背完觉得稳了一到二面动手写SQL就露馅。我见过太多候选人在简历上写“熟悉MySQL”结果连EXPLAIN的type字段都说不全更别提间隙锁和MVCC的可见性规则。这份笔试题PDF覆盖的东西说白了就是面试官用来快速筛选“背题党”和“真用过的人”的试金石。这篇文章不打算带你逐题对答案而是把笔试题背后真正想考察的知识地图拆开告诉你哪些考点必须弄懂原理哪些只需要记住结论以及刷题过程中最常见的几个翻车点。适合正在准备MySQL岗位笔试和面试的开发者也适合那些想用一套自测题检验自己数据库功底的人。2. 摸清考察范围笔试题型分布与高频考点优先级2.1 选择题、填空题与简答题三类题型对应的三种能力一份MySQL笔试题PDF通常不会只有一种题型常见构成是单选或多选、填空、简答或手写SQL。这三种题型对应的能力并不一样备考策略也不能一概而论。选择题考察的是概念的精确度。比如“MySQL默认存储引擎是InnoDB还是MyISAM”这种题背过就能拿分但稍微升级一点问你“哪个隔离级别下会产生幻读”就需要你真的理解事务隔离级别的语义而不是只记住几个名词。多选更狠错选、漏选都不得分逼着你把每个选项都判断清楚。填空题和简答题是另一个路子。填空往往考参数名和命令比如innodb_buffer_pool_size的取值范围、SHOW PROCESSLIST能看到哪些列。简答题则偏向让你写出某条复杂SQL或者解释某个线上故障的处理过程。我一般建议备考者先花一小时通读整份PDF按题型给知识点打标签再决定每个知识点的投入深度。2.2 高频考点优先级表先穷举再排序根据我接触过的多份MySQL笔试题考点分布高度集中大致可以归类成下面的优先级表。这个顺序不是按难度排的而是按出现频率和性价比排的。优先级考点模块常见考察方式建议投入P0SQL语法与增删改查手写查询、多表连接、分组聚合熟练掌握P0索引与执行计划EXPLAIN分析、索引失效场景必须吃透P0事务与隔离级别脏读/不可重复读/幻读辨析必须吃透P1锁机制锁分类、死锁场景重点理解P1存储引擎与日志InnoDB与MyISAM对比、redo/undo/binlog理解差异P1性能优化慢SQL分析、分页优化结合案例P2主从复制与高可用binlog格式、复制延迟原因了解原理P2存储过程与触发器语法编写、执行结果能写能调这份表的意思是如果你时间有限P0级别的内容必须做到闭卷能写P1级别要做到遇到题目能聊出思路P2级别可以最后再看。值得注意的是很多笔试题会把P0和P1混在一起出比如“在可重复读隔离级别下两个事务同时更新同一行会发生什么”这一下就串起了事务、锁和隔离级别三个知识点。另外我看过一些按难度分级的卷子初级岗位的题目通常只到P0中级岗位会加入P1的死锁和索引优化高级岗位则会出现主从延迟排查、批量更新性能对比这类贴近生产的题。所以拿到一份新的笔试题PDF先对照这张表给每道题打标签就能快速判断出题人想要的是什么级别的人。3. 索引、执行计划与锁笔试题里最能拉开分差的三件事3.1 用EXPLAIN把索引失效选择题变成步骤题索引是MySQL笔试的必考模块但是考法越来越不满足于“哪些情况会导致索引失效”这种背诵题。现在的题目通常是给一张表结构和一条SQL问这条SQL有没有走索引或者让你写出优化方案。面对这种题最靠谱的做法不是在脑子里猜而是把EXPLAIN的每个关键字段过一遍。CREATE TABLE order_info ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL, order_no varchar(64) NOT NULL, status tinyint NOT NULL, create_time datetime NOT NULL, PRIMARY KEY (id), KEY idx_user_status (user_id, status), KEY idx_create_time (create_time) ) ENGINEInnoDB; -- 用EXPLAIN判断这条查询是否命中了索引 EXPLAIN SELECT id, order_no FROM order_info WHERE user_id 12345 AND status 1 ORDER BY create_time DESC LIMIT 20;这条SQL里的WHERE条件是user_id和status的等值匹配恰好命中idx_user_status这个联合索引的最左前缀所以type应该是refkey显示idx_user_status。如果笔试题问你“查询能不能走索引”答案不是简单看有没有索引而是要看type和key的组合。我一般会建议把EXPLAIN的六个字段背熟id、select_type、table、type、key、rows其中type的常见取值从好到差依次是system、const、eq_ref、ref、range、index、all只要出现all就说明全表扫描是优化题的标准靶子。索引失效的判断是另一个高频出题点。隐式类型转换是最经典的坑如果user_id是bigint但SQL里写成了WHERE user_id 12345MySQL会把字符串转成数字再比较一般情况下还能走索引反过来如果是字符串字段order_no和数字字面量比较WHERE order_no 12345字段上有索引也可能失效。函数操作和前置模糊查询也一样LEFT(order_no, 5) ORD01、order_no LIKE %123%都让索引失去意义。笔试里遇到这类题我的判断顺序是先看字段有没有索引再看有没有破坏索引列的函数或隐式转换最后用EXPLAIN验证。3.2 锁的分类与死锁判断画一张等待图胜过背十遍口诀锁机制是MySQL笔试题最容易考崩的部分因为锁的分类维度太多而且题目喜欢结合并发场景来问。最基础的分法要能脱口而出按模式分有共享锁和排他锁按粒度分有表锁和行锁InnoDB还有记录锁、间隙锁、临键锁、插入意向锁、自增锁和元数据锁。不要一上来就背定义而是要把锁和场景绑在一起记。死锁题是这类考点里的重点常见考法是给出两个事务的操作序列问它们是否会产生死锁。遇到这种题我建议直接在草稿纸上画等待图。举个例子事务A先更新id1的行再更新id2的行事务B先更新id2再更新id1这就会形成一个双向等待环死锁成立。更隐蔽的是间隙锁造成的死锁比如两个事务都执行SELECT ... WHERE status 1 FOR UPDATE在可重复读隔离级别下如果status 1的行不存在两个事务都会在间隙上加上间隙锁然后再各自插入数据时就会互相阻塞。关于mysql锁的分类笔试题里还有一个非常容易答错的点意向锁。意向锁是表级锁但它的作用是告诉其他会话“这个表里已经有事务在持有行锁”它不阻塞行级操作只阻塞表级操作如LOCK TABLES ... WRITE。很多考生把意向锁理解成一种行锁一答就错。我的习惯是画一张三层表格来描述锁兼容性共享锁之间互相兼容排他锁与任何锁都不兼容意向锁之间总是兼容的。-- 查看当前事务持有的锁死锁排查时必用的命令 SHOW ENGINE INNODB STATUS; -- 查看正在执行的SQL进程判断是否有长时间未提交的事务 SHOW PROCESSLIST;SHOW ENGINE INNODB STATUS这个命令会在输出里带上最近一次死锁的信息包括两个事务互相等待的SQL语句和锁对象。笔试如果出排查题这基本是标准答案的入口。遇到死锁题先用进程列表找出两个以上的活动事务再用锁等待图判断环最后确认持有和等待的锁对象。3.3 InnoDB日志三件套redo、undo和binlog的不同分工很多MySQL笔试题会问到崩溃恢复和主从复制这类题目背后依赖的就是日志机制。InnoDB里有两类日志加上MySQL Server层的binlog共三件套。它们的定位完全不同redo log保证事务的持久性解决的是宕机后已提交事务不丢失的问题undo log支持事务回滚和MVCC用来在回滚时恢复旧版本数据binlog是逻辑日志记录的是SQL级别的变更用于主从复制和时间点恢复。笔试题常挖的坑是redo log和binlog有什么区别回答要点是这两者的写入时机和内容格式都不一样。redo log是InnoDB存储引擎层的物理日志记录的是对数据页的修改binlog是Server层的逻辑日志记录的是SQL语句或行数据变更。两阶段提交协议会出现在更细的题目里问“为什么redo log和binlog需要两阶段提交”答案是避免写完一个日志但另一个日志没有写导致主从数据不一致。如果题目考到参数调优innodb_flush_log_at_trx_commit和sync_binlog这两个参数基本是必问。我把它们的取值影响放在一个表里直接背这张表比翻书快参数取值含义崩溃丢失风险innodb_flush_log_at_trx_commit0每秒刷新redo log到磁盘最多丢1秒事务innodb_flush_log_at_trx_commit1每次提交都刷新到磁盘不丢已提交事务innodb_flush_log_at_trx_commit2每次提交写入OS缓存、每秒刷盘最多丢1秒事务sync_binlog0由OS决定何时刷盘可能丢binlogsync_binlog1每次提交都刷盘不丢binlog笔试里的配置题不会只问你默认值而是给一个“RPO为0”的要求让你选参数组合。答案是innodb_flush_log_at_trx_commit1加上sync_binlog1这两者同时为1才能保证主库和从库都不丢数据。这种题考的是从参数到业务需求的反向映射背数值没有用得理解每个参数控制哪一段写入路径。4. 事务、隔离级别与MVCC把背过的概念变成看得见的ReadView4.1 四种隔离级别别只背名字要能说清每种级别解决了什么事务隔离级别是MySQL笔试题的核心也是区分“背过书”和“真正理解”的分水岭。标准SQL定义了四种隔离级别MySQL的可重复读是默认值这和很多其他数据库默认的读已提交不一样这个差异本身就经常被拿来出题。需要理清一条线读未提交允许读未提交数据所以有脏读问题读已提交解决脏读但两次查询之间其他事务可能提交了新数据所以有不可重复读问题可重复读解决不可重复读但新插入的行可能让同一条件的查询又多出结果所以有幻读问题串行化用锁把所有事务排队执行解决幻读但并发度最低。MySQL的InnoDB在可重复读级别下用间隙锁和MVCC解决了大部分幻读场景但注意是“大部分”不是“全部”。笔试里最爱出的一个变种是在可重复读级别下事务A先查一个范围的记录事务B插入一条新记录并提交事务A再查这个范围会不会多出一行如果事务A的查询是普通快照读答案是看不到新行因为MVCC复用了第一次查询的ReadView但如果事务A使用SELECT ... FOR UPDATE这种当前读间隙锁会锁住范围事务B的插入会被阻塞根本插入不了。还有更极端的场景如果事务A在事务B插入并提交之后才第一次执行查询那么第二条SQL会建立新的ReadView就可能看到事务B插入的数据。这种题需要你用ReadView的时间线来推不能只背“可重复读没有幻读”这个结论。-- 在可重复读隔离级别下验证快照读的可见性 START TRANSACTION; SELECT * FROM order_info WHERE status 1; -- 此时在另一个会话插入一条status1的记录并提交 -- 回到当前事务再次执行相同查询 SELECT * FROM order_info WHERE status 1; -- 结果与第一次一致看不到新插入的行 COMMIT;4.2 MVCC的ReadView规则用一条SQL推演可见性MVCC的题目难不在概念而在应用。要理解MVCC脑子里要有两个东西一是undo log里的版本链二是ReadView的四个核心字段。版本链上每个事务都会留下自己的事务ID一条记录的多个历史版本通过回滚指针串联。ReadView则是某个事务在执行快照读时拍下的“活跃事务列表”快照它包含m_ids、min_trx_id、max_trx_id和creator_trx_id。判断一条记录对当前事务是否可见规则可以压缩成四句话如果记录的事务ID等于创建者ID可见如果记录的事务ID小于min_trx_id说明是已提交事务可见如果记录的事务ID大于等于max_trx_id说明是未来事务不可见如果记录的事务ID在min_trx_id和max_trx_id之间则要看这个ID在不在m_ids活跃事务列表里在就不可见不在就可见。笔试题不会直接让你背这四个规则而是给你若干个并发事务的时序让你判断某条SELECT能看到哪个版本的数据。我的建议是遇到这种题就画条时间轴把每个事务的开始时间、修改记录时间、提交时间标出来再按照ReadView的四个规则逐条判断。画完时间轴答案很难出错。这个判断过程和我们在线上排查“为什么从库查到旧数据”的思路一致把抽象概念落到时间轴上比死记硬背可靠得多。4.3 幻读与当前读一道题把隔离级别和锁串起来关于mysql事务处理有一道经典笔试题是这样出的事务A执行SELECT * FROM t WHERE id 10 FOR UPDATE事务B要插入id 11的记录问会不会阻塞。答案是会阻塞。因为在可重复读隔离级别下id 10的范围会被临键锁覆盖事务B的插入操作要被插入意向锁阻塞。这道题同时考了当前读、临键锁和插入意向锁还顺带考了幻读的解决方案是一道性价比非常高的综合题。反过来如果事务A执行的只是普通SELECT事务B插入id 11就能成功因为快照读不加锁。这个区别是考生失分的重灾区。我把这两条路总结成一句话快照读靠MVCC防幻读当前读靠临键锁防幻读。笔试题里如果看到FOR UPDATE或LOCK IN SHARE MODE就进入锁分析模式如果只是裸SELECT就进入MVCC分析模式。5. 刷题避坑5个让我翻过车的细节与对应解法5.1 默认值判断题NOT NULL到底要不要DEFAULT 0笔试题喜欢考建表语句里的细节。现象是很多人在写ALTER TABLE ... ADD COLUMN时加了一个NOT NULL却不带默认值然后被问“这张表已经有一万行数据执行会怎样”。原因是对MySQL的默认行为不够清楚在严格模式下给已有数据的表新增NOT NULL字段不带默认值MySQL会用隐式默认值填充数字类型填0字符串类型填空字符串但不同版本的行为有差异。解决方法是建表或加列时显式写出DEFAULT既保证可读性也避免版本差异导致的血泪经验。5.2 ORDER BY排序题忽略filesort是常见翻车点现象是在分析一条带ORDER BY的慢查询时只看了EXPLAIN里的key字段以为走了索引就万事大吉结果Extra列里赫然写着Using filesort。原因是排序字段不在索引里或者排序方向和索引顺序不一致MySQL只能把结果集放进内存或磁盘临时文件排序。解决方法是把ORDER BY字段加入联合索引或者让排序方向与索引一致。一道经典题是SELECT * FROM t WHERE a 1 ORDER BY b如果只建了idx_a那就免不了filesort建idx_a_b才能同时覆盖筛选和排序。这就是mysql排序题的核心。5.3 深度分页优化题只答LIMIT加OFFSET会被追问现象是笔试题问“如何优化百万数据量的分页查询”照着常规写法答LIMIT 100000, 20就被否了。原因是OFFSET越大MySQL扫描并丢弃的行越多代价线性增长。解决方法是改写为游标分页或子查询分页。游标分页用WHERE id 上一页最大id ORDER BY id LIMIT 20子查询分页则是先在索引上定位起始点再回表取数据。笔试题遇到这个点我会主动说清楚两种方案的使用边界游标分页适合按主键或唯一键排序的列表子查询分页适合无法用简单游标表达的多条件筛选。5.4 存储过程题语法写对了但忽略了delimiter现象是手写存储过程时把CREATE PROCEDURE的语句直接贴进去结果报语法错误。原因是MySQL的默认语句分隔符是分号存储过程体内的分号会被客户端提前截断导致整个创建语句被拆散。解决方法是先用DELIMITER //把分隔符换掉写完过程体再用DELIMITER ;恢复。如果你在笔试题里看到一段存储过程创建语句先看第一行是不是DELIMITER这是出题人经常埋的小坑。-- 正确的存储过程创建方式 DELIMITER // CREATE PROCEDURE get_user_orders(IN uid BIGINT) BEGIN SELECT order_no, status, create_time FROM order_info WHERE user_id uid ORDER BY create_time DESC LIMIT 100; END // DELIMITER ;这段代码有两个参数说明IN表示输入参数uid是BIGINT类型调用时直接传用户ID即可DELIMITER不是SQL语句是客户端命令所以不要在面试时把它当成MySQL语法的一部分来讲但要能解释清楚它解决的是什么问题。5.5 连接池题maximumPoolSize不是越大越好mysql的数据库连接池在笔试题里通常是简答题问的是HikariCP或Druid的参数怎么调。现象是有人把最大连接数调到几百觉得并发越高越好结果数据库连接数暴涨数据库CPU被打满。原因是连接池里的连接是珍贵资源每条连接都会占用数据库端的内存和线程过大的池子反而会增加上下文切换和锁竞争。解决方法是先确认数据库的max_connections再把连接池最大连接数设为预期并发数的合理倍数。笔试题里如果给出“单次查询耗时50ms目标QPS 2000”的条件答案是用连接数 QPS × 平均耗时的公式估算即2000 × 0.05 100再留出冗余。6. 把答案解析变成生产习惯验证方法与进阶用法刷完一份笔试题PDF不要急着背下一份先用本地方案把几个关键答案跑一遍。最常见的做法是装一个MySQL实例。你要是想在Windows上快速搭环境用MySQL安装教程里的zip解压版就能跑起来注册服务之后用mysql -uroot -p登录要是你习惯用容器docker run -p 3306:3306 --name mysql-test -e MYSQL_ROOT_PASSWORD123456 -d mysql:8.0就能起一个测试实例。装的时候注意MySQL 5.7和8.0在认证插件上的差别8.0默认的caching_sha2_password会让一些旧版客户端连不上遇到Authentication plugin报错就改用mysql_native_password。如果docker安装mysql失败先看日志确认是不是端口冲突或数据目录权限问题再决定是否挂载宿主机目录。验证题目时我有三个固定动作。第一把笔试题里的表结构和数据原样建一遍用真实数据跑SQL核对EXPLAIN输出的type和Extra和答案是否一致。第二开两个终端窗口模拟两个事务验证锁和隔离级别相关的答案重点复现死锁场景然后查看SHOW ENGINE INNODB STATUS里的死锁日志。第三针对慢查询优化题先记录优化前的执行时间再按索引方案修改并对比执行时间把优化效果量化出来。这三个动作做完笔试题对你来说就不再是“答案是什么”而是“为什么会这样”。进阶一点的玩法是把笔试题当成压测素材。比如题库里说“OR条件可能导致索引失效”你可以用sysbench或者简单的并发脚本在同一个表上分别跑OR版和UNION ALL版的查询对比TPS和平均延迟。我习惯把这类结论整理成一张自用的调参清单建索引前先看字段选择性数值类型选择用BIGINT而非VARCHAR深度分页默认走游标方案批量更新控制每批行数。这套习惯来自当年一道“为什么批量删除性能很差”的笔试题查了半天发现是删500万行时每行都走独立事务。从那以后我凡是做清理任务一律先按主键分段每段一个事务删完一段提交一次。这个教训让我少踩了很多坑也希望帮到你。本文还有配套的精品资源点击获取