没加索引或索引失效导致全表扫描
这是最常见的原因:当
WHERE、
ORDER BY、
JOIN字段上没有有效索引,MySQL 就只能一行行读完整张表。哪怕只有几万行,I/O 和 CPU 开销也会陡增。
检查方法很简单:
EXPLAIN SELECT ...看
type是否为
ALL(全表扫描),
key是否为
NULL,
rows是否远超实际匹配数。 复合索引要注意最左前缀原则,
(a,b,c)无法加速
WHERE b = ?
OR条件中只要有一边没索引,整条语句可能退化为全表扫描 对索引字段做函数操作(如
WHERE YEAR(create_time) = 2024)会直接让索引失效 隐式类型转换(比如字符串字段查数字:
WHERE mobile = 13800138000)也可能绕过索引
查询返回了太多无用数据
用
SELECT *或没加
LIMIT的分页查询,在数据量大时非常危险。网络传输、内存排序、客户端处理都会拖慢整体响应。
典型例子:
SELECT * FROM orders ORDER BY created_at DESC LIMIT 100000, 20—— MySQL 仍要先定位前 100000 行,再取 20 行,效率极低。 只查真正需要的字段,避免
*深分页优先改用游标方式(比如记录上一页最大
id,下一页查
WHERE id )大结果集导出类需求,考虑用
mysqldump或服务端流式读取,别在应用层一次拉完
锁等待和事务长跑阻塞查询
慢查询不一定是 SQL 本身慢,也可能是被别的连接卡住了。比如一个未提交的事务长时间持有行锁/表锁,后续所有涉及这些行的
SELECT ... FOR UPDATE或写操作都会排队等待。
查当前阻塞情况:
SHOW ENGINE INNODB STATUS\G看
TRANSACTIONS部分;或者查
information_schema.INNODB_TRX和
INNODB_LOCK_WAITS。 避免在事务里做 HTTP 调用、文件读写等外部耗时操作 写操作尽量短平快,别把
UPDATE套在大循环里 读多写少场景可考虑开启
READ COMMITTED隔离级别,减少锁范围 注意
AUTO_COMMIT=OFF后忘记
COMMIT的“幽灵事务”
服务器资源或配置不合理
即使 SQL 写得再好,硬件跟不上或参数设错也会拖慢查询。比如
sort_buffer_size太小导致
ORDER BY落盘排序,
innodb_buffer_pool_size远小于数据量造成频繁磁盘读。
关键指标要看:
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'(磁盘读次数) vs
'Innodb_buffer_pool_read_requests'(总读请求),比值超过 1% 就说明缓存严重不足。 缓冲池大小建议设为物理内存的 50%–75%,但别超过可用内存,否则触发系统 OOM 临时表过大(
Created_tmp_disk_tables高)说明
tmp_table_size和
max_heap_table_size设太小 高并发下
innodb_thread_concurrency设为 0(默认不限制)通常更稳,设死值反而可能限制吞吐
慢查询背后往往是多个因素叠加,比如一条没索引的语句在缓冲池不足时,既触发大量磁盘 I/O,又因排序落盘进一步放大延迟。定位时别只盯着 SQL,得从执行计划、锁状态、服务器指标三层一起看。
