1. 为什么有时候建了索引查询还是慢?
举例:
SELECT * FROM users WHERE age + 1 = 30;
场景:索引失效,因为对字段做了计算。
解决方法:避免在索引列上做函数或运算,改为
WHERE age = 29
。
追问链条
追问:MySQL 索引失效常见有哪些情况?
回答:对索引列做函数或运算
使用隐式类型转换(int 列用字符串条件)
使用
or
条件,其中部分列无索引like 前导
%
查询范围过大,优化器判断全表扫描更快
追问:explain 中
rows
和filtered
怎么理解?
回答:rows
是预计扫描的行数,filtered
是过滤后保留的比例。rows * filtered
≈ 实际处理的行数。追问:如果索引没生效,你如何排查?
回答:先用explain
看执行计划,再analyze table
更新统计信息,如果优化器仍选错,可以用force index
。
2. 什么是索引覆盖查询?为什么更快?
举例:
SELECT name, age FROM users WHERE id = 100;
场景:联合索引
(id, name, age)
覆盖了查询字段,不用回表。解决方法:建立覆盖索引,提高查询性能。
追问链条
追问:explain 结果里
Using index
代表什么?
回答:表示索引覆盖查询,数据直接从索引里获取,不用回表。追问:覆盖索引和普通索引有什么本质区别?
回答:普通索引需要回表获取数据;覆盖索引存储了查询所需字段,可直接返回结果。追问:如果表字段很多,哪些列适合放到覆盖索引?
回答:经常出现在 where、order by、group by、select 子句里的列,但要控制索引大小,避免过度索引。
3. 为什么 count(*) 很慢?如何优化?
举例:
SELECT COUNT(*) FROM big_table;
场景:大表全表扫描。
解决方法:
使用 Redis 缓存近似值
使用分区表分区统计
InnoDB 下
COUNT(主键)
与COUNT(*)
效果一致
追问链条
追问:MyISAM 和 InnoDB 的
COUNT(*)
有何区别?
回答:MyISAM 直接存储了总行数,COUNT(*)
很快;InnoDB 必须逐行扫描,性能差。追问:Redis 里存储计数值,如何保证和数据库一致?
回答:可以通过事务更新时同步更新 Redis;或者异步校正(定时批量刷新)。追问:如果必须实时准确统计,你会怎么做?
回答:用数据库事务内维护计数表,或者利用 binlog 同步到统计系统实时计算。
4. MySQL 中 limit 偏移量大时性能问题
举例:
SELECT * FROM orders LIMIT 1000000, 20;
场景:偏移量大时,MySQL 仍会扫描前 N 行。
解决方法:
使用
WHERE id > ? LIMIT ?
游标翻页延迟关联优化:先查主键,再 join 回表
追问链条
追问:什么是延迟关联?
回答:先用索引查出主键集合,再通过主键 join 原表获取数据,减少扫描。追问:MySQL 8.0 窗口函数能优化分页吗?
回答:可以用ROW_NUMBER()
或NTILE()
来分页,但依然受限于排序开销。追问:前端无限滚动分页时你会怎么做?
回答:使用游标分页(基于 id 或时间戳),避免大 offset。
5. explain 执行计划中 type = ALL 意味着什么?
举例:
EXPLAIN SELECT * FROM users WHERE name = 'Tom';
场景:type=ALL 表示全表扫描。
解决方法:加索引,避免全表扫描。
追问链条
追问:type 的执行方式从好到差怎么排?
回答:system > const > eq_ref > ref > range > index > ALL。追问:explain 中 possible_keys 和 key 有何区别?
回答:possible_keys 是可用索引,key 是实际使用索引。追问:如果优化器选择了错误索引怎么办?
回答:可以用force index
指定索引,或者通过analyze table
更新统计信息来修正。
6. 为什么 MySQL 有时不走索引而走全表扫描?
举例:
SELECT * FROM users WHERE name LIKE '%abc';
场景:前导
%
,索引失效。解决方法:
改写成
LIKE 'abc%'
建 FULLTEXT 索引
使用外部搜索引擎(ES)
追问链条
追问:索引失效的其他原因有哪些?
回答:函数运算、隐式转换、or 未优化、范围查询后索引失效。追问:FULLTEXT 索引和普通索引的区别?
回答:FULLTEXT 用倒排索引存储词项,更适合模糊搜索。追问:如果查询条件必须模糊匹配
%abc%
,如何优化?
回答:使用倒排索引(ES)或 trigram 索引,MySQL 自身不适合处理这种场景。
7. 为什么 in 查询过长会变慢?
举例:
SELECT * FROM users WHERE id IN (1,2,3,...100000);
场景:in 列表过长,优化器难以处理。
解决方法:
使用临时表 join
分批查询
追问链条
追问:in 和 exists 的区别?
回答:in 先查子查询再匹配,exists 逐行判断子查询结果,优化器会自动转换。追问:in 列表多少个元素以内性能还可以?
回答:一般几百个以内影响不大,几千上万性能就很差。追问:如果前端传一个几万 id 的数组怎么办?
回答:写入临时表,用 join 方式查询,效率更高。
8. join 查询为什么会慢?
举例:
SELECT * FROM users u JOIN orders o ON u.id=o.user_id;
场景:join 字段没索引,或者两个大表 join。
解决方法:
给 join 字段建索引
小表驱动大表
追问链条
追问:什么是小表驱动大表?
回答:MySQL 优化器会选择小结果集的表做驱动表,减少循环次数。追问:join 的索引建在哪个表?
回答:建在被驱动表的关联字段上。追问:两个 1 亿行大表 join 怎么办?
回答:分批 join、用中间表存结果,或拆到离线数仓做计算。
9. MySQL 中的回表问题
举例:索引
(id, name)
,查询SELECT * FROM users WHERE id=10;
场景:索引不覆盖所有列,需要回表。
解决方法:建覆盖索引,或者只查需要字段。
追问链条
追问:回表为什么慢?
回答:需要通过二级索引找到主键,再去主键索引取数据,增加 I/O。追问:什么时候可以避免回表?
回答:使用覆盖索引,或者只查询索引里的列。追问:联合索引如何设计避免回表?
回答:尽量把 where 和 select 用到的列放进索引里。
10. explain 里的 rows 和 filtered 代表什么?
场景:rows 表示预计扫描的行数,filtered 表示过滤比例。
解决方法:关注
rows*filtered
,判断优化空间。
追问链条
追问:rows 值是如何估算的?
回答:基于索引统计信息,可能不准。追问:filtered 小说明什么?
回答:说明条件过滤效果不好,大量行被丢弃,索引利用率低。追问:如何让 rows 和 filtered 更准确?
回答:执行analyze table
更新统计信息。
11. MySQL 中的联合索引为什么遵循最左前缀匹配?
举例:索引
(name, age, city)
,查询WHERE name='Tom' AND age=20
能用上,但WHERE age=20
用不了。场景:索引匹配规则决定了必须从最左边字段开始匹配。
解决方法:建索引时根据查询条件的常用顺序来设计。
追问与回答:
问:如果查询
WHERE name LIKE 'Tom%'
,能走索引吗?
答:可以,因为前缀匹配仍然符合最左原则。问:如果查询
WHERE age=20 AND city='BJ'
,会用索引吗?
答:不会,因为 name 缺失,破坏了最左前缀。问:如何解决这种索引利用率低的问题?
答:可以调整索引顺序,或者用多个单列索引结合 Index Merge。
12. 什么是索引下推优化(Index Condition Pushdown, ICP)?
举例:
SELECT * FROM users WHERE age > 20 AND name LIKE 'Tom%';
场景:MySQL 5.6+ 会把
age > 20
这样的条件下推到存储引擎层过滤,减少回表次数。解决方法:升级 MySQL 版本,合理利用 ICP 优化。
追问与回答:
问:怎么判断查询用了 ICP?
答:执行计划里 Extra 字段会显示Using index condition
。问:ICP 在什么情况下没用?
答:当查询列不在索引里时,MySQL 仍需回表。问:ICP 和覆盖索引能一起用吗?
答:可以,但覆盖索引通常比 ICP 更高效。
13. 为什么大批量 insert/update/delete 会导致性能下降?
举例:一次性
DELETE FROM orders WHERE status='closed';
删除 1000 万行。场景:大量行锁、redo log、undo log 导致 I/O 压力大。
解决方法:
分批操作,每次 1000 或 10000 条
使用
pt-archiver
工具做归档
追问与回答:
问:大批量删除时如何避免 binlog 太大?
答:分批提交,或者临时关闭 binlog(非主从环境)。问:update 为什么会锁表?
答:当 where 条件缺乏索引,行锁会退化为表锁。问:delete 太慢还能怎么优化?
答:逻辑删除(打标记),配合定期归档。
14. InnoDB 和 MyISAM 的区别?
举例:同样的
SELECT COUNT(*) FROM table;
,MyISAM 直接读元数据,InnoDB 需要扫描。场景:不同引擎适合不同业务。
解决方法:一般使用 InnoDB,因为支持事务和行锁。
追问与回答:
问:为什么 MySQL 8.0 不再支持 MyISAM?
答:因为缺乏事务、崩溃恢复差,官方逐步弃用。问:什么时候还会用 MyISAM?
答:只读、临时统计类场景(历史上常见,现在少用)。问:如果我需要全文索引怎么办?
答:InnoDB 在 MySQL 5.6+ 也支持全文索引,不必依赖 MyISAM。
15. order by 为什么会触发 filesort?
举例:
SELECT * FROM users ORDER BY age;
没有 age 索引时。场景:无法利用索引顺序时,MySQL 需要额外排序。
解决方法:
建立合适的索引
避免大数据集排序
追问与回答:
问:filesort 的单路和双路算法区别?
答:单路算法直接取排序列和查询列,双路需要先排序再回表。问:能否用覆盖索引避免 filesort?
答:可以,只要排序列和 select 列都在索引里。问:group by 和 order by 在执行计划里有啥区别?
答:group by 本质上会隐含排序,除非group by ... order by null
。
16. 为什么 join 查询会很慢?
举例:
SELECT * FROM users u JOIN orders o ON u.id = o.user_id;
场景:大表 join,驱动表和被驱动表选择错误。
解决方法:
小表驱动大表
给 join 字段加索引
追问与回答:
问:什么是小表驱动大表?
答:让行数少的表作为驱动表,减少 join 次数。问:join 时索引建在主表还是从表?
答:一般建在被驱动表(右表)的关联字段。问:两个 1 亿行大表 join 怎么办?
答:先分区/分表,或考虑中间结果写入临时表,再 join。
17. MySQL 如何避免幻读?
举例:事务 A
SELECT * FROM users WHERE age > 20;
,事务 B 插入一条age=25
,事务 A 再次查询时看到新数据。场景:幻读问题。
解决方法:InnoDB 在 RR 隔离级别下用 Next-Key Lock 避免幻读。
追问与回答:
问:RC 隔离级别下会不会有幻读?
答:会,因为只加行锁,不加 gap 锁。问:gap lock 是什么?
答:锁定索引区间,阻止插入。问:MySQL 默认隔离级别是什么?
答:RR(Repeatable Read,可重复读)。
18. 什么是死锁?如何解决?
举例:事务 A 锁住 row1,再等 row2;事务 B 锁住 row2,再等 row1。
场景:多个事务交叉锁定,形成循环依赖。
解决方法:
保持一致的加锁顺序
设置事务超时
追问与回答:
问:InnoDB 如何检测死锁?
答:等待图(Wait-for Graph),发现循环依赖时回滚一个事务。问:怎么避免死锁?
答:固定顺序锁资源、尽量缩短事务时间。问:遇到死锁你会怎么排查?
答:查看SHOW ENGINE INNODB STATUS\G
,分析死锁日志。
19. explain 中 key_len 的含义是什么?
举例:
EXPLAIN SELECT * FROM users WHERE id=100;
,key_len=4
。场景:表示索引使用的字节数。
解决方法:通过 key_len 可以判断索引是否被完整利用。
追问与回答:
问:如果 key_len=8,说明什么?
答:用了索引里 8 个字节,比如 BIGINT 类型。问:能否通过 key_len 判断联合索引用到几列?
答:可以,不同列累加。问:possible_keys 和 key 的区别?
答:possible_keys 表示可能用的索引,key 表示实际用的索引。
20. 分区表有哪些使用场景?
举例:日志表
log_2023
、log_2024
按时间分区。场景:大表数据按时间或范围划分,提高查询效率。
解决方法:使用 RANGE、LIST、HASH 分区。
追问与回答:
问:分区表和分表的区别?
答:分区表是逻辑上一个表,物理上多份;分表是多个独立表。问:分区表的限制有哪些?
答:不支持外键,分区键必须出现在所有唯一索引里。问:分区表对 count(*) 有加速吗?
答:如果 where 条件能命中分区,可以减少扫描数据。
21. 大表 alter table 为什么很慢?
举例:
ALTER TABLE users ADD COLUMN age INT;
在亿级表上执行。场景:需要复制表结构和数据,阻塞 DML。
解决方法:
使用 Online DDL(MySQL 5.6+ 支持)
使用 pt-online-schema-change 工具
追问与回答:
问:什么是 Online DDL?
答:DDL 执行期间允许读写,避免长时间锁表。问:MySQL 哪些 alter 操作支持 Online DDL?
答:如新增列、创建索引,多数支持,但删除列不支持。问:不停机情况下如何加索引?
答:使用 Online DDL 或 pt-online-schema-change。
22. 主从复制延迟的原因?
举例:主库写入压力大,从库同步延迟。
场景:高并发写操作导致延迟。
解决方法:
使用半同步复制
增加从库,提高并行复制能力
追问与回答:
问:MySQL 5.7 的并行复制怎么实现的?
答:按库并行,8.0 支持按事务组并行。问:如何监控复制延迟?
答:SHOW SLAVE STATUS
里的Seconds_Behind_Master
。问:如何减少延迟?
答:优化主库写入,或使用多线程复制。
23. 如何优化 select * 查询?
举例:
SELECT * FROM users;
场景:会扫描所有字段,增加 I/O。
解决方法:只取必要字段,使用覆盖索引。
追问与回答:
问:为什么 select * 慢?
答:返回多余字段,可能导致回表。问:select * 对于 ORM 框架有什么影响?
答:容易加载过多数据,导致 N+1 问题。问:什么时候 select * 可以接受?
答:临时调试,或小表查询。
24. delete 和 truncate 的区别?
举例:
DELETE FROM users;
vsTRUNCATE TABLE users;
场景:清空表数据的两种方式。
解决方法:按需选择。
追问与回答:
问:delete 会触发事务和触发器吗?
答:会;truncate 不会。问:truncate 会释放空间吗?
答:会重置自增 ID 并释放空间。问:在 binlog 里,delete 和 truncate 有什么区别?
答:delete 逐行记录,truncate 是 DDL。
25. 事务中的脏读、不可重复读和幻读的区别?
举例:
脏读:读到未提交数据
不可重复读:前后两次读结果不同
幻读:前后两次读,行数不同
场景:事务并发读写导致问题。
解决方法:通过不同隔离级别控制。
追问与回答:
问:MySQL 默认隔离级别能避免哪几种?
答:RR 能避免脏读、不可重复读,避免大多数幻读。问:哪种隔离级别性能最好?
答:READ UNCOMMITTED,几乎无加锁,但问题最多。问:Serializable 怎么实现的?
答:强制所有事务串行执行,锁粒度最大。
26. MySQL 为什么建议使用自增主键?
举例:
CREATE TABLE orders (id BIGINT AUTO_INCREMENT PRIMARY KEY, ...);
场景:自增主键能保证数据有序插入,减少页分裂。
解决方法:推荐使用自增主键,避免业务字段作为主键。
追问与回答:
问:UUID 做主键会有什么问题?
答:UUID 无序,插入会导致页分裂、随机 I/O。问:主键为什么建议用 BIGINT?
答:可支持更大范围,避免自增 ID 溢出。问:是否适合用雪花算法替代自增 ID?
答:可以,但要保证有序性,否则索引性能会下降。
27. 为什么不要在 where 条件中对列使用函数?
举例:
WHERE DATE(create_time) = '2023-09-01';
场景:会导致索引失效。
解决方法:改为
WHERE create_time >= '2023-09-01' AND create_time < '2023-09-02'
。追问与回答:
问:
WHERE YEAR(create_time)=2023
为什么不走索引?
答:因为索引存的是完整字段值,函数破坏了匹配。问:能否通过函数索引解决?
答:MySQL 8.0+ 支持函数索引,可以优化。问:如果不能修改 SQL,怎么办?
答:可以通过生成列(Generated Column)+ 索引。
28. 为什么说 select count(1) 和 count(*) 性能差不多?
举例:
SELECT COUNT(*) FROM users;
vsSELECT COUNT(1) FROM users;
场景:MySQL 内部优化后,两者几乎一样。
解决方法:统一使用 count(*),语义更清晰。
追问与回答:
问:count(column) 和 count() 有什么区别?
答:count(column) 会忽略 NULL,count() 会统计所有行。问:InnoDB 的 count(*) 为什么慢?
答:没有存储总行数,需要扫描。问:如果只要近似值怎么办?
答:用 show table status 或 Redis 计数器。
29. 为什么 MySQL 的 like '%abc' 不能走索引?
举例:
WHERE name LIKE '%Tom'
场景:前缀不确定,B+Tree 无法利用索引。
解决方法:
LIKE 'Tom%'
可以走索引使用全文索引或 ES
追问与回答:
问:
LIKE 'Tom%'
为什么能用索引?
答:因为前缀确定,可以从索引树定位。问:InnoDB 支持全文索引吗?
答:支持,5.6+ 开始提供。问:大规模模糊搜索怎么办?
答:考虑 Elasticsearch 等搜索引擎。
30. 如何优化慢查询?
举例:某查询
SELECT * FROM orders WHERE user_id=123 AND status='done';
执行 10 秒。场景:索引缺失或执行计划不佳。
解决方法:
开启慢查询日志
用 explain 分析
调整索引或 SQL
追问与回答:
问:如何开启慢查询日志?
答:SET GLOBAL slow_query_log=1;
问:explain 中
rows=1000000
说明什么?
答:预估扫描行数大,说明索引不合适。问:如果索引优化后仍慢,怎么办?
答:考虑缓存、分库分表或改用 ES。
31. 为什么 in 查询比 join 慢?
举例:
WHERE id IN (SELECT id FROM ...)
场景:in 可能导致子查询重复执行。
解决方法:改写为 join 或 exists。
追问与回答:
问:in 和 exists 的区别?
答:in 适合小表驱动大表,exists 适合大表驱动小表。问:优化器会自动把 in 转为 join 吗?
答:有时会,但不稳定。问:什么时候 in 更优?
答:当 in 集合较小、且有索引时。
32. 为什么 update 会锁全表?
举例:
UPDATE users SET age=20 WHERE name LIKE '%Tom%';
场景:where 条件没用索引,锁退化为表锁。
解决方法:加索引,避免无索引更新。
追问与回答:
问:InnoDB 默认是行锁还是表锁?
答:行锁,但缺乏索引时会退化为表锁。问:delete 会不会全表锁?
答:同理,如果没有索引,会锁全表。问:如何避免大范围锁表?
答:加索引,或分批更新。
33. 为什么 MySQL 不建议使用外键?
举例:
ALTER TABLE orders ADD CONSTRAINT fk_user FOREIGN KEY(user_id) REFERENCES users(id);
场景:外键会导致写操作性能下降,难以分库分表。
解决方法:应用层维护约束,避免外键。
追问与回答:
问:外键的优点是什么?
答:保证数据一致性。问:外键约束能不能优化性能?
答:不能,反而增加额外校验。问:如果不用外键,怎么保证一致性?
答:在应用层保证,比如写入时校验。
34. binlog 有几种格式?区别是什么?
举例:STATEMENT、ROW、MIXED 三种模式。
场景:不同格式影响复制和性能。
解决方法:多数情况下用 ROW 模式。
追问与回答:
问:ROW 格式的优缺点?
答:优点是可靠,缺点是日志量大。问:STATEMENT 格式的风险?
答:非确定性函数(如 now())可能导致不一致。问:MIXED 格式什么时候用?
答:特殊情况会自动切换,如使用函数时。
35. MySQL 的 redo log 和 binlog 有什么区别?
举例:redo log 是物理日志,binlog 是逻辑日志。
场景:redo 保证 crash-safe,binlog 保证主从一致。
解决方法:两者结合才能保证事务安全。
追问与回答:
问:redo log 写完但 binlog 没写完,会怎样?
答:可能导致主从不一致。问:binlog 主要用来做什么?
答:主从复制、数据恢复。问:redo log 循环写满会发生什么?
答:会阻塞写入,直到 checkpoint 推进。
36. MySQL 的 undo log 有什么作用?
举例:执行
UPDATE users SET age=30 WHERE id=1;
,undo log 保存原值 20。场景:用于事务回滚和 MVCC。
解决方法:合理控制长事务,避免 undo 堆积。
追问与回答:
问:undo log 存放在哪里?
答:存放在回滚段,表空间里。问:MVCC 为什么依赖 undo log?
答:读取历史版本需要从 undo log 里获取。问:undo log 会不会被清理?
答:提交后可清理,长事务可能导致堆积。
37. 为什么不建议大事务?
举例:一次性更新 100 万行。
场景:大事务会占用大量锁和日志空间。
解决方法:拆分事务,分批提交。
追问与回答:
问:大事务对 binlog 有什么影响?
答:产生超大 binlog,可能导致同步延迟。问:大事务对锁的影响?
答:持锁时间长,容易引发阻塞和死锁。问:如何避免?
答:分批提交,每批 1000 或 10000 行。
38. 为什么 count(distinct col) 很慢?
举例:
SELECT COUNT(DISTINCT user_id) FROM orders;
场景:需要排序去重,性能差。
解决方法:使用临时表、分组统计,或借助数据仓库。
追问与回答:
问:count(distinct col1, col2) 怎么优化?
答:可以拼接成 concat(col1, col2),再 distinct。问:能否用 group by 替代 distinct?
答:可以,有时更快。问:如果数据量很大?
答:可以用 Spark/Hive 计算。
39. group by 为什么会用临时表?
举例:
SELECT city, COUNT(*) FROM users GROUP BY city;
场景:当没有合适索引时,需要额外排序或临时表。
解决方法:在 group by 列上建索引。
追问与回答:
问:group by 默认会排序吗?
答:会,可以用ORDER BY NULL
禁止。问:extra 里出现 Using temporary,说明什么?
答:说明用了临时表,效率低。问:怎么优化 group by?
答:索引覆盖,或分批汇总。
40. MySQL 的临时表分为哪几种?
举例:内存临时表、磁盘临时表。
场景:order by/group by 超出内存限制会用磁盘临时表。
解决方法:调整 tmp_table_size、max_heap_table_size。
追问与回答:
问:怎么判断用了磁盘临时表?
答:Created_tmp_disk_tables
监控指标升高。问:临时表会不会影响性能?
答:会,尤其是大查询。问:如何减少临时表?
答:加索引,避免 select *。
41. MySQL 的行锁是怎么实现的?
举例:
SELECT * FROM users WHERE id=1 FOR UPDATE;
场景:通过索引项加锁。
解决方法:尽量走索引,避免表锁。
追问与回答:
问:行锁是加在记录上还是索引上?
答:加在索引项上。问:如果没有索引条件,会怎样?
答:会退化为表锁。问:怎么避免锁升级?
答:使用合理索引,避免范围过大。
42. 什么是间隙锁(gap lock)?
举例:
SELECT * FROM users WHERE age BETWEEN 20 AND 30 FOR UPDATE;
场景:会锁定区间,阻止插入。
解决方法:减少范围锁,尽量用等值条件。
追问与回答:
问:gap lock 是为了避免什么?
答:避免幻读。问:在 RC 隔离级别下有 gap lock 吗?
答:没有,只有 RR 下有。问:gap lock 会不会影响并发?
答:会,范围越大并发越差。
43. MySQL 的 change buffer 是什么?
举例:非唯一二级索引更新时,写入 change buffer,异步合并。
场景:减少磁盘 I/O。
解决方法:合理开启 innodb_change_buffering。
追问与回答:
问:哪些情况会用 change buffer?
答:更新非唯一二级索引。问:唯一索引会走 change buffer 吗?
答:不会,因为必须立即校验唯一性。问:什么时候合并 change buffer?
答:后台线程或查询索引页时。
44. MySQL 的 buffer pool 有什么作用?
举例:buffer pool 缓存数据页和索引页。
场景:减少磁盘 I/O。
解决方法:合理配置 innodb_buffer_pool_size。
追问与回答:
问:buffer pool 一般设多少?
答:推荐 60%~80% 内存。问:怎么查看命中率?
答:通过SHOW ENGINE INNODB STATUS
。问:buffer pool 太小会怎样?
答:频繁淘汰页,性能下降。
45. MySQL 的 doublewrite buffer 有什么用?
举例:数据页写入时,先写 doublewrite,再写数据文件。
场景:防止部分写失败导致页损坏。
解决方法:保持开启 doublewrite。
追问与回答:
问:doublewrite 是写两次吗?
答:是,先写 2MB buffer,再刷盘。问:能关闭吗?
答:能,但风险大。问:影响性能吗?
答:几乎无明显影响,因为是顺序写。
46. MySQL 的 redo log 是循环写的吗?
举例:redo log 文件组,固定大小,循环覆盖。
场景:保证写入性能。
解决方法:合理设置 innodb_log_file_size。
追问与回答:
问:如果写满会怎样?
答:会阻塞写操作,等待 checkpoint。问:checkpoint 的作用是什么?
答:把脏页刷盘,推进日志。问:redo log 和 undo log 的区别?
答:redo 用于恢复,undo 用于回滚。
47. MySQL 的主键索引和二级索引有什么区别?
举例:主键索引存储完整数据,二级索引存储主键。
场景:二级索引需要回表。
解决方法:合理利用覆盖索引。
追问与回答:
问:回表是什么意思?
答:二级索引定位到主键,再回到主键索引取数据。问:为什么主键索引是聚簇索引?
答:数据按主键顺序存储。问:二级索引能避免回表吗?
答:可以,靠覆盖索引。
48. 为什么大表查询建议分区?
举例:分区表按日期拆分。
场景:避免全表扫描。
解决方法:合理设计分区键。
追问与回答:
问:分区表和分库分表区别?
答:分区是逻辑拆分,底层仍在一个实例。问:分区表有什么限制?
答:不支持外键,某些 SQL 也有限制。问:什么时候不用分区?
答:小表或随机访问为主时。
49. MySQL 为什么要有自适应哈希索引?
举例:热点范围查询多时,会自动生成哈希索引。
场景:提高热点访问性能。
解决方法:保持默认开启。
追问与回答:
问:哈希索引和 B+Tree 索引区别?
答:哈希索引适合等值查找,不适合范围。问:自适应哈希索引会不会影响写?
答:会,维护开销增加。问:能关闭吗?
答:可以,通过 innodb_adaptive_hash_index。
50. 为什么要控制表字段数量?
举例:单表 200 列,查询效率很差。
场景:字段过多,行大小超限,导致溢出存储。
解决方法:字段拆分,避免过宽表。
追问与回答:
问:MySQL 一行最大多大?
答:InnoDB 约 8KB,超过会用溢出页。问:TEXT、BLOB 存储有什么特殊性?
答:只存指针,数据放溢出页。问:宽表有什么坏处?
答:行长大,缓存命中率低,I/O 成本高。