1. 面试官真正想问的:从来不是背参数,而是排查链路
1.1 为什么大多数人挂在第一步
我在准备MySQL调优面试的时候,最深的感受是:网上资料都在教“参数怎么调、索引怎么写”,但面试官真正想听的,往往不是这些散装知识点。
你去面Java后端或者DBA岗位,十有八九会遇到这样的问题:
- “线上有条SQL跑了3秒,你怎么处理?”
- “你们数据库最近CPU打满,你从哪查起?”
- “这张表500万数据,分页到第10万页巨慢,怎么优化?”
这些问题看着问法不同,内核其实是同一个:你能不能从“问题现象”出发,走完一条完整的排查链路,而不是张口就来“加索引、调buffer pool”。
我见过不少候选人,索引讲得头头是道,什么最左前缀、覆盖索引全都会背。结果一问“怎么发现这条SQL慢”,他愣了半天,说“开发报上来我才知道”。这种回答在面试里基本就凉了。因为MySQL调优面试,考的不是你知道多少参数,而是你在真实故障面前有没有一套稳定的操作顺序。
1.2 准备面试前,先建立这三层认知
第一层认知:调优是分层排查的,不是一把抓。
MySQL出问题,表面上看都是“慢”,但慢的原因可能差得很远。应用层可能是连接池耗尽、SQL并发太高;MySQL层可能是索引失效、锁等待、buffer pool命中率太低;OS层可能是CPU跑满、磁盘IO延迟飙高、Swap在频繁交换内存。
面试时能说出“先分清是哪个层的问题”,比直接报参数值高分得多。我通常按这个顺序梳理:先看机器负载,再查数据库内部状态,最后才落到SQL和索引。
第二层认知:调优本质是取舍,不存在万能配置。
一个很典型的例子,innodb_flush_log_at_trx_commit设为1,安全性最高,每次事务提交都要刷盘;设为2,性能更好,但宕机可能丢最后一秒日志。你告诉面试官“我生产环境一律双1(commit=1+sync_binlog=1)”,他会问你:“那你的写入性能瓶颈怎么解决?”如果你能答出“用批量提交、减少不必要事务、或者从业务上降低刷盘频次”,这才是真正的理解,而不是背参数。
第三层认知:不要只盯着MySQL,要盯着整个调用链路。
面试里经常出现“前端一操作,接口很慢,慢在哪”这种综合题。如果你能把问题拆成:网络耗时、应用线程阻塞、SQL执行耗时、主从延迟,一层层排除,面试官会认为你具备生产环境的全局视角。这一点在你入职后处理线上问题时会特别有用。
1.3 我面试前总结的一条排查主链路
给你一条可以直接背下来的链路,面试时照着说,至少不会乱:
- 确认现象:是某条SQL慢,还是整个库慢,还是某个接口慢。
- 看监控:CPU、IO、连接数、慢查询数量,先锁定大致方向。
- 开慢查询日志,或者查
performance_schema,捞出事SQL。 - 对慢SQL执行
EXPLAIN,看执行计划,重点看type、rows、Extra。 - 判断是索引问题、SQL写法问题,还是MySQL参数/硬件瓶颈。
- 做优化,改完用压测或线上灰度验证,对比前后耗时。
这条链路既能在面试里展示你的方法论,也能直接搬到工位上用。后面几章,我会把每个环节真正会考到的细节都过一遍。
2. 慢查询与执行计划:手撕现场的第一步
2.1 慢查询日志怎么开,开完怎么读
很多人面试被问“如何定位慢SQL”,只会答“开慢查询日志”,但具体怎么开、参数是什么,反而含糊。这块其实是送分题,你记住几个命令就够了。
-- 查看当前慢查询配置 SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time'; -- 临时开启(重启失效,生产环境慎用) SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1; SET GLOBAL log_queries_not_using_indexes = ON;注意几点:
long_query_time单位是秒,线上建议从 1 秒起调,一开始别设置0.1这种过于敏感的值,否则日志量爆炸。log_queries_not_using_indexes这个开关很好用,它能把没走索引的SQL也记下来,即使执行时间不到阈值。这招在面试里提出来,面试官会觉得你有实战经验,因为很多人不知道这个参数。- MySQL 8.0 默认慢查询日志是关闭的,需要先看配置再判断。
日志开了之后,原始文件不好看,一般用自带工具或者pt-query-digest分析。简单场景下,mysqldumpslow就够:
mysqldumpslow -s c -t 10 /var/lib/mysql/*-slow.log-s c表示按执行次数排序,-t 10取前10条。这样能快速抓到“哪些SQL是高频慢查询”,而不是被一两条偶发慢查询带偏。
2.2 EXPLAIN 九大字段,面试就考这几个
拿到慢SQL,下一步就是EXPLAIN。面试官大概率会挑几个字段问,你不需要背全部,但这几个必须张口就来:
| 字段 | 核心含义 | 面试怎么答 |
|---|---|---|
| type | 访问类型 | 从好到坏:const、eq_ref、ref、range、index、ALL。看到ALL就是全表扫描,重点怀疑对象 |
| key | 实际使用的索引 | 注意possible_keys有值不代表key一定用上 |
| key_len | 索引使用长度 | 可以判断联合索引到底用到了哪几列,面试加分项 |
| rows | 预估扫描行数 | 这个值越大越危险,是优化前后对比的核心指标 |
| Extra | 附加信息 | 看到Using filesort、Using temporary基本就是性能杀手;看到Using index是好事(覆盖索引);Using index condition说明用到了索引下推 |
还有一个容易被忽视的filtered,它表示经过索引过滤后,剩下的行占扫描行数的比例。比如rows=100000, filtered=1,说明要过滤掉99%的行,往往意味着索引选择得不够精准,还要回表查很多数据。
面试时如果能补充一句“key_len可以用来验证联合索引有没有用满”,会明显区别于只会背type的候选人。因为key_len的计算涉及字段类型长度、字符集、是否允许NULL,能讲清楚的人不多。
2.3 一次真实慢SQL定位过程
我拿一个简化案例演示完整过程。假设订单表orders有50万行,业务反馈下面这条查询很慢:
SELECT * FROM orders WHERE user_id = 123456 ORDER BY create_time DESC LIMIT 20;第一步,确认它有没有走索引。执行:
EXPLAIN SELECT * FROM orders WHERE user_id = 123456 ORDER BY create_time DESC LIMIT 20;发现type = ALL,rows = 500000,Extra里还有Using filesort。这就是经典的双重问题:没走索引做筛选,还因为ORDER BY create_time触发了文件排序。
第二步,建联合索引:
ALTER TABLE orders ADD INDEX idx_user_time (user_id, create_time);实际工作中,user_id是查询条件,create_time是排序字段,联合索引(user_id, create_time)既能让user_id等值匹配,又能让create_time按序读取,排序也就不需要filesort了。
第三步,再看执行计划,type变成ref,rows从50万降到几百,Extra里的Using filesort消失。这条SQL的耗时直接从秒级降到毫秒级。
这整个案例,从发现问题到定位、加索引、验证,顺序清晰。面试时按这个节奏讲,面试官能直接看到你的排查能力,而不是零散知识点的堆砌。
3. 索引优化:面试最值钱的十分钟
3.1 联合索引的最左前缀,得用B+树讲
索引部分几乎是MySQL面试的必考区,其中“最左前缀原则”被问到的概率最高。但很多人只会背结论:“查询条件必须从最左列开始”。面试官如果追问“为什么”,就卡住了。
我建议你这样理解:联合索引(a, b, c)在B+树里,先按a排序,a相同再按b排序,b相同再按c排序。所以查询能用到索引的条件是:从a开始连续匹配。
生活化类比就是查电话号码簿:先按姓氏排,再按名字排。你要找“张伟”,可以直接翻到“张”那一片,再在“张”里找“伟”;但如果你只知道名字叫“伟”,不知道姓什么,就只能在整本电话簿里翻,联合索引同理。
所以最左前缀具体能匹配的情况是这样的:
| 查询条件 | 能否用到联合索引 | 原因 |
|---|---|---|
| WHERE a = 1 | 能用 | 从最左列开始 |
| WHERE a = 1 AND b = 2 | 能用 | 连续匹配 a、b |
| WHERE a = 1 AND c = 3 | 部分使用 | 只用到了a,c用不上,因为中间断了b |
| WHERE b = 2 | 不能 | 没从a开始 |
这里有个容易被忽略的细节:MySQL 8.0 引入了索引跳跃扫描(Index Skip Scan),在某些情况下能跳过最左列使用索引。但面试里建议你先讲清楚最左前缀,再补充“8.0在某些场景下可以skip scan,但有条件限制,底层还是要扫描多个子区间”。这样既准确,又显得你有跟进新版本的习惯。
3.2 覆盖索引与索引下推,为什么总是一起出现
面试里还有个高频组合拳:回表、覆盖索引、索引下推。
先说过回表。普通二级索引,叶子节点存的是主键值;你执行SELECT *,先通过二级索引找到主键,再拿主键去聚簇索引查完整行,这个过程叫回表。回表次数多了,性能自然差。
覆盖索引就是“查询的列都在索引里”,不需要回表。比如SELECT user_id, create_time FROM orders WHERE user_id = 123,如果索引是(user_id, create_time),两个字段都在索引里,直接返回结果就行,Extra会显示Using index。
索引下推(ICP)很多人讲不清楚。它是指:MySQL 在存储引擎层,先用索引中的列做过滤,减少回表次数。没有ICP之前,是先从索引取出所有满足最左条件的记录,一个个回表,再在Server层做条件过滤。有了ICP,能在索引遍历时直接判断c是否满足条件,不满足就不回表。这是从MySQL 5.6开始支持的,默认开启。
面试时你可以顺带说一句:“覆盖索引是结果列层面的优化,索引下推是过滤条件层面的优化,两者都为了减少回表。”这句话虽短,但能证明你理解得比较透。
3.3 高频索引失效场景,一张表记牢
面试官几乎必问:“哪些情况会导致索引失效?”下面这几种你对照着记,每一个都能配一句解释:
| 失效场景 | 示例 | 为什么会失效 |
|---|---|---|
| 对索引列做函数操作 | WHERE DATE(create_time) = '2025-01-01' | 索引里存的是原值,不是函数处理后的值 |
| 隐式类型转换 | WHERE phone = 13800000000(phone是varchar) | 字符串列跟数字比较,MySQL会把列转换成数字,等于对列做了函数 |
| 模糊匹配以通配符开头 | WHERE name LIKE '%张%' | B+树只能按前缀匹配 |
| OR连接非索引列 | WHERE user_id = 1 OR status = 0(status无索引) | 必须回表合并,优化器权衡后可能放弃索引 |
| 对索引列做计算 | WHERE age + 1 = 18 | 索引里存的是原始值 |
| 联合索引不满足最左前缀 | WHERE b = 1(索引(a,b)) | 树结构决定 |
有一条需要特别提醒:NOT IN、!=很多时候会走全表扫描,但具体行为依赖优化器和统计信息,不绝对。面试时别一口咬死“一定失效”,可以说“通常会导致索引利用率很低,优化器可能放弃索引”。这个表达更严谨,显得你有真实调优经验。
3.4 冗余索引和低基数问题,面试加分项
进阶一点的候选人,会被问“索引是不是越多越好”。答案当然不是。每多一个索引,写入的时候就要多维护一棵B+树,插入、更新、删除全变慢,磁盘占用也会上升。
面试时你可以主动提两个实战经验:
- 检查冗余索引。比如已经存在
(a, b),又建了(a),后者基本就是冗余的,因为(a, b)已经能覆盖a单独作为前缀的所有场景。 - 关注基数(Cardinality)。索引选择性强不强,主要看列的区分度,比如“性别”这种只有几个值的列,基数很低,建索引帮助有限,还可能因为回表率太高反而变慢。
MySQL在生成执行计划时也会参考索引基数,所以如果一张表的统计信息不更新,优化器可能选错索引。实战中我们偶尔会执行ANALYZE TABLE刷新统计信息,这也是面试中能讲的细节。
4. 参数调优三件套:被追问最多的配置
4.1 第一件:innodb_buffer_pool_size,内存怎么给
说到MySQL参数调优,我习惯把最常见的三个归为“三件套”:innodb_buffer_pool_size(内存)、max_connections+wait_timeout(连接)、innodb_flush_log_at_trx_commit+sync_binlog(刷盘)。面试时点名这三组,基本就覆盖了90%的参数题。
先看内存。innodb_buffer_pool_size是InnoDB的缓冲池大小,相当于MySQL的数据缓存仓库。你要查的数据、要写的改动,都会先经过这里。这个值设得太小,页面频繁被淘汰,磁盘IO就上去了;设得太大,又可能影响操作系统本身的稳定性和其他进程。
我的经验是:如果服务器专门跑MySQL,物理内存在32G以上,可以给到总内存的60%到75%。注意,不是越高越好,别超过80%。因为除了buffer pool,还有innodb_log_buffer_size、各种会话级的sort_buffer_size、join_buffer_size、操作系统页缓存和其他进程都要分内存。
验证方法有两个:
- 看
SHOW ENGINE INNODB STATUS里的Buffer pool hit rate,长期低于99%,说明缓冲池可能偏小。 - 用
performance_schema看磁盘读和内存读的比例。
面试时提议“设成75%左右”,再补一句“要根据命中率微调”,就能展示你不是死记硬背。
4.2 第二件:max_connections 与 wait_timeout,连接风暴怎么治
max_connections是MySQL最大连接数,默认151(5.7)/151(8.0里也是151),这个值在生产环境一般不够用。很多团队直接调到1000甚至2000,但这里有个坑:每个连接都要占用线程和内存,连接数堆到几千,数据库CPU和内存一起报警。
面试题经常这么出:“突然有大量连接进来,数据库连不上,你怎么办?”
比较好的回答链路是:
- 先看
SHOW STATUS LIKE 'Threads_connected',确认是不是连接数打满。 - 看
SHOW VARIABLES LIKE 'max_connections'当前上限。 - 快速缓解可以临时调大
max_connections,或者重启应用缩小连接池。 - 根本解决要看应用层连接池是否合理,以及是否有慢查询占着连接不释放。
- 顺势把
wait_timeout调小,比如从28800秒调到60秒到300秒,让空闲连接尽快被回收。
这里有个技巧:wait_timeout和interactive_timeout是分开的,一个针对非交互连接,一个针对命令行交互连接。如果你只改了wait_timeout,可能对某些客户端不生效。面试时能说出这两个参数的区别,又是加分项。
4.3 第三件:刷盘策略,双一和安全性的取舍
innodb_flush_log_at_trx_commit是典型的取舍题。它有三个取值:
| 取值 | 行为 | 安全性 | 性能 |
|---|---|---|---|
| 1 | 每次事务提交都把redo log刷到磁盘 | 最高,最多丢已提交事务?不,最安全 | 最慢 |
| 2 | 每次提交只写到OS缓存,每秒刷盘 | 实例崩溃不丢,机器断电可能丢1秒 | 较快 |
| 0 | 每秒刷盘,提交时不主动刷 | 可能丢1秒多数据 | 最快 |
生产环境的标准建议是:commit=1且sync_binlog=1,也就是常说的“双1”。这样事务提交时,redo log和binlog都强制刷盘,保证不丢事务。面试如果问“为什么MySQL双1也慢”,你要能回答:因为每次提交都要等磁盘写入完成,刷盘次数多,延迟自然高。
如果你的业务可以接受极端情况下的少量数据丢失,又想提升性能,才考虑把commit调成2。千万别在支付、订单这类场景下调成0,丢了数据就事故了。
顺带提一句:sync_binlog影响的是binlog落盘策略,如果为1,每次事务提交都刷binlog;如果为0或N,则由系统决定刷盘频率。双1模式下,主从环境的数据一致性更有保障。面试问到主从复制时,把“双1”和半同步复制联系起来,会显得很专业。
4.4 那些容易被追问的小参数
除了三件套,面试偶尔会追问一些“会话级”参数,比如sort_buffer_size、join_buffer_size、tmp_table_size。这里有个大坑:这些参数是每个会话单独分配的,不是全局共享一份。你把它们调得很大,100个并发连接就乘100倍,内存瞬间爆掉。
正确理解是:它们通常不用调太大,sort_buffer_size默认256KB起步,一般没必要超过2M。tmp_table_size如果太小,临时表会从内存转到磁盘,导致SQL变慢;但调太大又可能让大量会话同时占用内存。面试时能说出“会话级参数不能盲目全局调大”这个点,比死记参数值有用得多。
5. SQL改写与连接池:笔试与场景题高发区
5.1 深分页优化:延迟关联和游标
后端面试手写SQL优化,最经典的场景就是深分页。假设你执行:
SELECT * FROM orders WHERE user_id = 1 ORDER BY create_time DESC LIMIT 100000, 20;这条SQL的问题在于:MySQL 要先把前10万行全查出来,再扔掉,只留下最后的20行。扫描10万行,哪怕有索引也一样慢。
常见优化方案有三种:
方案一,延迟关联:
SELECT t.* FROM orders t JOIN ( SELECT id FROM orders WHERE user_id = 1 ORDER BY create_time DESC LIMIT 100000, 20 ) tmp ON t.id = tmp.id;内层子查询只查主键id,走覆盖索引,不需要回表。得到20个id后,再用这20个id去关联查询整行,回表次数从10万次降为20次。
方案二,游标方式(seek method):
SELECT * FROM orders WHERE user_id = 1 AND create_time < '2025-01-01 00:00:00' ORDER BY create_time DESC LIMIT 20;这适合“加载更多”的场景,把上一页最后一条记录的排序字段值传进来,直接定位到那一页之后的数据。页越深,优势越大,因为不需要跳过大量数据。
方案三,业务上限控制。很现实的一点:没有用户会翻到第1万页。产品层面直接限制最大查询页数,或者改成“下一页”式游标,往往比SQL优化更彻底。面试时说“很多深分页问题应该从业务上规避”,这个回答会很加分。
5.2 连接池设置:别让数据库被连接数淹没
连接池是应用层和MySQL之间的桥梁,也是面试里“Redis/MQ/MySQL综合场景”常出现的考点。最常被问的是:HikariCP的maximumPoolSize设多大合适?
理论上有个经验公式:
连接数 = 核心线程数 × 2 + 有效机械硬盘数(+ 1)
这个公式来自HikariCP官方文档,但它描述的是理想情况。我在实战中更倾向于按“目标QPS × 单请求平均耗时”来估算,再结合压测调整。
举例:你的接口平均耗时100ms,目标QPS是500,那并发在途请求约50个。50个并发请求如果都打到DB层面,连接池给到50到80就够,没必要设500。把连接池设得过大,反而会让MySQL维护大量空闲连接,Threads_connected虚高,一旦遇到慢SQL,所有连接全被占住,数据库直接“假死”。
这里有个联动关系:应用连接池大小 + 服务实例数量,不能超过MySQL的max_connections。比如MySQL上限500,你有10个服务实例,那单实例连接池上限就得控制在40到50以内,否则高峰期直接把数据库打死。面试能讲清楚这个“双上限”关系,说明你真的处理过生产问题。
5.3 count、order by、group by 的隐藏考点
这几类SQL的优化细节,面试很喜欢出。
COUNT(*)在MyISAM里有特殊优化,但InnoDB没有,因为InnoDB要按行实时统计。如果你真的需要频繁统计大表行数,可以引入计数表或者用缓存,但要注意一致性问题。另外COUNT(1)和COUNT(*)在现代版本里性能差别不大,面试不要纠结这种细枝末节,反而显得外行。
ORDER BY优化的关键就是避免Using filesort。利用联合索引让排序字段有序,是最常见的解法。但如果排序字段和WHERE条件字段不在同一个索引里,或者排序方向不一致,优化器还是可能选择临时排序。
GROUP BY的优化思路类似:如果能利用索引分组,就不需要Using temporary。否则分组数据量大会在临时表里操作,内存临时表放不下还会转磁盘。面试时能说一句“group by本质是先排序后分组,索引支持能省掉临时表”,就够有深度了。
6. 锁、事务与隔离级别:压轴题专区
6.1 锁类型:从表锁到行锁,一张图记概念
MySQL调优面试后半段,基本会进入锁和事务的“深水区”。这部分概念多,但高频考点很集中。
从锁粒度看,有表级锁和行级锁。InnoDB支持行锁,但要注意:行锁不是直接锁在数据行上的,而是锁在索引记录上。这意味着“没有索引的表,行锁可能退化为表锁”——因为无法定位到具体记录,只能锁全表。这句话面试说出来,效果很好。
从读写性质看,有共享锁(S锁,读锁)和排他锁(X锁,写锁)。S锁之间兼容,S锁和X锁互斥,X锁和X锁互斥。加锁方式也有两种:SELECT ... LOCK IN SHARE MODE加共享锁,SELECT ... FOR UPDATE加排他锁。
InnoDB还引入了意向锁(IS、IX),它是一种表级锁,用来表示“事务准备对某些行加S锁/X锁”。它存在的意义是:让表级锁和行级锁之间无需逐行检查就能快速判断是否冲突。
再进阶一点:行锁里面还分为记录锁(Record Lock,锁单条记录)、间隙锁(Gap Lock,锁一个范围,但不锁记录本身)、临键锁(Next-Key Lock,前两者结合,锁范围加记录本身)。RR隔离级别默认使用Next-Key Lock,这是为了解决幻读问题。
6.2 MVCC 与隔离级别:RR为什么默认,RC为什么常见
MVCC(多版本并发控制)是InnoDB的核心机制。它靠undo log里的版本链,加上ReadView来实现不同隔离级别下的快照读。
简单理解:每行数据被修改时,都会在undo log里留下旧版本,形成一个版本链。事务执行快照读时,会生成一个ReadView,用来判断“当前事务能看到哪些版本”。
两个关键差异:
- 在RC(读已提交)下,每次快照读都会生成新的ReadView,所以能读到别的事务刚提交的数据。
- 在RR(可重复读)下,事务内第一次快照读生成ReadView后,后面一直复用这个ReadView,所以同一个事务里,多次读取的结果保持一致。
MySQL默认隔离级别是RR,很多人以为它就是“防幻读”。这里有个坑:RR的防幻读只对快照读生效,如果是当前读(比如SELECT ... FOR UPDATE),仍然可能通过Next-Key Lock锁住范围来防止幻读,但如果你的查询条件没有索引,锁范围可能很大,并发会明显下降。
面试时如果被问“为什么很多大厂把隔离级别改成RC”,你可以说:RC的锁冲突更少,因为不需要长时间持有间隙锁,并发能力更强;RR则一致性更好,但更容易出现锁等待。实际选型要看业务对一致性的要求,没有绝对标准。
6.3 一个死锁案例的复盘
死锁几乎是必考题。经典的死锁场景是两个事务互相持有对方需要的锁。我用一个简化案例:
-- 事务A UPDATE orders SET status = 1 WHERE id = 1; UPDATE orders SET status = 2 WHERE id = 2; -- 事务B(和A并发执行) UPDATE orders SET status = 3 WHERE id = 2; UPDATE orders SET status = 4 WHERE id = 1;如果事务A先锁了id=1,事务B先锁了id=2,然后A想去锁id=2时发现被B持有,B想去锁id=1时发现被A持有,两边都等对方释放,死锁就产生了。
排查死锁的办法,生产环境里我常用的:
- 先看错误日志,死锁发生时MySQL会记录相关事务信息。
- 执行
SHOW ENGINE INNODB STATUS,重点看LATEST DETECTED DEADLOCK段,里面会列出两个事务各自执行的SQL和持有/等待的锁。 - 打开
innodb_print_all_deadlocks=ON,把死锁信息都记录到error log里,方便事后复盘。
解决死锁的思路也简单:让所有事务按相同的顺序访问资源,比如都先更新id小的行,再更新id大的行,能有效降低死锁概率。另外,缩小事务范围、尽快提交,也能减少锁持有时间。
7. 实战中的三个坑,以及面试话术建议
7.1 坑一:一次性改太多参数,出了问题无从排查
我在刚接手一个项目时,犯过一个典型错误:因为线上慢,我一次性调了buffer pool、max_connections、刷盘策略、临时表大小整整四个参数。改完确实快了,但过了一周出现一个间歇性性能抖动,我完全无法判断是哪个参数导致的,只能一个个回滚排查,白白折腾很久。
后来我的习惯是:每次只改一个参数,记录改动时间和前后指标。面试讲这个坑,能很自然地引出你的方法论:调优是一个“假设—验证—回滚”的循环,不是拍脑袋。
7.2 坑二:只看执行计划,没看统计信息
有次我发现一条SQL,EXPLAIN显示它走了索引,rows只有几十行,但线上就是慢。查了半天才发现:优化器根据统计信息判断这条路最快,但统计信息已经过期,实际要扫描的数据量远超预期。执行一句ANALYZE TABLE刷新统计信息,问题立刻缓解。
这个案例说明:执行计划不是真理,它只是优化器基于统计信息做的“猜测”。面试时主动提这个点,能看出你做过真实调优,而不是只会在本地Explain跑一遍。
7.3 坑三:把所有内存都塞给Buffer Pool
服务器64G内存,有人直接把buffer pool设成60G,觉得“反正MySQL是主角”。结果系统内存不足,触发OOM,整库被操作系统杀掉。教训就是:buffer pool再重要,也要给OS和其他进程留余地。我后来习惯用SHOW ENGINE INNODB STATUS频繁观察命中率,命中率已经接近100%的时候,加内存就是浪费,反而该去优化SQL本身。
7.4 面试话术建议
最后说点实际的:面试回答问题,别急着报结论。先给思路,再落参数。比如问“连接数打满怎么处理”,你可以说“我先看Threads_connected确认是否真的打满,再看是应用连接池太大还是慢查询占着连接,然后再决定调max_connections还是优化SQL”。这个顺序说完,面试官自然会觉得你有生产级的问题处理能力。
MySQL调优面试,说到底是考“链路思维”和“取舍判断”。你不需要把每个参数背到小数点后两位,但你需要能清晰地讲出:问题在哪一层、怎么定位、为什么这么优化、有没有副作用。把本文这条链路吃透,再去面试,你至少不会在“慢SQL怎么查”这种送分题上翻车。