MySQL高频面试题全解析:从索引优化到主从复制的实战指南
又到一年跳槽季后台私信里问得最多的还是那句话“MySQL 面试题到底怎么准备”说实话市面上的题解很多但大部分都停留在背答案的层面——索引优化八股背得滚瓜烂熟面试官换一种问法就露怯。我把过去三年里真实面过、帮朋友模拟面试复盘过、以及各大技术社区讨论度最高的 MySQL 高频面试题重新梳理了一遍剔除了那些问了等于没问的送分题留下的每一条都能往深处挖两层。这份清单适合正准备 Java 后端、大数据、数据库运维方向面试的读者也适合入职两三年想系统查漏补缺的开发者。我会尽量讲清楚面试官为什么这么问以及回答到哪一步能加分。1. 索引为什么用 B 树从数据结构一路追问到底索引相关的内容在 MySQL 面试里出现频率实在太高了几乎每个面试官都会从一个看似基础的问题开始然后层层加码。你如果只背到“B 树查询快”就停下大概率会被追问“那为什么不直接用红黑树”或者“Hash 索引不是更快吗”。1.1 面试官问索引真正想考什么很多人在准备索引题时有误区喜欢背结论而不理解推导过程。面试官问“为什么 MySQL 用 B 树做索引”其实是想考察两个层面的东西第一你知不知道各种数据结构的适用场景包括树高、磁盘 IO、范围查询、顺序访问第二你能不能把“磁盘预读”这个隐藏前提讲出来。这里给一个直观的类比。数据库的索引好比一本很厚的书的目录如果目录本身就要翻很多页才能找到目标那你还不如直接翻正文。磁盘上的数据是按页读取的一次 IO 大概能读 4KB 到 16KB 不等InnoDB 默认页大小是 16KB所以数据结构的设计目标非常明确尽可能让一次磁盘 IO 读到更多有效信息同时减少树的高度。B 树相比 B 树的优势在于所有数据都存在叶子节点非叶子节点只存键值这样一棵 3 层的 B 树可以放下千万级别的数据而从头到尾只要 3 次磁盘 IO。范围查询的时候叶子节点之间有指针相连可以直接顺序遍历这是红黑树做不到的。反观红黑树虽然内存里增删查都很优秀但树的高度比 B 树高很多而且在磁盘上不连续存储范围查询需要回溯根本不适合做磁盘索引。1.2 聚簇索引与非聚簇索引回表是怎么发生的高频题里“聚簇索引”和“非聚簇索引”的区别几乎是必问的。简单说InnoDB 里聚簇索引的叶子节点直接存放整行数据所以一张 InnoDB 表只能有一个聚簇索引你给表加的主键就是聚簇索引如果没有主键InnoDB 会用第一个非空的唯一索引替代再没有就生成一个隐藏的 rowid。非聚簇索引也就是二级索引的叶子节点存的是索引列的值加主键值。这里就引出了“回表”的概念你通过二级索引找到主键再用主键去聚簇索引里查一次完整数据这个过程就叫回表。我见过不少候选人能说出“回表”的定义但被问到“怎么避免回表”的时候思路打不开。最优解是覆盖索引如果你要查询的列全部包含在二级索引里那就不需要回表了这就是覆盖索引为什么快的本质原因。# 假设这张表有 id、name、age 三个字段 # 这句会回表idx_name 只包含 name 和主键查 age 需要回表 SELECT id, name, age FROM user WHERE name zhangsan; # 这句不会回表idx_name_age 包含 name、age、主键 # 查询列都在索引里 CREATE INDEX idx_name_age ON user(name, age); SELECT id, name, age FROM user WHERE name zhangsan;1.3 索引失效场景为什么明明建了索引还是慢面试里高频的场景题之一给某列建了索引但查询还是全表扫描为什么其实索引失效的本质是“优化器觉得走索引还不如全表扫描”或者“你的写法让索引失去了有序性”。按踩坑频率排一下常见的失效场景第一对索引列做了函数运算或隐式类型转换。比如WHERE DATE(create_time) 2026-01-01这破坏了索引列本身的有序性MySQL 只能放弃索引。正确写法是WHERE create_time 2026-01-01 AND create_time 2026-01-02。第二模糊匹配用了前导通配符比如LIKE %abc因为 B 树是严格按照前缀有序的前缀不确定就只能全扫。第三联合索引不满足最左前缀原则。面试官最爱问的就是“建立了 (a, b, c) 索引查询条件是b ? AND c ?能不能走索引”答案是不能因为联合索引先按 a 排序a 不确定时 b、c 的有序性无法利用。第四OR连接的条件里有一个没索引整个查询可能都不走索引。第五数据量本身很小或者优化器估算走索引代价更高MySQL 会主动放弃索引这种情况往往被初学者误判为“索引失效”。我自己的习惯是分析这类问题时先打开EXPLAIN看一眼key、type、rows而不是凭感觉猜。关于 EXPLAIN 的具体用法后面会有一整部分展开。1.4 实操心得怎么设计一个真正低成本的索引索引不是越多越好每个索引都要占用磁盘空间写入时还要维护 B 树的更新索引一多写入性能就会下降。我在实际项目中给表加索引前会问自己四个问题这个查询的过滤性如何如果某个字段只有两个取值比如性别选择性太差加索引大概率没意义。查询的列能不能组成覆盖索引写操作多不多如果这张表每分钟几千次的 INSERT/UPDATE索引数量必须克制。最容易被忽略的一个经验联合索引的顺序非常讲究。一般来说把等值查询的字段放前面把排序字段放后面高频查询优先。比如订单表经常按user_id等值过滤再按create_time排序那建(user_id, create_time)远比(create_time, user_id)合理前者一个索引就同时解决了过滤和排序。另外线上大表加索引不要在业务高峰期直接跑MySQL 8.0 虽然支持 INPLACE 算法但大批量 DDL 依然会引起主从延迟。常规做法是先在从库上加好验证低峰期再切换更稳妥的团队会走工具来减轻主库压力。2. 事务隔离级别与 MVCC快照读背后的可见性规则事务这块是 MySQL 面试的另一座大山而且往往和 Spring 事务、分布式事务的题目联动出现。面试官通常会从一个具体场景切入两个会话同时更新同一行数据会发生什么你看到什么、更新什么、提交后对方能不能看到这些问题的底层都是隔离级别和锁机制。2.1 ACID 到底靠什么实现背 ACID 四个特性的定义只是及格线要知道每个特性背后对应的机制才算有深度。原子性靠 undo log。事务里万一执行到一半失败了MySQL 需要把已修改的数据回滚到事务开始前的状态undo log 记录的就是旧值。持久性靠 redo log。每次数据页的修改不会立即刷盘而是先写 redo log即使实例崩溃重启后也能通过 redo log 重放恢复这就是 WALWrite-Ahead Logging机制。一致性是应用层的概念数据库层面通过约束、触发器以及原子性和隔离性共同保证。隔离性靠的是锁和 MVCC也就是下面要展开的重点。面试官如果追问“redo log 和 binlog 有什么区别”记得抓住核心redo log 是 InnoDB 引擎层的东西记录的是物理页的修改用于崩溃恢复binlog 是 MySQL Server 层的逻辑日志记录的是语句或行数据的变化用于主从复制和数据归档。两者配合还引出了“两阶段提交”的概念这在主从复制部分再展开。2.2 四种隔离级别会踩到什么坑MySQL 默认隔离级别是REPEATABLE READ可重复读这和 Oracle 默认的READ COMMITTED不一样也是面试官喜欢埋坑的地方。四种隔离级别分别解决的问题是读未提交READ UNCOMMITTED会出现脏读读已提交READ COMMITTED解决了脏读但会出现不可重复读可重复读REPEATABLE READ解决了不可重复读但理论上还有幻读串行化SERIALIZABLE最安全但并发性能最差。关键在于MySQL 的 InnoDB 在可重复读级别下通过间隙锁和 MVCC 基本把幻读也解决了对快照读而言所以实际生产中可重复读就够用了。面试官如果问“那为什么很多互联网公司把隔离级别改成读已提交”你可以回答可重复读下间隙锁的锁定范围更大在高并发写入场景下更容易出现锁冲突和死锁读已提交只锁行不锁间隙并发度更高代价是可能出现不可重复读但在大部分业务里这种副作用可以接受。2.3 MVCC 的隐藏字段与 ReadView 规则MVCC 全称是多版本并发控制是 InnoDB 实现高并发读的核心机制。每行数据都有两个隐藏字段trx_id最近一次修改该行的事务 ID和roll_pointer指向 undo log 中该行旧版本的指针。更新一行数据时旧版本先写入 undo log然后行上的trx_id替换成当前事务 ID。查询的时候每个事务会生成一个 ReadView里面记录着“当前活跃事务的 ID 列表”和“创建该 ReadView 的事务 ID”。判断一行可见性的规则是如果行的trx_id小于 ReadView 里最小的活跃事务 ID说明这个版本在快照创建前就已提交可见如果大于最大的活跃事务 ID说明这个版本是快照创建后产生的不可见如果落在中间则看trx_id是否在活跃列表里在则不可见不在则可见。这里有个高频追问既然可重复读和读已提交都有 ReadView那两者有什么区别答案是读已提交每次查询都会生成一个新的 ReadView所以两次查询可能看到不同版本可重复读只会在事务第一次查询时生成 ReadView之后整个事务都复用同一个快照自然就“可重复”了。2.4 间隙锁与 next-key lock幻读是怎么防住的只聊 MVCC 不讲锁面试官追问两句就会露馅。可重复读级别下InnoDB 加锁时不仅锁住命中的行还会锁住行之间的间隙这就是间隙锁Gap Lock。间隙锁和行锁合起来叫 next-key lock临键锁锁定的范围是“左开右闭”的区间。为什么需要间隙锁假设订单表里金额在 100 到 200 之间的记录只有三条事务 A 执行SELECT ... WHERE amount BETWEEN 100 AND 200 FOR UPDATE事务 B 同时插入一条 amount 150 的记录。如果只锁已有行B 的插入就会成功事务 A 再次查询时多出了一条“幻影”记录这就是幻读。间隙锁把 100~200 这个范围锁住B 的插入会被阻塞从机制上杜绝了这类问题。需要提醒的是间隙锁本身只防止“插入”不防止“更新”所以它的粒度比行锁大得多也是死锁的高发源头之一。互联网公司喜欢切读已提交其中一个原因就是为了减少间隙锁带来的冲突。2.5 死锁排查从 show full processlist 到 kill死锁几乎是业务场景必考候选人如果能讲出一次真实排查经历会非常加分。实际判卷标准是命令怎么用、怎么看结果、怎么处理。死锁的经典场景是两条 SQL 按不同顺序加锁。比如事务 A 先锁 id1 再锁 id2事务 B 先锁 id2 再锁 id1双方互相等待不出意外其中一个会被 InnoDB 检测到并回滚。排查的第一步是用SHOW ENGINE INNODB STATUS查看最近一次死锁的详细信息里面会给出两个事务的 SQL 和它们正在等待的锁对象。如果不想看大段输出可以先SHOW FULL PROCESSLIST找一找有没有长时间Waiting for lock状态的会话定位到可疑连接后确认业务来源然后KILL thread_id释放资源。我自己遇到过最典型的死锁场景是一个批量更新任务程序用循环逐个更新记录但列表是按主键升序排的另一个定时任务却按某个二级索引的顺序批量更新两条链路拿锁的顺序相反在数据量大的时候频繁死锁。这类问题的解法不是单纯重试而是统一加锁顺序让所有事务都按同一顺序拿锁可以从根本上消除大部分死锁。3. 一条慢 SQL 的完整排查链路EXPLAIN 里的每一列都别放过索引和事务讲完面试题开始转向实战给你一条慢 SQL你怎么定位、怎么优化这一块最能区分“背题选手”和“真做过优化的人”。建议先把慢查询日志打开再学会看执行计划最后才是调 SQL 或加索引。3.1 慢查询日志怎么配最省事先给一个可以直接抄的配置-- 开启慢查询日志超过 1 秒的 SQL 都会被记录 SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1; -- 确认当前配置 SHOW VARIABLES LIKE slow_query_log%; SHOW VARIABLES LIKE long_query_time;生产环境更常见的做法是在配置文件my.cnf里配置slow_query_log 1、long_query_time 1并且把log_queries_not_using_indexes打开这样没有用索引的查询也会被记录下来方便提前发现隐患。这里有个实际操作中的坑long_query_time的单位是秒默认是 10 秒很多小项目跑一年都记不到几条日志根本起不到监控作用。我一般上线后先设成 1 秒跑一两天低峰期再根据业务调整同时要小心SET GLOBAL只对后续新建的连接生效线上排查时最好用SET SESSION long_query_time 0.1在当前会话里临时设置免得影响全局配置。3.2 EXPLAIN 的每一列都要会看执行计划是定位慢查询的利器但很多人只会看type和key这就浪费了。我把最关键的字段列一张表字段关注点常见取值type访问类型从好到坏排序system const eq_ref ref range index ALLkey实际用到的索引不是“可能用到的索引”NULL 表示没走索引rows优化器估算的扫描行数越小越好注意是估算值Extra附加信息藏了很多细节Using index覆盖索引、Using filesort文件排序、Using temporary临时表、Using where面试官常问的两个陷阱一是type index看着是走了索引但它扫的是整个索引树性能未必比 ALL 好多少比如对一个非索引列做统计时可能出现。二是Extra里有Using filesort说明 MySQL 为了排序可能要额外消耗大量内存或临时文件即使key不为空这条 SQL 依然可能慢。我见过一个真实案例订单列表页按create_time倒序分页WHERE user_id ? ORDER BY create_time DESC索引只建了(user_id)于是走索引拿到了所有订单但排序是文件排序。加了(user_id, create_time)联合索引之后B 树本身就有序排序环节直接消失查询时间从 800 毫秒降到 20 毫秒。这就是为什么我总说“排序字段要进索引”。3.3 分页查询为什么越翻越慢分页深翻页是业务里最常见的一类慢查询。LIMIT 200000, 20看起来只是查 20 条实际 MySQL 要先扫出前 200020 条再丢掉前 200000 条扫描量巨大。优化的思路有两个方向如果查询条件允许用“上一页最后一个主键”代替偏移量。比如列表页上一页最后的 id 是 185432下一页就写成SELECT * FROM orders WHERE id 185432 ORDER BY id ASC LIMIT 20;这样每次查询都只扫 20 条稳稳地走主键索引。如果场景复杂、必须用偏移量分页比如中间跳页可以考虑延迟关联先查LIMIT 200000, 20只取主键再拿主键关联回原表取全字段避免回表大量无关行SELECT o.* FROM orders o INNER JOIN ( SELECT id FROM orders ORDER BY create_time DESC LIMIT 200000, 20 ) t ON o.id t.id;子查询只扫主键索引数据量小再把 20 条主键带回原表查询整体性能远好于直接大偏移量。3.4 高频 SQL 细节排序、UPDATE、字符串转日期热词里出现了mysql排序、mysql update语法、mysql将字符串转为日期这类细节题看着简单却经常被笔试和面试官当作“基本功”抽查。排序方面提醒一个点ORDER BY 默认按升序null 值的排序在不同数据库里表现不同MySQL 里 null 默认排在最前面想放到最后可以用ORDER BY create_time IS NULL, create_time ASC。UPDATE 语法有一个容易踩坑的细节UPDATE更新同一张表时子查询不能直接引用目标表比如UPDATE t SET a (SELECT MAX(a) FROM t)会报错You cant specify target table for update in FROM clause需要先包一层临时子查询。字符串转日期的高频写法是STR_TO_DATE(2026-03-15 10:30:00, %Y-%m-%d %H:%i:%s)反向的日期格式化用DATE_FORMAT。这里有个让我吃过亏的坑字符串里日期格式如果含中文或空格与格式串不一致转换结果是 NULL而且不报错排查起来非常隐晦。建议所有字符串转日期的操作都先单独验证一下输出再放进业务 SQL。4. InnoDB 与 MyISAM 之选存储引擎背后的设计取舍“MySQL 支持哪些存储引擎InnoDB 和 MyISAM 有什么区别”这是五年前的高频题现在虽然大多数场景已经默认 InnoDB但面试官依然喜欢拿出来考基础功底。原因在于通过对比可以考察你知不知道一个存储引擎该考虑哪些维度事务、并发、索引结构、崩溃恢复、外键、全文索引等。4.1 两种引擎的能力对比直接上一张对比表方便记忆也方便面试时快速组织语言对比项InnoDBMyISAM事务支持支持不支持锁粒度行锁 间隙锁表锁外键支持不支持崩溃恢复通过 redo log 恢复无崩溃恢复能力索引结构聚簇索引非聚簇索引全文索引8.0 支持支持数据文件.ibd数据和索引一起.MYD .MYI 分离这里有个很经典的追问为什么 InnoDB 比 MyISAM 适合高并发写入回答的关键在锁粒度。MyISAM 的锁是表级锁写入时整张表都锁住读写互斥严重InnoDB 支持行锁和 MVCC读不会阻塞写写可以通过多版本机制与读并行并发度完全不在一个量级。另一个隐藏考点是“InnoDB 表为什么必须有主键”。前面提过InnoDB 聚簇索引基于主键组织数据如果你不确定主键系统也会生成隐藏主键但隐藏主键对你不可见回表和后续优化都会很别扭。所以建表的第一件事就是定一个业务主键或自增主键不要偷懒。4.2 InnoDB 的 Buffer Pool为什么热数据查得快问到存储引擎原理Buffer Pool 是一个很好的加分项。InnoDB 对数据的读写并不是直接操作磁盘数据文件而是通过内存缓冲池。读的时候先看数据页是否在 Buffer Pool在就直接返回页写的时候先修改内存里的页再由后台线程异步刷盘这样大部分操作在内存里完成磁盘 IO 被极大减少。Buffer Pool 的大小在 MySQL 8.0 里默认是物理内存的 75% 左右生产环境通常建议设置在物理内存的 60%~70%具体要看实际负载。如果设置过小页被频繁换入换出会出现大量的磁盘 IO 和 free pages 告警如果过大又可能挤压操作系统和其他进程的内存。面试时可以主动提一下 LRU 回收策略InnoDB 的 Buffer Pool 用改进版 LRU大约 5/8 的区域被定义为“热区”3/8 为“冷区”新读入的页先进冷区避免一次全表扫描把真正的热数据全部挤出内存。这个细节一说出来面试官基本能把你和普通背题选手区分开。4.3 auto_increment 锁一个容易忽略的并发考点auto_increment是建表时最常见的设置但它的锁机制很多候选人没想过。InnoDB 对自增值的分配有三种锁模式默认配置是innodb_autoinc_lock_mode 2交错模式。这个模式下插入语句执行时不会锁表并发插入的自增值可能不连续但性能最好。如果面试官问“批量插入时会不会把表锁住”得分情况回答对于无法提前确定插入行数的语句比如INSERT ... SELECT即使锁模式是 2仍然可能使用表级 AUTO-INC 锁保证生成的自增值连续代价是这个语句执行期间其他插入只能等待。所以大事务里尽量避免混合使用不同类型的插入语句不然容易出现自增值空洞和偶发的等待。4.4 实际项目里怎么选型虽然教材里列了很多引擎但以我现在的观点新业务直接无脑 InnoDB 就行除非你明确需要 MyISAM 的某些特性。比如早期版本的全文索引在 MyISAM 上支持更好但对于新项目MySQL 8.0 的 InnoDB 已经原生支持全文索引MyISAM 的最后一个优势也没了。还有一个从运维角度容易忽视的点MyISAM 在崩溃后往往需要REPAIR TABLE才能恢复而且有可能损坏数据InnoDB 可以通过 redo log 自动恢复。在数据价值越来越高的今天选 MyISAM 的隐性成本很难接受。如果面试官问“你线上用过别的引擎吗”最好能讲出“因为什么场景考虑过、最后为什么放弃”的完整决策过程这才是真正的加分项。5. 主从复制的三个坑延迟、丢数据与同步异常凡是简历上写了“负责订单/交易系统”的候选人主从复制几乎是必考因为单机数据库总有性能瓶颈生产环境普遍走“一主多从 读写分离”。热词里也有“怎么使用 mysql 主从复制、把远程库的这张表同步到本地”这类检索说明大家在实操中确实遇到不少问题。5.1 一条数据的复制全链路主从复制的原理很简单但很多人答不全链路。可以按一条 INSERT 语句的旅程来拆解主库写入时事务提交前会写 binlog二进制日志从库的 IO 线程去主库拉取 binlog写到从库本地的 relay log中继日志从库的 SQL 线程读 relay log在从库上按顺序回放。只要两个线程工作正常主从数据就能保持一致。有几个关键词值得展开binlog 有STATEMENT、ROW、MIXED三种格式。MySQL 5.7.7 之后默认是ROW格式记录的是每一行的变化虽然日志量更大但复制更安全尤其是遇到 UUID、NOW() 这类非确定性函数时ROW 格式不会出现两边结果不一致的问题。面试官问到“为什么推荐 ROW 格式”建议从这个角度切入。5.2 异步、半同步与组复制丢数据的分寸主从复制最怕的不是延迟而是丢数据。默认的主从复制是异步的主库提交事务后立刻返回成功binlog 还在内存里从库还没来得及拉走主库如果宕机这部分数据就丢了。半同步复制解决了部分问题主库在提交事务后必须等待至少一个从库确认收到 binlog 才能返回成功这样主库宕机时至少有一个从库还有数据丢失的概率大幅降低。代价是主库的写入延迟会增加因为多了“等待从库确认”这一步。组复制Group Replication更进一步通过 Paxos 共识协议在多个节点间达成一致适合对数据一致性要求极高的场景但配置和运维复杂度也要高不少。面试时被问到“能不能接受丢失数据”不要直接答“不能”或“能”而要区分场景核心交易数据必须开半同步或组复制日志、报表这类可容忍少量丢失的数据可以用异步复制换取性能。这种有取舍的回答比单纯背书要立体得多。5.3 主从延迟的排查与处理延迟是主从复制最常见的实际故障。排查之前先看从库状态SHOW SLAVE STATUS\G -- 重点看两个字段 -- Seconds_Behind_Master主从延迟的秒数 -- Slave_IO_Running / Slave_SQL_Running必须都显示 YesSeconds_Behind_Master越大说明从库回放越慢。常见原因有这几种从库硬件性能不如主库从库上还有一堆分析型大查询在抢资源主库的写入并发太高从库单线程 SQL 线程回放不过来。针对最后一个原因MySQL 5.7 之后可以从库开启并行复制slave_parallel_workers让多个线程并行回放不同库/不同事务的 binlog。实际项目里还有一类“伪延迟”从库长时间没有收到任何新事务Seconds_Behind_Master的值反而会很大但这不代表复制出问题了只是算出来的时间差。所以排查延迟不能只看一个指标要结合 binlog 位点、从库当前正在执行的 SQL 以及主库的写入情况综合判断。5.4 实操远程到本地的表同步怎么做回到热词里“把远程库的这张表同步到本地”这个具体诉求。如果说的是一次性同步最简单的思路是用 mysqldump# 在本地执行把远程主库的 order_db 库的 orders 表导出到本地文件 mysqldump -h 远程主机IP -u用户名 -p密码 order_db orders orders.sql # 再将 SQL 导入本地库 mysql -h 127.0.0.1 -u用户名 -p密码 local_db orders.sql注意几个细节mysqldump导出时最好指定--single-transaction这样在 InnoDB 下可以获得一致性快照不会锁住线上表如果只是想要表结构不要数据加-d参数表比较大时可以加--whereid 100000分批导出。如果说是持续同步那就得进入主从复制或者用工具做增量同步。最简单的是从库订阅主库 binlog但需要先在主库开启log_bin和设置server_id然后通过CHANGE MASTER TO配置主从关系。更轻量的方案是用 Canal、Debezium 这类工具解析 binlog把数据写进本地消息队列再落到目标库适合业务侧需要过滤转换的场景。5.5 数据一致性校验主从对账怎么做主从复制跑久了难免出现个别不一致。有一些团队信奉“从库不可恢复就重建”但大多数业务还是需要先做一次对账。常用的工具有pt-table-checksum它可以对主从库的表做差量校验找出不一致的行然后用pt-table-sync修复。如果公司不允许引入额外工具可以自己写脚本对核心表做哈希对比但注意不要在业务高峰期跑因为校验本身也会占用从库资源。6. 存储过程、触发器与查询细节容易被问懵的边角考点最后一章聚焦一些看似冷门、但面试官经常用来“补刀”的知识点。这类题的特点是答得出来不能说明你多强答不出来却很容易暴露短板。热词里专门有mysql存储过程、mysql声明存储过程、mysql中触发器中分隔符等检索说明关注的人并不少。6.1 存储过程的声明与 delimiter 之谜存储过程其实就是在数据库端保存的一段预编译 SQL。声明一个最简单的存储过程DELIMITER // CREATE PROCEDURE get_user_orders(IN user_id INT) BEGIN SELECT * FROM orders WHERE user_id user_id; END // DELIMITER ;新手最容易卡在DELIMITER上。MySQL 客户端默认用分号作为一条语句的结束符而存储过程体里又有大量分号如果客户端还在用默认分隔符解析CREATE PROCEDURE就会被截断。所以用DELIMITER //把结束符临时改成//等存储过程完整创建完再改回DELIMITER ;。面试时如果被问到“存储过程有什么优缺点”建议用结构化方式回答优点是减少网络往返、封装复杂逻辑、批量处理方便缺点是难以调试、版本管理麻烦、存储过程内逻辑过于复杂时会成为性能隐患而且分布式架构下数据库层承担太多业务逻辑并不合适。这里要展现的是“知道什么时候不该用”而不是一味推荐。6.2 触发器与事件调度器用之前先想清楚触发器Trigger是在 INSERT、UPDATE、DELETE 前后自动执行的一段逻辑。比如每次更新订单状态时自动写一条历史记录听起来很方便。但触发器最大的问题是隐式执行排查问题时执行一条 UPDATE 却发现别的表也被改了复盘成本很高。我用过几次后除非是很小很简单的项目否则更倾向把这类逻辑放到业务代码里或者用事件调度器定时扫表处理。事件调度器Event Scheduler有点像数据库内置的定时任务-- 开启调度器 SET GLOBAL event_scheduler ON; -- 每天凌晨 2 点清空临时表 CREATE EVENT clean_temp_table ON SCHEDULE EVERY 1 DAY STARTS 2026-01-01 02:00:00 DO DELETE FROM temp_table WHERE create_time NOW() - INTERVAL 7 DAY;这里的坑是事件调度器依赖 MySQL 实例持续运行实例重启后如果没有把event_scheduler写进配置文件事件不会自动启用。另外跨时区的项目要特别留意事件里用的时间函数避免任务在三更半夜执行。6.3 UNION 与 UNION ALL一字之差性能悬殊这是 SQL 细节题里出镜率非常高的一题。UNION ALL只是把两个查询的结果简单合并不做去重UNION在合并之后还会做一次去重排序代价是额外消耗内存和临时表。如果业务上确定不会有重复行一定用UNION ALL。类似容易被忽略的还有GROUP BYHAVING的顺序WHERE先过滤行GROUP BY再分组HAVING在分组之后过滤组。能用WHERE过滤的尽量在WHERE阶段解决否则HAVING阶段要处理的数据量会大很多。还有一个细节题是COUNT(*)和COUNT(1)哪个快这个题在 MySQL 8.0 里答案已经统一做过优化后两者基本没有差别不要在这个点上浪费太多时间。更值得聊的是COUNT(字段)会跳过 NULL如果字段上有 NULL 值统计结果会不一样这在做报表时是个隐蔽的坑。6.4 数据库连接池翻车的往往是参数热词里出现了“mysql的数据库连接池”这题主要考察生产经验。连接池的作用是复用连接减少频繁创建/销毁连接的开销常见实现有 HikariCP、Druid、C3P0。HikariCP 是目前 Spring Boot 默认的性能好但参数需要按业务调。最容易翻车的是maximumPoolSize设得过大或过小。很多团队照抄默认值或者凭感觉设成 200、500结果数据库连接数被打满反而出现连接等待。我的建议是连接池大小不是越大越好每个连接背后都是一个数据库会话占用内存和锁资源。一般中低并发项目从 20 开始配合压测去调整而不是一上来拉满。另外一个常见问题是连接泄漏——代码里没close()连接池被借空应用就卡死。排查这类问题可以从连接池监控里看Active连接数和活跃线程时间也可以开 SQL 审计定位是哪段代码长时间占用连接。这一类“边角考点”在面试评分上权重不算高但一旦连着答错两三道面试官对基础功底的判断就会明显下滑。所以还是建议花半小时把这块过一遍。最后分享一点我自己的体会。面试题整理得再多如果不去动手开一个 MySQL 实例实际验证很多结论你心里是虚的。我从开始带团队以后一直要求候选人回答数据库问题时给出“你亲眼验证过的实验结果”而不是“网上都这么说”。比如隔离级别这块自己开两个会话跑一遍比背十篇博客都有用。准备面试的核心不是把答案塞满脑子而是建立一套“遇到问题能自己查、能自己推演”的思维习惯这也是这份清单存在的意义——把路标给你但路还得自己走。