SELECT高并发变慢主因是锁争用、IO瓶颈或连接耗尽,而非SQL本身;需用SHOW PROFILE和sys.schema_table_statistics定位等待环节,检查缓冲池命中率,并合理配置连接池与索引。

为什么 SELECT 在高并发下突然变慢?先看真实瓶颈点
不是所有“慢查询”都该优化 SQL,高并发下 SELECT 延迟飙升,大概率卡在锁、IO 或连接争用上。比如:大量短连接反复建连耗尽 wait_timeout 资源;或二级索引 + WHERE 条件触发间隙锁,导致后续查询被阻塞;又或者 innodb_buffer_pool_size 设置过小,频繁刷脏页+磁盘随机读。
EXPLAIN 看不准延迟?必须补上 SHOW PROFILE 和 sys.schema_table_statistics
EXPLAIN 只反映执行计划,不体现真实 IO 和锁等待。高并发场景下更需定位“谁在等什么”:
- 用
SHOW PROFILE FOR QUERY N查看单次查询各阶段耗时(重点关注Waiting for table metadata lock、Sending data、Creating sort index) - 查
sys.schema_table_statistics找出物理读最多的表:SELECT * FROM sys.schema_table_statistics WHERE total_latency LIKE '%s%' ORDER BY io_read_latency DESC LIMIT 5 - 确认是否命中缓冲池:
SELECT (1 - (innodb_buffer_pool_reads / innodb_buffer_pool_read_requests)) * 100 AS hit_rate FROM information_schema.GLOBAL_STATUS WHERE variable_name IN ('innodb_buffer_pool_reads', 'innodb_buffer_pool_read_requests')
加索引反而更慢?注意 WHERE 条件顺序和 IN 列表长度
复合索引失效常发生在高并发写入后统计信息未更新,或 IN 子句超过 MySQL 优化器阈值(默认约 300 项),导致放弃使用索引走全表扫描:
- 用
ANALYZE TABLE强制刷新统计信息(尤其在大批量INSERT/DELETE后) - 避免
WHERE a = ? AND b IN (?, ?, ..., ?)中b的IN列表超 200 项;拆成多个查询或改用临时表JOIN - 索引字段顺序要匹配最常用过滤组合,例如高频查
status = 'active' AND created_at > '2024-01-01',就建INDEX(status, created_at),而非反过来
连接池配多大才不拖垮 MySQL?关键看 max_connections 和 wait_timeout 协同
应用层连接池(如 HikariCP)设 100,但 MySQL max_connections=151 默认值极易打满,且空闲连接不及时释放会持续占用内存和锁资源:
- 先调高
max_connections(建议按峰值 QPS × 平均查询耗时 × 1.5 估算,但不超过 OS 文件描述符限制) - 把
wait_timeout从默认 28800(8 小时)降到 60–300 秒,配合连接池的idleTimeout使用 - 禁止应用代码中手动
CONNECTION.close()后再复用,MySQL 不支持连接重用,必须由连接池管理生命周期
真正难的是锁竞争与缓冲区争用,这两个问题不会因为加了索引或调了连接数就自动消失——它们藏在慢日志的 Rows_examined 和 InnoDB_row_lock_waits 指标背后,得盯着看。










