并发查询变慢主因是锁、缓存、连接三者叠加:未走索引致全表扫描;间隙锁阻塞;buffer pool不足引发频繁刷脏页;连接数打满或线程竞争严重。

查询没走索引导致全表扫描
这是并发查询变慢最常见原因。当多个请求同时执行 SELECT 且 WHERE 条件列没建索引,MySQL 就得反复读取整张表,磁盘 I/O 暴涨,CPU 和 buffer pool 压力陡增。
- 用
EXPLAIN检查执行计划,重点关注type是否为ALL或index,key是否为NULL - 复合查询注意最左前缀匹配,比如有索引
(a, b, c),WHERE b = ?仍不会命中 -
隐式类型转换会让索引失效,例如字段是
VARCHAR但传入数字参数:WHERE user_id = 123(而user_id实际是字符串)
行锁升级或间隙锁阻塞高并发
InnoDB 在可重复读隔离级别下,范围查询(如 WHERE status IN (1,2))可能触发间隙锁(Gap Lock),导致多个事务互相等待,看起来像“查询卡住”。
- 用
SELECT * FROM information_schema.INNODB_TRX查当前活跃事务,结合INNODB_LOCK_WAITS看谁在等谁 - 临时降低隔离级别到
READ COMMITTED可避免间隙锁(但需评估一致性风险) - 尽量用等值查询代替范围条件;若必须范围查询,考虑加覆盖索引减少锁粒度
buffer pool 不足 + 频繁刷脏页
并发查询多时,若 innodb_buffer_pool_size 设置过小(比如默认 128MB),会导致大量数据页频繁进出内存,同时后台线程拼命刷脏页(innodb_io_capacity 不够也会拖慢)。
- 监控
SHOW ENGINE INNODB STATUS中的Buffer pool hit rate,低于 95% 就该扩容 - 生产环境建议设为物理内存的 50%–75%,但不要超过 80%
- 检查
innodb_log_file_size是否太小——日志频繁切换会强制刷脏页,放大 I/O 压力
连接数打满或线程竞争严重
当并发请求数超过 max_connections,新连接会被拒绝或排队;即使没超限,线程创建/销毁开销、锁竞争(如 LOCK_thread_count)也会让查询响应时间抖动增大。
- 用
SHOW STATUS LIKE 'Threads_connected'和'Threads_running'对比实时连接负载 - 应用层务必启用连接池(如 HikariCP、mysql-connector-python 的
pool_size),避免短连接风暴 - 避免在事务里做 HTTP 调用、文件读写等耗时操作,否则连接被长期占用,实际并发能力远低于配置值
SELECT trx_id, trx_state, trx_started, trx_wait_started, trx_mysql_thread_id, trx_query FROM information_schema.INNODB_TRX WHERE trx_state = 'LOCK WAIT';
真正卡住的往往不是 SQL 写得差,而是锁、缓存、连接三者叠加作用的结果。调优时别只盯着单条语句的执行时间,先看 Threads_running 是否持续高位,再查锁等待,最后才优化 SQL。











