select * 拖垮索引效率是因为强制回表读聚簇索引,导致高并发下i/o与buffer pool竞争激增;应改用明确字段+覆盖索引,并按过滤强度、排序、覆盖需求合理设计复合索引顺序。

为什么 SELECT * 在高并发下会拖垮索引效率
不是索引没建好,而是查询把索引“绕过去了”。SELECT * 强制回表读聚簇索引,即使 WHERE 条件命中了二级索引,MySQL 仍要根据主键再去查一遍完整行数据。高并发时磁盘 I/O 和 Buffer Pool 竞争激增,QPS 直线下降。
- 只查必需字段,把
SELECT *改成明确列出的字段,比如SELECT user_id, status, updated_at - 如果经常按
status查询且只用这几个字段,可建覆盖索引:CREATE INDEX idx_status_cover ON users(status, user_id, updated_at) - 注意:覆盖索引字段顺序影响范围查询效率,等值条件放前,范围条件(如
BETWEEN、>)放后
ORDER BY + LIMIT 分页慢的根本原因
偏移量大的分页(如 LIMIT 10000, 20)会让 MySQL 扫描并丢弃前 10000 行——这些行可能全在索引里反复遍历,但不走索引有序性优势。
- 用游标分页替代偏移分页:记录上一页最后一条的
updated_at和id,下一页查WHERE updated_at - 确保
ORDER BY字段在索引最左,且与WHERE条件能合并使用,例如WHERE category = 'A' ORDER BY created_at DESC应建索引(category, created_at) - 避免在
ORDER BY中使用函数或表达式,如ORDER BY DATE(created_at)会导致索引失效
高并发写入时唯一索引导致的锁冲突
高频插入带 UNIQUE 约束的字段(如订单号、手机号),InnoDB 会在索引间隙加 INSERT INTENTION 锁+唯一检查锁,容易引发死锁或长时间等待。
- 批量插入优先用
INSERT ... ON DUPLICATE KEY UPDATE,比先SELECT再INSERT少一次索引查找和锁竞争 - 对非核心业务字段(如邀请码、短链 hash),可改用应用层生成 + 数据库冗余校验,避开唯一索引热点
- 确认是否真需要
UNIQUE:比如用户注册邮箱唯一,但后台导入选项可临时禁用约束,事后异步去重
复合索引中字段顺序怎么排才不白建
字段顺序不是按出现频率排,而是按「过滤强度 + 排序需求 + 覆盖需求」综合决定。错序的复合索引等于没建。
- 等值查询字段放最左(如
user_type = 2),再跟范围字段(如created_at > '2024-01-01'),最后放ORDER BY或覆盖字段 - 如果同时有
WHERE a = ? AND b > ? ORDER BY c DESC,索引应为(a, b, c);若改成ORDER BY d DESC,则d必须加入索引末尾才能覆盖 - 单列索引和复合索引不要共存冗余字段,例如已有
(a, b),再单独建(a)索引意义不大,还会增加写开销
EXPLAIN SELECT user_id, name FROM orders
WHERE shop_id = 1001 AND status IN ('paid', 'shipped')
ORDER BY created_at DESC LIMIT 20;
这条语句理想索引是 INDEX(shop_id, status, created_at, user_id, name) —— 注意 status IN 虽是等值,但优化器可能转为范围扫描,所以它放在 shop_id 后、created_at 前更稳妥。实际是否生效,得看 EXPLAIN 的 key_len 和 Extra 是否含 Using index。
真正卡住高并发的,往往不是没建索引,而是建了但没被用上,或者用了却因回表/排序/锁竞争变成瓶颈。每次加索引前,先跑一遍 EXPLAIN FORMAT=JSON,盯着 used_key_parts 和 attached_condition 看它到底读了哪几列、有没有隐式转换。











