mysql数据库中的查询缓存与索引优化

来源:这里教程网 时间:2026-02-28 20:39:15 作者:

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

索引不是越多越好,也不是建了就一定生效。真正卡住性能的,往往是那条没走索引的慢查询,藏在凌晨三点的监控告警里,而不是文档开头的“最佳实践”列表中。

相关推荐