索引没建对,缓存再快也白搭
缓存(比如 Redis)只能加速「已经能快速查出来的数据」,如果 SQL 本身走全表扫描、
EXPLAIN显示
type=ALL、
rows动辄几十万,那哪怕把结果缓存 1 小时,下一次未命中缓存的请求照样拖垮数据库。真实线上案例里,70% 的“缓存无效”问题,根子都在索引没覆盖查询条件或顺序错——比如
WHERE status = ? AND created_at > ? ORDER BY id,却只给
status单独建了索引,
created_at和
id完全没进索引,MySQL 只能先扫出所有
status匹配的行,再内存排序,缓存根本救不了这个 IO+CPU 双爆的场景。
哪些查询值得加缓存?先看索引是否“覆盖”
真正适合缓存的查询,往往满足两个硬条件:高频、稳定、且已由**覆盖索引**支撑。所谓覆盖索引,就是
SELECT的所有字段 +
WHERE/
ORDER BY用到的字段,全部包含在同一个索引里,让 MySQL 连主表数据页都不用读。例如:
SELECT user_id, order_no, amount FROM orders WHERE shop_id = ? AND status = ? ORDER BY created_at DESC;
对应建复合索引:
ALTER TABLE orders ADD INDEX idx_shop_status_time (shop_id, status, created_at DESC, user_id, order_no, amount);这样查询走索引就能拿到全部结果,
Extra字段显示
Using index,IO 极低,才值得扔进 Redis 缓存 如果索引里漏了
amount,MySQL 就得回表查聚簇索引,延迟波动大,缓存命中的收益被抵消 缓存 key 设计也要对齐索引字段,比如
orders:shop_123:status_1,避免缓存粒度太粗(全量缓存)或太碎(单行缓存)
缓存失效和索引更新不是一回事
很多人误以为“我改了数据,缓存自动失效”,其实完全无关——InnoDB 更新数据时会维护 B+ 树索引,但不会通知 Redis。必须由应用层主动清理或设 TTL。更危险的是:如果索引设计导致查询结果逻辑变更(比如新增一个
WHERE is_deleted = 0条件但没加到索引里),旧缓存可能长期返回错误数据,而 DB 层毫无感知。 写操作后,优先清缓存,而不是等 TTL;尤其涉及金额、状态类字段 避免用「更新即删缓存」的简单策略——如果并发写同一记录,可能因时序问题导致缓存击穿,建议用延迟双删或订阅 binlog 索引重建(
OPTIMIZE TABLE或
ALTER TABLE ... ENGINE=InnoDB)不触发缓存失效,但可能改变查询执行计划,需同步验证缓存 key 是否仍有效
别拿缓存当索引缺陷的遮羞布
见过太多团队在慢查询报警后第一反应是“加个 Redis”,结果缓存掩盖了真实瓶颈:比如一个分页查询
OFFSET 10000 LIMIT 20,索引虽存在,但深度遍历导致延迟飙升,缓存只能缓解第一页,后面翻页照样超时。这种场景该做的是改用游标分页(
WHERE id > ? ORDER BY id LIMIT 20)+ 覆盖索引,而不是堆缓存。 缓存适合解决「读多写少 + 查询模式固定」的问题;索引优化解决「任意条件组合下都能快速定位」的问题 监控时要分开看:Redis 的
get_hits / get_misses比率高 ≠ 数据库健康;MySQL 的
Handler_read_next持续飙升,说明索引没发挥应有作用 最易被忽略的一点:
JOIN多表时,每张表的驱动顺序和关联字段索引质量,直接影响最终是否能走覆盖索引——这一步没调好,缓存连入口都摸不到
