MySQL 8.0 已彻底移除查询缓存(query_cache),仅保留 innodb_buffer_pool 缓存数据页和索引页;联合索引遵循“连续等值+最多一个范围”原则;覆盖索引可避免回表但需避开 SELECT * 和大字段陷阱;buffer pool 大小需根据命中率与物理读频率合理配置。
MySQL 5.7 是最后一个支持 query_cache_type 和 query_cache_size 的正式版本;8.0 起这些变量已被删除,强行设置会报错 Unknown system variable 'query_cache_type'。如果你还在查文档配 query_cache,说明你用的是过时资料或降级环境。
真实场景中,靠查询缓存提升性能的思路早已失效:它只对完全相同的 SQL 文本(含空格、大小写、库名)生效,且只要表有任意 DML 操作,整个表相关缓存全失效。高并发更新下,命中率极低,反而增加锁争用。
innodb_buffer_pool —— 它缓存的是数据页和索引页,不是 SQL 结果SHOW VARIABLES LIKE 'query_cache%';,8.0 返回空结果即正常WHERE 条件里写了 a = 1 AND b > 10 AND c = 5,但只有 (a, c, b) 索引,b > 10 就无法做索引范围扫描,因为 c = 5 是等值,但位置在 b 前面,导致 b 之后的字段无法参与范围查找。
联合索引遵循“最左前缀原则”,但更准确的理解是“连续等值 + 最多一个范围”:
(a, b, c) 可用于:a = 1 AND b = 2 AND c > 3(c 是最后一个,可范围)a = 1 AND c = 3(跳过了 b,中断最左连续)a IN (1,2) AND b = 3(MySQL 5.7+ 支持多值等号后的字段继续走索引)EXPLAIN 看 key_len 值,能反推出实际用了索引的前几列当 SELECT 的所有字段都在索引中(比如索引是 (user_id, status, created_at),而语句是 SELECT user_id, status FROM orders WHERE user_id = 123),InnoDB 就不必回主键聚簇索引取整行,直接从二级索引叶子节点返回结果 —— 这就是覆盖索引。
但两个常见坑容易被忽略:
SELECT * 几乎不可能走覆盖索引,除非你建的索引包含所有列(不现实,且极大拖慢写入)TEXT 或 VARCHAR(2000),InnoDB 可能放弃使用该索引,因为单条索引记录过大,B+ 树效率下降EXPLAIN 看 
Extra 列:出现 Using index 表示走了覆盖索引;出现 Using where; Using index 是覆盖索引 + 索引条件下过滤;出现 Using filesort 或 Using temporary 通常意味着优化空间很大innodb_buffer_pool_size 不是越大越好,也不是设成物理内存 70% 就万事大吉。它影响三件事:热数据驻留能力、脏页堆积量、刷脏频率。
典型问题场景:
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'; 持续上升vmstat 1 可见 si/so 非零innodb_io_capacity 和 innodb_io_capacity_max 设太低 → 日志写满触发同步刷脏,查询突然变慢建议用如下方式粗估初始值:
SELECT
ROUND((SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'innodb_buffer_pool_size') / 1024 / 1024 / 1024, 2) AS pool_gb,
ROUND((SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads'), 2) AS hit_ratio;命中率低于 95%,且 Innodb_buffer_pool_reads 每秒 > 10 次,才值得调大 buffer pool。
索引设计要贴着查询写,缓存得看清楚是哪一层的缓存 —— MySQL 8.0 里已经没有“查询缓存”这个东西了,别被老教程带偏。