MySQL 8.0 之后查询缓存已被移除,别再配 query_cache_type
MySQL 官方从 8.0.3 版本起彻底删除了查询缓存(Query Cache)模块。如果你在配置文件里还写着
query_cache_type=1或调用
SQL_CACHE提示符,MySQL 启动会报警告,语句也会被静默忽略。 5.7 是最后一个支持查询缓存的大版本,但默认已禁用(
query_cache_type=0),且存在严重并发锁争用问题 即使开启,只要表有任意写操作(
INSERT/
UPDATE/
DELETE),该表所有缓存结果都会被清空,实际命中率极低 替代方案是应用层缓存(如 Redis)或代理层缓存(如 ProxySQL 的 result cache),它们可控性更强、粒度更细
EXPLAIN
看懂索引是否生效,重点盯 type
和 key
字段
执行
EXPLAIN SELECT ...是判断索引是否走对的第一步。真正关键的不是有没有
key,而是
type值是否为
range、
ref、
eq_ref;如果出现
ALL,基本等于全表扫描。
key为空:没走索引,可能原因包括字段没建索引、用了函数(如
WHERE YEAR(created_at) = 2023)、隐式类型转换(如字符串字段查数字:
WHERE status = 1,而
status是
VARCHAR)
Extra出现
Using filesort或
Using temporary:ORDER BY 或 GROUP BY 无法利用索引完成,需要优化排序字段或加覆盖索引 联合索引要注意最左前缀:对
(a,b,c)索引,
WHERE b = ?不会命中;但
WHERE a = ? AND b > ?可以用到前两列
覆盖索引能避免回表,但别为了覆盖强行堆字段
当
SELECT的所有字段都包含在索引中时,MySQL 直接从 B+ 树叶子节点取值,无需回主键索引查数据行——这就是覆盖索引。它减少 I/O,提升速度,但设计不当反而拖慢写入和占用空间。 适合场景:高频查询的固定字段组合,例如日志表查
SELECT user_id, action, created_at FROM log WHERE app_id = ? ORDER BY created_at DESC LIMIT 20,可建
(app_id, created_at, user_id, action)不推荐:把大字段(如
TEXT、长
VARCHAR)塞进索引;索引本身变重,插入/更新变慢,缓冲池压力上升 注意:主键本身就是聚簇索引,所以
SELECT *在主键查询下天然“覆盖”,但非主键索引必须显式包含所有 SELECT 字段才生效
索引失效的典型现场:隐式转换、OR
、LIKE
左模糊
很多 SQL 看似合理,却因细节触发索引失效。这些不是 bug,是 B+ 树索引的固有限制。
WHERE mobile = 13800138000→ 如果
mobile是
VARCHAR,会转成数字比较,触发全表扫描;应统一写成字符串:
'13800138000'
WHERE status = 'active' OR type = 'vip'→ 即使两字段都有索引,
OR通常导致放弃索引走全表;改用
UNION或确保其中一个条件能走强索引
WHERE name LIKE '%张%'→ 左模糊无法使用 B+ 树的有序性;如必须模糊搜索,考虑全文索引(
FULLTEXT)或 Elasticsearch
EXPLAIN SELECT id, name FROM users WHERE name LIKE '张%';
这句能走索引;但把
'张%'换成
'%张',
type就变成
ALL。
索引不是越多越好,也不是建了就一定生效。真正卡住性能的,往往是那条没走索引的慢查询,藏在凌晨三点的监控告警里,而不是文档开头的“最佳实践”列表中。
