在8月前后这个时间节点,Java岗位的面试需求会明显集中,MySQL几乎是无差别必考项。无论面Java后端、大数据还是中间件岗位,面试官都会通过SQL题、索引原理和事务问题快速判断候选人的数据库功底。真正麻烦的不是单个语法记不住,而是知识散落:会写增删改查但不会调优,知道索引但说不清B+树,背了隔离级别但遇到幻读追问就卡壳。这篇内容的目标,是把MySQL面试中最高频的考点压缩成3天可执行的复习路线,从SQL语法、UPDATE陷阱、int类型问题、排序优化,到索引、事务、锁、存储引擎、日志和存储过程,每一块都给出面试侧的关键结论和可直接参考的答题结构。
需要提前说明的是,“3天搞定”并不是什么速成魔法,而是一条复习优先级路线。真正面试时,面试官看的不是你背了多少题,而是你能不能把语法、原理和排查思路连起来讲清楚。所以这篇文章的每一章都按“面试问什么 -> 原理是什么 -> 代码或SQL怎么写 -> 出错怎么排查”来组织,你按这个顺序读一遍,再按文末的自测清单检查一遍,比盲目刷题更有效。
1. 先建立MySQL面试知识地图,再安排3天复习节奏
很多准备面试的人容易犯一个错误:一上来就背题,背了一堆“什么是索引”“什么是事务”,但遇到具体场景还是答不好。原因在于脑子里没有知识地图,考点之间是孤立的。MySQL面试题的分布其实很固定,先看清地图,再安排时间。
1.1 Java面试中MySQL到底考什么
从近两年Java后端岗位的面试反馈看,MySQL考点主要集中在六个方向:SQL语法、索引、事务、锁、存储引擎、日志与优化。每个方向在笔试和面试中的考察形式不一样,优先级也不一样。
| 考点方向 | 常见题型 | 优先级 | 考察能力 |
|---|---|---|---|
| SQL语法 | 手写增删改查、UPDATE陷阱、排序、聚合 | 高 | 是否能直接干活 |
| 索引 | B+树结构、最左前缀、索引失效、EXPLAIN | 极高 | 是否理解查询性能 |
| 事务 | ACID、隔离级别、脏读、不可重复读、幻读 | 极高 | 是否理解并发场景 |
| 锁机制 | 共享锁、排他锁、间隙锁、死锁排查 | 高 | 是否能处理线上问题 |
| 存储引擎 | InnoDB与MyISAM对比、行锁与表锁 | 中 | 是否理解选型 |
| 日志与优化 | redo log、undo log、binlog、慢查询优化 | 高 | 是否理解数据安全与调优 |
其中索引、事务和锁是重灾区,面试官喜欢连环追问。SQL语法是笔试和机试的筛选题,写不出来后面基本没机会。存储引擎和日志更多是概念题,但答案要准确,不能含混。
1.2 面试题的两种深度:会写SQL与会讲原理
MySQL面试题有两层深度,很多人在第一层就停了。第一层是“会写”,比如能写出SELECT、UPDATE、DELETE,能建索引,能写存储过程;第二层是“会讲”,比如能解释为什么UPDATE会锁住整张表,为什么某个查询没走索引,为什么隔离级别是RR时还会出现幻读。
面试官真正想确认的是第二层。举个典型例子:
SELECT * FROM user WHERE name = '张三';如果name列没有索引,这条语句会全表扫描。面试官会接着问:那加索引后一定快吗?如果你回答“一定”,就会被追问:如果查询条件里写了WHERE name = ?但name是VARCHAR类型,传入的是数字呢?这就引入了隐式类型转换导致索引失效的问题。
所以复习时不要只记结论,要记“结论的边界”。索引不是加了就一定生效,事务不是开了就一定安全,存储过程不是能跑通就适合生产。带着边界去复习,面试时才不会被问住。
1.3 3天复习计划怎么排
3天时间不能平均用力,要按考点出现频率分配。推荐这样安排:
| 天数 | 复习主题 | 核心目标 | 验收方式 |
|---|---|---|---|
| 第1天 | SQL语法、UPDATE、int类型、排序 | 手写SQL不卡壳,能讲清常见坑 | 完成10道手写SQL题 |
| 第2天 | 索引、事务、锁、MVCC | 能画B+树结构,能解释隔离级别和锁的关系 | 能用白话讲清RR如何解决幻读 |
| 第3天 | 存储引擎、日志、存储过程、调优 | 能说出选型依据,能给出调优步骤 | 模拟回答“MySQL怎么调优” |
每天晚上用30分钟做自测,不要只看答案,要自己讲一遍。面试本质是口头输出,讲不出来等于没掌握。
2. 第1天:SQL基础、UPDATE语法、int类型和排序问题
第一天的目标是把手写SQL练到肌肉记忆程度。笔试和机试不会给你太多思考时间,SQL语法写错一个关键字、少一个条件,结果就是零分。
2.1 常用数据库命令和查询语法先过一遍
MySQL的命令分为运维命令和查询命令两类。面试和机试常用的是查询命令,但SHOW系列命令在排查问题时会经常用到,也需要熟悉。
-- 查看数据库列表 SHOW DATABASES; -- 切换数据库 USE mydb; -- 查看当前数据库的表 SHOW TABLES; -- 查看表结构 DESC user; -- 查看建表语句 SHOW CREATE TABLE user; -- 查看表的索引 SHOW INDEX FROM user; -- 查看当前执行中的线程 SHOW PROCESSLIST; -- 查看系统变量,比如隔离级别 SHOW VARIABLES LIKE 'transaction_isolation';查询语法最基础但最容易出错的点是执行顺序。很多人以为SELECT先执行,实际上MySQL的执行顺序是:
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT这个顺序决定了你能不能在WHERE里使用SELECT中定义的别名。例如下面这条SQL是错的:
SELECT name AS n FROM user WHERE n = '张三';因为WHERE在SELECT之前执行,执行WHERE时n这个别名还不存在。改成下面这样才对:
SELECT name AS n FROM user WHERE name = '张三';这类细节在面试中很容易被拿来考,属于“看起来简单,写错就露馅”的题。
2.2 UPDATE语法是高频考点:单表更新、多表更新和误更新防护
UPDATE语法在热搜词里出现频率很高,说明很多人在实际写代码和面试翻过车。先看基本语法:
UPDATE user SET name = '李四', update_time = NOW() WHERE id = 1;关键点有三个:
SET后面可以同时更新多个字段,用逗号分隔。WHERE条件是筛选要更新的行,不加WHERE会更新整张表。- 更新后返回的“影响行数”指实际被修改的行数,如果值没有变化,影响行数可能是0。
最常见的生产事故就是UPDATE漏写WHERE。比如:
UPDATE user SET status = 1;这条语句会把所有用户状态改成1。如果是生产环境,数据恢复会非常麻烦。所以面试时一定主动说出这个风险,面试官会认为你有安全意识。
多表更新也是高频考点。比如根据订单金额更新时间,更新用户的会员等级:
UPDATE user u JOIN `order` o ON u.id = o.user_id SET u.level = 2 WHERE o.amount > 1000;这里的JOIN用于关联两张表,ON指定关联条件,SET更新的是主表字段。要注意order是MySQL保留字,作为表名时需要加反引号。
另一个容易忽略的点是UPDATE配合LIMIT。在批量修复数据时,LIMIT可以限制每次更新的行数,避免一次锁大量行:
UPDATE user SET status = 1 WHERE status = 0 LIMIT 100;这条语句每次只更新100行,需要循环执行多次。这个写法在数据订正脚本中很实用,面试时提出来会加分。
2.3 int + 5:整数溢出和显示宽度不能混淆
热搜词里的“mysql中int+5”不是指某个固定面试题,而是围绕int类型的一组考察点。第一个坑是int(5)的显示宽度,第二个坑是整数运算溢出。
先看显示宽度。MySQL中int(5)的5并不是存储长度限制,而是显示宽度。INT类型固定占用4字节,取值范围是-2147483648到2147483647。无论写成int(5)还是int(11),能存储的数值范围都一样。加上ZEROFILL属性时,显示宽度才有意义:
CREATE TABLE t( num INT(5) ZEROFILL );插入123后,查询结果会显示00123。这里的5只是补零宽度,不影响存储范围。如果面试官问“int(5)能存多少位”,正确回答是存储范围由INT类型本身决定,5只是显示宽度。
再看整数溢出。int类型加减运算时,如果结果超出-2147483648到2147483647的范围,会产生溢出。例如:
SELECT 2147483647 + 5;在MySQL中,这个表达式的结果类型会升级为DECIMAL或BIGINT,所以不一定直接报错。但如果是在Java代码里用int接收数据库返回的结果,就会出现数值溢出或截断问题。Java的int同样是32位,范围与MySQL的INT一致。如果业务上可能超过这个范围,应该改用BIGINT,Java侧对应Long。
| 类型 | 存储字节 | 有符号范围 | Java对应类型 |
|---|---|---|---|
| TINYINT | 1 | -128 到 127 | byte / Byte |
| SMALLINT | 2 | -32768 到 32767 | short / Short |
| INT | 4 | -2147483648 到 2147483647 | int / Integer |
| BIGINT | 8 | -9223372036854775808 到 9223372036854775807 | long / Long |
面试答题时,建议把“显示宽度”和“存储范围”分开说,再补一句“业务中超过int范围的字段建议用BIGINT”,完整性会高很多。
2.4 ORDER BY排序:索引排序与filesort
ORDER BY是另一个热搜词,也是笔试常客。排序的底层有两种实现方式:利用索引直接排序,或者生成filesort(文件排序)。面试官主要考察你能不能从EXPLAIN结果里看出排序是否走索引。
先看基本用法:
-- 单字段升序 SELECT * FROM user ORDER BY create_time ASC; -- 单字段降序 SELECT * FROM user ORDER BY create_time DESC; -- 多字段排序:先按status升序,再按create_time降序 SELECT * FROM user ORDER BY status ASC, create_time DESC; -- 配合分页 SELECT * FROM user ORDER BY id DESC LIMIT 10;多字段排序时,排序优先级按字段从左到右排列。也就是说,只有当status相同时,才会继续按create_time排序。
判断排序是否走索引,用EXPLAIN:
EXPLAIN SELECT * FROM user ORDER BY create_time DESC;如果Extra列出现Using filesort,说明没有使用索引完成排序,而是在内存或磁盘上做了额外排序。数据量大时,filesort会显著拉高查询时间。
避免filesort的方法有两种。一种是给排序字段建索引,并且让排序顺序和索引顺序一致:
CREATE INDEX idx_create_time ON user(create_time);另一种是减少排序数据量,比如先用索引过滤掉大部分行,再对剩余数据排序:
SELECT * FROM user WHERE status = 1 ORDER BY create_time DESC LIMIT 20;这里要注意一个坑:对索引列使用函数或表达式后,索引会失效。例如:
SELECT * FROM user ORDER BY DATE(create_time) DESC;DATE(create_time)对字段做了函数运算,索引无法直接用于排序。正确做法是直接按create_time排序,或者在需要按日期分组时单独设计日期字段。
3. 第2天:索引、事务隔离级别和锁机制
索引、事务和锁是MySQL面试中区分度最大的部分。前一天的SQL是“手速题”,这一天的内容才是真正的“实力题”。面试官对这三个方向的要求不是“听说过”,而是“能讲清原理”。
3.1 B+树索引与最左前缀原则
MySQL InnoDB存储引擎使用B+树作为索引结构。B+树的特点是:非叶子节点只存索引值,叶子节点存完整数据或主键值;叶子节点之间通过双向链表连接,便于范围查询。
面试时不需要背整棵树的定义,但要能回答三个关键点:
- 为什么用B+树而不是二叉搜索树:因为B+树是多叉树,树的高度低,磁盘IO次数少。MySQL数据最终存在磁盘上,IO次数决定查询速度。
- 为什么叶子节点有序:因为叶子节点用链表串联,范围查询时不需要回树中间跳转。
- 聚簇索引和非聚簇索引的区别:InnoDB的聚簇索引叶子节点存整行数据,非聚簇索引叶子节点存主键值,查询时需要回表。
最左前缀原则是联合索引的必考题。假设建立联合索引:
CREATE INDEX idx_name_age ON user(name, age);这个索引实际上会按照name优先排序,再按age排序。因此:
-- 可以走索引 SELECT * FROM user WHERE name = '张三'; SELECT * FROM user WHERE name = '张三' AND age = 20; -- 无法走索引(跳过最左列) SELECT * FROM user WHERE age = 20;面试时的标准表述是:联合索引的查询条件必须从最左列开始,且不能跳过中间列。如果跳过了,后面的列无法走索引。
3.2 事务ACID和四种隔离级别
事务是并发环境下保证数据一致性的基础。ACID四个特性要能一句话说清:
| 特性 | 含义 | 破坏场景 |
|---|---|---|
| 原子性 Atomicity | 事务内所有操作要么全部成功,要么全部回滚 | 中途失败只执行一半 |
| 一致性 Consistency | 事务执行前后,数据满足所有约束 | 转账后总金额变化 |
| 隔离性 Isolation | 多个事务并发执行,互不干扰 | 脏读、不可重复读、幻读 |
| 持久性 Durability | 事务提交后,修改永久保存 | 数据库重启后数据丢失 |
MySQL的四种隔离级别解决不同并发问题:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED 读未提交 | 可能 | 可能 | 可能 |
| READ COMMITTED 读已提交 | 不会 | 可能 | 可能 |
| REPEATABLE READ 可重复读 | 不会 | 不会 | 可能(InnoDB通过间隙锁基本解决) |
| SERIALIZABLE 串行化 | 不会 | 不会 | 不会 |
脏读指读到其他事务未提交的数据;不可重复读指同一事务内两次读同一行,结果不一样;幻读指同一事务内两次执行同一查询,结果集行数不一样。
MySQL默认隔离级别是REPEATABLE READ,这一点要记住。Oracle默认是READ COMMITTED,两个数据库的默认值不同,面试官经常拿这个做对比题。
3.3 MVCC:快照读和当前读
MVCC(多版本并发控制)是InnoDB实现隔离级别的核心机制。它的思路是:不直接加锁阻塞读写,而是通过版本链保存数据的历史版本,让读操作读到一个一致性快照。
MVCC依赖两个隐藏列:trx_id(最近修改该行的事务ID)和roll_pointer(指向上一个版本的指针)。每次更新数据时,不是覆盖旧值,而是生成一个新版本,通过roll_pointer形成版本链。
快照读使用MVCC,普通的SELECT就是快照读,不需要加锁。当前读需要读取最新版本并加锁,SELECT ... FOR UPDATE、UPDATE、DELETE都是当前读。
面试时最常问的问题是:REPEATABLE READ下,普通SELECT为什么不会出现幻读?答案是快照读读取的是事务开始时的快照,后续事务插入的新行对这个快照不可见,所以同一事务内两次SELECT结果一致。但如果是当前读,比如SELECT ... FOR UPDATE,就需要配合间隙锁防止其他事务插入新行。
3.4 行锁、间隙锁和死锁排查
InnoDB支持行级锁,行锁又分为共享锁和排他锁。共享锁之间不互斥,排他锁和任何锁都互斥。SELECT ... LOCK IN SHARE MODE加共享锁,SELECT ... FOR UPDATE加排他锁。
间隙锁是InnoDB在REPEATABLE READ隔离级别下用来解决幻读的锁。它锁住的不是一个具体行,而是一个范围,阻止其他事务在这个范围内插入数据。
死锁是并发事务互相持有对方需要的锁时产生的。排查死锁的标准做法是执行:
SHOW ENGINE INNODB STATUS;在输出中找到LATEST DETECTED DEADLOCK段落,里面会显示两个事务分别持有哪把锁、等待哪把锁。面试中不需要完整复述这段输出,但要能说出排查思路:
- 查看死锁日志中的两个事务ID。
- 确认两个事务的加锁顺序。
- 检查业务代码中是否按相同顺序访问表和行。
- 通过调整SQL顺序、缩小事务范围、缩短持有锁的时间来避免死锁。
常见死锁场景是事务A先更新表1再更新表2,事务B先更新表2再更新表1,两者互相等待。解决方式是在业务层统一加锁顺序。
4. 第3天:存储引擎、日志体系、存储过程和生产级优化
第三天的内容更偏实用和体系化。前面两天解决的是“查询怎么写、事务怎么理解”,第三天解决的是“表怎么选型、数据怎么恢复、存储过程怎么写、线上SQL怎么优化”。
4.1 InnoDB和MyISAM关键差异
Java后端日常开发中,InnoDB是绝对主流,但面试官仍会拿MyISAM做对比题,考察你的选型意识。
| 对比项 | InnoDB | MyISAM |
|---|---|---|
| 事务支持 | 支持 | 不支持 |
| 锁粒度 | 行锁 | 表锁 |
| 外键支持 | 支持 | 不支持 |
| 聚簇索引 | 是 | 否 |
| 崩溃恢复 | 通过redo log恢复 | 恢复能力弱 |
| 适用场景 | 业务读写、事务场景 | 只读、报表、日志表 |
表格背下来很容易,关键是要能解释为什么InnoDB更适合业务系统。原因有三个:支持事务保证数据一致;支持行锁降低并发冲突;支持崩溃恢复避免数据丢失。MyISAM的优势是查询计数快,但在并发写入下锁冲突严重,所以生产环境很少使用。
4.2 redo log、undo log、binlog三兄弟
MySQL的日志体系是面试高频题,尤其是redo log和binlog的区别。三者职责不同:
| 日志 | 作用 | 产生位置 | 主要用途 |
|---|---|---|---|
| redo log | 记录物理页修改,保证事务持久性 | InnoDB存储引擎层 | 崩溃恢复,重做已提交事务的修改 |
| undo log | 记录数据的历史版本,用于回滚和MVCC | InnoDB存储引擎层 | 回滚事务,实现快照读 |
| binlog | 记录逻辑SQL操作 | MySQL Server层 | 主从复制、数据恢复 |
面试最常见的问题是:redo log和binlog有什么区别?标准答案包含四点:
- redo log是InnoDB特有的,binlog是MySQL Server层生成的。
- redo log是物理日志,记录“哪个页的哪个位置改成了什么”;binlog是逻辑日志,记录SQL语句或行变更。
- redo log是循环写的,binlog是追加写的。
- redo log用于崩溃恢复,binlog用于数据备份和主从复制。
补充一个扩展点:两阶段提交。事务提交时,redo log的写入分为prepare和commit两个阶段,binlog在其中间写入。这样设计是为了保证redo log和binlog的一致性,避免恢复数据时出现两边不一致。
4.3 存储过程实战:变量、循环和游标
存储过程在部分公司笔试中会出现,主要考察能否把多条SQL封装成一个可复用流程。先看一个最简单的存储过程:
DELIMITER // CREATE PROCEDURE proc_count_user() BEGIN DECLARE cnt INT DEFAULT 0; SELECT COUNT(*) INTO cnt FROM user; SELECT cnt AS total; END // DELIMITER ;创建后调用:
CALL proc_count_user();DECLARE cnt INT DEFAULT 0声明变量,SELECT COUNT(*) INTO cnt把查询结果赋值给变量,SELECT cnt输出结果。
循环和游标是存储过程的进阶用法。下面示例遍历用户表,给每个用户插入一条日志记录:
DELIMITER // CREATE PROCEDURE proc_create_user_log() BEGIN DECLARE done INT DEFAULT 0; DECLARE uid INT; DECLARE cur CURSOR FOR SELECT id FROM user; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; OPEN cur; read_loop: LOOP FETCH cur INTO uid; IF done = 1 THEN LEAVE read_loop; END IF; INSERT INTO user_log(user_id, create_time) VALUES (uid, NOW()); END LOOP; CLOSE cur; END // DELIMITER ;这段代码包含五个要点:
- 游标
cur绑定SELECT id FROM user的结果集。 done变量用于判断是否遍历结束。CONTINUE HANDLER FOR NOT FOUND在结果集取完后触发,将done置为1。LOOP与LEAVE组合实现循环退出。- 执行结束后必须
CLOSE cur释放游标。
需要注意,存储过程在生产环境使用要谨慎。它会把业务逻辑放入数据库,不利于代码维护和版本管理;而且存储过程的调试和性能分析比应用代码困难。面试时可以回答“会写,但不建议把复杂业务逻辑放进存储过程”,这个态度比单纯会写更受认可。
4.4 生产环境SQL慢查询优化案例
慢查询优化是“MySQL调优”类问题的落地场景。以下是一个典型示例,面试或实际排查都可以套用。
先开启慢查询日志:
-- 查看慢查询日志是否开启 SHOW VARIABLES LIKE 'slow_query_log'; -- 开启慢查询日志 SET GLOBAL slow_query_log = ON; -- 设置阈值:超过2秒的SQL会被记录 SET GLOBAL long_query_time = 2;然后定位到慢SQL,常见形式是深分页:
SELECT * FROM user ORDER BY id LIMIT 100000, 20;这条SQL的问题在于,MySQL需要先扫描100020行,然后丢弃前100000行,只返回20行。数据量大时,id排序本身不慢,慢的是前面100000行的无效扫描。
优化方式是用子查询先查出目标页的主键范围,再关联回原表:
SELECT u.* FROM user u INNER JOIN ( SELECT id FROM user ORDER BY id LIMIT 100000, 20 ) t ON u.id = t.id;子查询只扫描主键,把数据量从整行降到主键,再通过主键关联取回完整数据。这个优化在不改变业务语义的情况下,能显著减少扫描行数。
其他常见的慢查询优化手段包括:
- 避免
SELECT *,只查需要的字段,减少回表和传输开销。 - 避免在
WHERE条件中对字段做函数运算,否则索引失效。 - 分页查询优先基于主键或唯一索引定位。
- 大批量更新或删除时,分批执行,避免长事务和行锁长时间持有。
5. 面试官最爱追问的MySQL连环题
这一章模拟面试现场,把高频问题连起来。很多面试官喜欢先问一个简单问题,然后顺着你的答案不断深入,直到把你问住或者听到满意答案为止。
5.1 面试官问“MySQL怎么调优”时,按这个顺序答
“MySQL调优”是最开放的问题,也是最容易答得乱的问题。建议按“系统化流程”回答,展示你有完整排查思路:
- 先定位慢SQL:开启慢查询日志,找到执行时间超过阈值的SQL。
- 用
EXPLAIN分析执行计划,看type、key、rows、Extra字段。 - 检查索引:是否命中索引,是否失效,是否需要新建或调整联合索引。
- 优化SQL写法:去掉不必要的查询字段,避免函数运算,优化分页。
- 优化表结构:字段类型是否合理,是否过度冗余,是否需要拆分大字段。
- 上升到架构层面:数据量极大时考虑读写分离、分库分表、缓存。
每一步都要能举例。比如看到type=ALL说明全表扫描,看到Extra里有Using filesort说明排序没走索引。只背步骤不给例子,面试官会觉得你在背模板。
5.2 隔离级别为什么MySQL默认是RR
这个问题考察的是对默认配置和并发方案的理解。MySQL选择REPEATABLE READ作为默认,历史原因是为了兼容主从复制和binlog格式。在READ COMMITTED级别下,部分binlog格式无法正确记录行变更,导致主从数据不一致。随着MySQL 5.7和8.0版本演进,行格式的binlog已经能更好地支持READ COMMITTED,但默认值仍然保留为REPEATABLE READ。
回答时不要只说“历史原因”,要补充InnoDB在RR下的实现方案:通过MVCC解决快照读的幻读问题,通过间隙锁解决当前读的幻读问题。这样回答既有深度,又不会变成纯背诵。
5.3 字段类型设计和大表DDL
字段类型选择的常见考点如下:
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 主键 | BIGINT 自增 或 雪花ID | 空间小,性能高 |
| 用户状态 | TINYINT | 取值有限,节省空间 |
| 固定长度编码 | CHAR | 长度固定,无碎片 |
| 变长字符串 | VARCHAR | 根据实际长度存储 |
| 金额 | DECIMAL | 避免浮点精度问题 |
| 时间 | DATETIME 或 TIMESTAMP | 明确时区需求后选择 |
金额字段必须避免使用FLOAT或DOUBLE,否则会出现精度丢失。数据库层面用DECIMAL(10,2),Java侧用BigDecimal。
大表DDL是生产环境的难点。直接执行ALTER TABLE修改大表结构时,会长时间锁表,阻塞业务写入。生产环境通常使用在线DDL工具,比如gh-ost,在变更期间通过触发器或增量复制方式同步数据,减少锁表影响。面试时能提到“大表DDL不能直接在线上执行,需要评估锁表和回滚方案”,就已经体现了生产经验。
6. 3天自测清单和面试失分点复盘
面试准备的最后一步不是继续刷题,而是检查漏洞。下面这份清单覆盖了前面所有章节的核心问题,建议每天睡前对着清单自测,能不用看资料讲出来的才算掌握。
6.1 考前自测清单
| 分类 | 自测问题 | 是否掌握 |
|---|---|---|
| SQL基础 | 能写出单表增删改查、多表JOIN、聚合查询 | |
| UPDATE | 知道不加WHERE会更新全表,知道多表更新写法 | |
| int类型 | 能说清int(5)是显示宽度,int溢出后的处理 | |
| 排序 | 能通过EXPLAIN判断是否Using filesort | |
| 索引 | 能解释B+树结构、最左前缀、索引失效场景 | |
| 事务 | 能说出ACID、四种隔离级别、脏读/不可重复读/幻读区别 | |
| MVCC | 能解释快照读和当前读 | |
| 锁 | 能区分共享锁、排他锁、间隙锁,知道死锁排查命令 | |
| 存储引擎 | 能对比InnoDB和MyISAM | |
| 日志 | 能说出redo log、undo log、binlog的区别和两阶段提交 | |
| 存储过程 | 能写出包含变量、循环、游标的存储过程 | |
| 调优 | 能按定位慢SQL、EXPLAIN、优化索引、优化SQL的顺序回答调优 |
每项如果卡壳超过30秒,说明还没掌握,需要回到对应章节重看。
6.2 常见错误写法与正确写法对照
以下是从大量面试机试和实际项目中总结的失分点:
| 错误写法或错误现象 | 原因 | 正确做法 |
|---|---|---|
UPDATE user SET status = 1 | 漏写WHERE,全表更新 | 先SELECT确认目标行数,再加WHERE |
DELETE FROM user | 忘记加WHERE | 删除前备份数据,确认条件 |
int(5)字段插入100000失败困惑 | 误以为显示宽度限制存储范围 | 使用BIGINT处理大数 |
WHERE name = 123,name是VARCHAR | 隐式类型转换导致索引失效 | 统一传入字符串类型 |
ORDER BY DATE(create_time) | 对索引列使用函数 | 直接按原字段排序 |
SELECT * FROM table LIMIT 1000000, 10 | 深分页扫描大量无用行 | 使用子查询先定位主键 |
| 事务里执行耗时网络调用 | 长时间持有锁,增加死锁概率 | 事务外先获取数据,事务内只做必要更新 |
6.3 面试现场怎么组织语言
SQL题和原理题答题方式不同。SQL题先确认表结构,再写SQL,写完检查WHERE条件和字段名。原理题建议采用“结论 -> 原理 -> 例子 -> 边界”的结构。
以“解释MVCC”为例,参考回答结构:
- 结论:MVCC是InnoDB实现多版本并发控制的机制,让读不加锁也读到一个一致性快照。
- 原理:每行数据有隐藏的
trx_id和roll_pointer,更新时生成新版本,通过版本链保存历史。 - 例子:事务A开启后,事务B提交了修改,事务A再SELECT时只看到自己事务开始前的版本。
- 边界:MVCC解决的是快照读,如果是
SELECT ... FOR UPDATE这种当前读,MVCC不生效,需要锁机制配合。
这个结构的好处是,无论面试官从哪里打断追问,你都有下一步可讲。MySQL面试题数量很多,但底层知识是收敛的。只要把语法、索引、事务、锁、日志、优化这条主线吃透,绝大多数问题都能归到这条线上。3天时间足够完成一轮系统复习,剩下的就是在真实项目中不断验证和加深理解。