
SQL查询缓存是否生效,关键不在“有没有开”,而在于“缓存键是否稳定”和“数据依赖是否被正确追踪”。命中率低通常不是缓存坏了,而是查询结构、参数或数据变更触发了隐式失效。
缓存命中靠的是完全一致的SQL文本
MySQL原生Query Cache(5.7及之前)或应用层缓存(如Redis存SELECT结果),都以标准化后的完整SQL字符串为键。哪怕一个空格、注释、大小写或字段顺序不同,都会生成新哈希值。
- ✅ 命中示例:
SELECT id, name FROM users WHERE id = 1;和下一次完全相同的语句 - ❌ 不命中示例:
SELECT name, id FROM users WHERE id = 1;(字段顺序变)、select id,name from users where id=1;(大小写+空格差异) - ⚠️ 动态拼接风险:用Python f-string 或
str.format()拼SQL,容易引入不可控空格或换行
哪些操作会立刻清空相关缓存
缓存不是长期有效的快照,而是带依赖关系的临时副本。只要底层数据或结构有变动,关联缓存就强制失效。
- 任意DML:对表执行
INSERT/UPDATE/DELETE,该表所有缓存条目立即作废(即使事务未提交) - DDL变更:
ALTER TABLE、DROP INDEX、TRUNCATE等操作,整张表缓存全清 - 不确定函数:
NOW()、RAND()、USER()、SYSDATE()会让每次查询视为不同语句,无法缓存 - 临时表与用户变量:
CREATE TEMPORARY TABLE或@var := 1类语句默认不进缓存
缓存键不一致的常见陷阱
即使业务逻辑相同,ORM或中间层生成的SQL也可能因调用顺序、参数类型、方言差异而不同。
- SQLAlchemy中:
.filter().order_by()和.order_by().filter()生成的AST不同,缓存键不同 - 参数序列化问题:字典传参时键顺序不确定(Python 3.6+虽保持插入序,但显式排序更稳妥)
- 数字类型混用:
WHERE id = '123'(字符串) vsWHERE id = 123(整数),部分方言生成不同SQL - 数据库方言差异:PostgreSQL可能加双引号,MySQL不加,导致键不匹配
如何验证当前缓存是否真在工作
别只看配置开关,要从运行时指标判断实效性。
- MySQL原生缓存:查
SHOW STATUS LIKE 'Qcache%';,重点看Qcache_hits / (Qcache_hits + Com_select)是否 > 30% - InnoDB缓冲池不是查询缓存:用
Innodb_buffer_pool_read_requests和Innodb_buffer_pool_reads算页级命中率,和SQL结果缓存无关 - 应用层缓存(如Redis):监控缓存get/set频次、过期率、miss后DB压力突增情况
- 开启SQL日志(如SQLAlchemy的
echo=True):观察相同业务请求是否重复发SQL










