不是。单表过亿后加索引未必有效,因B+树深度增加、页分裂频繁、缓冲池命中率低,且高频更新列建索引会加剧IO压力;需结合执行计划、数据分布、分片策略等综合优化。

单表过亿后 WHERE 查询变慢,是不是加个索引就行?
不是。单表超亿行时,INDEX 本身可能成为瓶颈:B+ 树深度增加、页分裂频繁、缓冲池命中率暴跌。更关键的是,很多“理所当然”的索引在大数据量下反而拖累写入和维护成本。
实操建议:
- 先用
EXPLAIN FORMAT=TREE看执行计划,确认是否真走索引——有时优化器会因统计信息过期而放弃索引,直接全表扫描 - 避免在高频更新列(如
status、updated_at)上建普通二级索引,每次 UPDATE 都要回写索引页,IO 压力翻倍 - 对
LIKE '%xxx'或JSON_CONTAINS()这类无法用 B+ 树高效定位的查询,别硬扛,该上MATCH ... AGAINST(全文索引)或迁到Elasticsearch
分库分表后全局唯一 ID 上的索引为什么总失效?
因为分片键(sharding key)和查询条件不一致。比如按 user_id 分库,但业务查的是 order_no,即使 order_no 有索引,也得扫所有分片。
实操建议:
- 强制要求所有高频查询必须带上分片键,否则在应用层做路由判断前就丢弃请求(用
MySQL Router或中间件拦截) -
order_no这类非分片字段,如果必须查,优先建UNIQUE索引 + 提前生成带分片标识的编码(如shard_02_order_123456),让查询能精准路由 - 跨分片
ORDER BY ... LIMIT是重灾区:每个分片返回 TOP N,合并后再取 LIMIT,内存和网络开销剧增——改用时间范围 + 游标分页(WHERE created_at > ? ORDER BY created_at LIMIT 100)
ALTER TABLE ... ADD INDEX 在大表上卡住不动怎么办?
MySQL 5.6+ 虽支持在线 DDL,但加索引仍需拷贝表数据(ALGORITHM=INPLACE 仅适用于某些场景),亿级表可能锁数小时,且临时空间吃满磁盘。
实操建议:
- 用
pt-online-schema-change替代原生命令,它通过触发器双写+影子表切换,全程不锁主表(但要注意触发器对写入性能的影响) - 提前在从库上建好索引,再主从切换——前提是业务能接受短暂只读窗口
- 若只是临时排查,可用
CREATE INDEX ... ALGORITHM=INPLACE LOCK=NONE,但必须确认满足条件(例如不能是TEXT列前缀索引)
复合索引的字段顺序,为什么线上效果和测试环境完全不同?
因为数据分布和查询模式变了。测试库只有十万行,WHERE a = ? AND b > ? 走 (a,b) 索引没问题;线上十亿行,a 的区分度暴跌(比如 a 是 tenant_id,90% 请求集中在 3 个租户),导致索引选择性失效。
实操建议:
- 用
SELECT COUNT(DISTINCT a)/COUNT(*)算选择性,低于 0.01 就别放复合索引最左位 - 把高区分度字段(如
id、create_time)放左边,低区分度字段(如status、type)放右边,哪怕违背“等值在前”教条 - 定期用
sys.schema_index_statistics查看索引实际命中次数和平均扫描行数,比EXPLAIN更真实
索引不是加了就完事,而是要跟着数据规模、分布、访问模式一起演进。很多人卡在“以为建了索引就等于优化完成”,其实才刚开始。


















