select * 会拖垮高并发查询,因其强制回表、破坏覆盖索引、加剧i/o与锁竞争;应按需查询字段+条件字段建联合索引,并避免索引列函数操作。

为什么 SELECT * 会拖垮高并发查询
在高并发读场景下,SELECT * 往往是索引失效的起点。它强制 MySQL 回表获取所有字段,即使只用到其中 1–2 个,也会放大 I/O 和锁竞争。更关键的是,覆盖索引(Covering Index)完全失效——只要查询字段没被索引“包住”,就无法避免回表。
实操建议:
- 把高频查询的
WHERE条件字段 +SELECT中实际用到的字段,一起建联合索引,顺序按「等值查询字段在前、范围查询字段居中、排序/查询字段在后」排列 - 用
EXPLAIN检查Extra列是否含Using index;若出现Using where; Using index,说明是覆盖索引 - 避免在索引列上做函数操作,例如
WHERE YEAR(create_time) = 2024会让create_time索引失效,改用WHERE create_time >= '2024-01-01' AND create_time
ORDER BY 和 LIMIT 组合为何容易慢
高并发分页最典型的卡点:类似 SELECT * FROM order WHERE status = 1 ORDER BY created_at DESC LIMIT 10000, 20。MySQL 仍需扫描前 10000 行才能跳过,索引虽能加速排序,但偏移量越大,回表成本越高。
实操建议:
- 用游标分页替代
OFFSET:记录上一页最后一条的created_at和id,下一页查WHERE status = 1 AND (created_at, id) - 确保
ORDER BY字段在索引中连续且顺序一致;若混合 ASC/DESC,5.7+ 需显式声明,如INDEX idx_status_ctime_id (status, created_at DESC, id DESC) - 对写多读少的表,可考虑冗余一个
sort_order整型字段,用触发器或应用层维护单调递增,让分页走主键范围扫描
唯一索引 vs 普通索引:高并发写入时怎么选
唯一索引在插入时必须做唯一性校验,会触发额外的 change buffer 合并或直接读页,尤其在二级索引未缓存时,随机 IO 显著上升。普通索引则允许延迟写入,吞吐更高。
实操建议:
- 业务层能保证唯一性(如订单号由雪花算法生成),就别在数据库加
UNIQUE约束,用普通索引 + 应用重试兜底 - 若必须用唯一索引,把唯一字段尽量前置到联合索引最左,减少校验范围;例如
(order_no, user_id)比(user_id, order_no)更利于快速定位冲突 - 监控
Innodb_buffer_pool_reads和Innodb_buffer_pool_read_requests比值,若 > 1%,说明缓冲池命中率低,大量唯一校验被迫走磁盘
什么时候该删索引而不是加索引
索引不是越多越好。每个新增索引都会拖慢 INSERT/UPDATE/DELETE,因为要同步更新 B+ 树;更隐蔽的问题是:多个相似索引(如 (a)、(a,b)、(a,b,c))会导致优化器选错执行计划,或因统计信息不准引发全表扫描。
实操建议:
- 用
sys.schema_unused_indexes视图(MySQL 5.7+)或performance_schema.table_io_waits_summary_by_index_usage找长期未被使用的索引 - 检查
SHOW INDEX FROM tbl中Cardinality极低(如 - 删除前先用
pt-duplicate-key-checker工具识别冗余索引,比如已有(user_id, status),再建(user_id)就是多余
真正难的不是加索引,是判断哪些查询不该走索引——比如日志类表的 LIKE '%keyword%' 查询,强行加全文索引或改用 Elasticsearch 更实际。索引只是手段,不是目的。











