复合索引字段顺序应按等值条件、范围条件、排序字段从左到右排列,确保最左前缀匹配且避免右侧字段失效;唯一索引在高并发写入时性能低于普通索引,因需全量校验;判断是否真走索引需结合explain的key_len、rows、filesort等综合分析;覆盖索引宜优先包含紧凑字段,避免长文本膨胀索引体积。

高并发下复合索引字段顺序怎么排
字段顺序直接决定索引是否能被 WHERE、ORDER BY、GROUP BY 用上。MySQL 从左到右匹配索引列,一旦遇到范围查询(>、BETWEEN、LIKE 'abc%')或 IS NULL,右侧字段就失效。
常见错误是把高区分度字段放前面却忽略查询模式。比如用户表常查 WHERE status = ? AND created_at > ? ORDER BY id DESC,这时 (status, created_at, id) 比 (id, status, created_at) 有效得多——因为 status 是等值,created_at 是范围,id 是排序,刚好满足最左前缀+覆盖排序。
- 等值条件字段放最左(如
user_id = 123) - 多个等值时,把区分度高、过滤性强的放前面(但别只看
CARDINALITY,要看实际查询分布) - 范围条件字段紧接等值字段之后,且只能有一个(再往后字段无法用于索引查找)
- 排序字段可放在最后,前提是前面全是等值或单个范围;否则会触发 filesort
唯一索引 vs 普通索引在写入热点上的表现差异
高并发 INSERT/UPDATE 场景下,唯一索引要求每次写都做唯一性校验,需访问完整索引树甚至回表,而普通索引只需定位到插入位置。当业务能接受应用层去重(如用 Redis 预判 + 数据库兜底),优先用普通索引可显著降低锁等待。
典型反例:秒杀库存扣减用 UNIQUE (order_id) 防重,但大量请求同时 INSERT 同一 order_id(因网络重试或前端重复提交),导致唯一冲突+死锁频发。换成先 INSERT IGNORE + 检查 ROW_COUNT(),配合普通索引,吞吐能提升 3–5 倍。
- 高频写入字段尽量避免建唯一索引,除非强一致性不可妥协
- 若必须唯一约束,考虑将校验逻辑前置(如分库分表键哈希后查缓存)
-
INSERT ... ON DUPLICATE KEY UPDATE在唯一索引冲突时仍要加 GAP 锁,比普通索引更易阻塞
如何判断一个查询是否真的走索引而不是全表扫描
不能只看 EXPLAIN 的 type 字段是 ref 或 range 就放心——还要看 key_len 是否符合预期、rows 是否远大于实际返回行数、有没有出现 Using filesort 或 Using temporary。
真实案例:某订单表有索引 (shop_id, status, created_at),查询 WHERE shop_id = 100 AND status IN ('paid', 'shipped') ORDER BY created_at DESC,EXPLAIN 显示用了该索引,但 key_len = 8(只用了前两列),created_at 无法用于排序,最终触发 filesort。改用 (shop_id, status, created_at) 并确保 status 是等值(如拆成两个 UNION 查询),才真正消除排序开销。
- 用
EXPLAIN FORMAT=JSON查看used_key_parts,确认哪些列实际生效 - 对慢查询开启
slow_query_log并设置log_queries_not_using_indexes = ON,但注意它不捕获已用索引却效率低的情况 - 线上验证务必用
SELECT ... FOR UPDATE或真实负载压测,避免执行计划因数据量变化而漂移
覆盖索引在高并发读场景下的取舍边界
覆盖索引能避免回表,减少 IO 和锁竞争,但索引本身体积变大,更新成本上升,且可能挤占 buffer pool。不是所有“能覆盖”的字段都该加进索引——尤其当其中包含 TEXT、BLOB 或长 VARCHAR 时。
比如日志表常查 SELECT user_id, action, ts FROM log WHERE app_id = ? AND ts BETWEEN ? AND ?,若把 action(平均长度 200 字节)和 ts 全放进索引,单条索引记录超 250 字节,1000 万行就多占近 2.5GB 索引空间,InnoDB page split 更频繁,反而拖慢写入。
- 优先覆盖整型、短字符串(如状态码、枚举 ID)、时间戳等紧凑字段
- 避免在高频更新表上为低频查询字段建覆盖索引
- 用
SELECT COUNT(*)对比SELECT COUNT(1)在覆盖索引下的执行计划,可快速验证是否真正免回表
索引不是越多越好,高并发场景下每个多余的二级索引都在悄悄增加每一行 INSERT 的加锁路径和缓冲区压力。设计时得盯着慢查日志里真实的 Rows_examined 和 Innodb_row_lock_waits,而不是凭经验猜“这个字段以后可能会查”。











