为什么高并发下索引失效比慢查询更致命
因为失效的索引会让大量请求同时落到磁盘 I/O,瞬间把
innodb_buffer_pool挤爆,连接数打满,进而引发雪崩。不是查得慢,是查得“一起卡”。常见诱因包括:隐式类型转换(比如
WHERE user_id = '123'对 int 字段)、函数包裹字段(
WHERE DATE(create_time) = '2024-01-01')、或最左前缀没对齐(联合索引
(a,b,c)却只查
WHERE b = 1)。
联合索引字段顺序必须按「过滤强度 + 查询频率」排
不能只看 WHERE 条件出现顺序。比如订单表常查
WHERE status = ? AND shop_id = ? AND created_at > ?,但
status只有 3–5 个枚举值,而
shop_id有十万级区分度——这时索引应建为
(shop_id, status, created_at),而非照搬 SQL 顺序。否则
status在前会导致索引选择性极低,优化器大概率放弃走索引。
实操建议:
用SELECT COUNT(DISTINCT col)/COUNT(*) FROM table算选择性,>0.1 才算高区分度 把等值查询字段放前面,范围查询(
>,
BETWEEN)放最后,避免截断后续字段的索引能力 如果存在
ORDER BY,尽量让排序字段也落在索引后缀中,避免额外 filesort
覆盖索引能直接消除回表,但别盲目加所有 SELECT 字段
回表是高并发下的性能黑洞——每行都要回到聚簇索引取数据,随机 I/O 爆增。用
EXPLAIN看
Extra列是否含
Using index可确认是否覆盖。
但注意:
覆盖索引越宽,B+ 树层级越深、内存占用越大,写入性能下降明显 只把高频查询中「固定且稳定」的返回字段加入索引,比如SELECT order_no, status, updated_at就建
(shop_id, status, updated_at, order_no)千万别把
TEXT或大
VARCHAR字段塞进索引——会触发
Row size too large错误
高并发写多读少场景慎用普通二级索引
每个二级索引插入都要更新 B+ 树+维护
change buffer,并发写入时容易在
dict_sys->mutex上争抢,导致
innodb_row_lock_waits暴涨。真实案例:某订单流水表加了 4 个二级索引后,QPS 从 8000 掉到 2200。
可考虑:
写入路径剥离:用 Kafka 异步落库,主表只保留必要字段和主键,索引精简到 1–2 个 读写分离 + 索引差异化:从库建更多索引,主库只保核心查询路径 用innodb_adaptive_hash_index=OFF降低热点页冲突(尤其 SSD 环境下收益明显)
真正难的不是加索引,是判断哪些查询值得索引、哪些该用缓存兜底、哪些干脆改查询逻辑——比如把
SELECT * FROM orders WHERE user_id IN (…)拆成多次单 ID 查询,反而比一个宽索引更稳。
