数据倾斜不直接导致慢查询,但会引发索引失效、执行计划误判和节点负载失衡;status=1因选择性虚高被误选索引,实际扫描95%行且增加随机i/o;复合索引中低区分度字段应置于右侧,避免最左前缀失效;热点数据宜业务层拆分处理。

数据倾斜本身不直接导致慢查询,但会让索引失效、执行计划误判、物理节点负载失衡,最终表现为某条SQL在某些参数下快、换一个就10秒起步——这不是SQL写得烂,是数据分布把优化器“骗”了。
为什么status = 1的查询会全表扫描
当status字段95%值为1,MySQL统计信息显示该值选择性高(CARDINALITY虚高),优化器误以为走索引能快速定位,结果WHERE status = 1实际要扫95%的行,比全表扫描还多一次随机I/O(Handler_read_rnd_next暴涨)。更糟的是,如果它在复合索引最左位(比如INDEX(status, create_time)),整个索引都废了。
- 用
SHOW INDEX FROM table_name查CARDINALITY,除以总行数, - 执行
SELECT COUNT(*) FROM t WHERE status = 1和SELECT COUNT(*) FROM t对比,过滤比 >15% 就不该走这个索引 -
ANALYZE TABLE table_name强制刷新统计,别等自动采样(尤其批量导入后)
联合索引里倾斜字段放哪才不坑人
不是“能不能加索引”,而是“加在哪、怎么用”。把低区分度字段放在最左,等于给优化器递刀子;挪到右边,它只在高区分度字段筛完后才生效,反而安全。
- 错误顺序:
INDEX(status, user_id, create_time)→status = 1时直接退化为全索引扫描 - 正确顺序:
INDEX(user_id, create_time, status)→ 先用user_id精准定位,再按时间范围过滤,最后才匹配status - 如果查询带
ORDER BY create_time LIMIT 10且常查status = 'pending'(占比小),保留status在索引里反而能跳过排序,此时它不是负担是加速器
热点数据单独处理比硬扛更省事
对极端倾斜键(比如某个user_id占全表40%行),与其调参、建虚拟分片、搞一致性哈希,不如业务层拆逻辑:一条SQL专查这个ID,其余走通用路径。
- 用
INFORMATION_SCHEMA.STATISTICS确认该字段是否在复合索引中为最左前缀,不是就删掉影响小 - 监控
Handler_read_rnd_next和Handler_read_next比值,持续偏高说明回表严重,得动索引或改SQL - 避免在索引列上用函数:
WHERE UPPER(status) = 'ACTIVE'必然失效,改成WHERE status = 'active'并配合适当collation
真正卡住人的,从来不是“有没有索引”,而是“这一条查询实际要读多少行”——rows是估算,Handler_read_rnd_next才是硬盘在喘气的真实声音。











