mysql索引在高并发场景如何设计_mysql性能调优方法

来源:这里教程网 时间:2026-02-28 20:51:10 作者:

高并发下复合索引字段顺序怎么排

字段顺序直接决定索引是否能被 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
,而不是凭经验猜“这个字段以后可能会查”。

相关推荐