MySQL慢查询优化从定位开始:打开慢查询日志,找出真正拖慢业务的SQL,再用执行计划分析瓶颈,最后针对性改写。本文按这条路径给出一套可操作的排查流程。
开启慢查询日志与监控基线
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
SET GLOBAL log_output = 'TABLE';
SHOW GLOBAL STATUS LIKE 'Slow_queries';
long_query_time设为1秒,记录超过1秒的查询;log_output用TABLE方便直接查。业务高峰期开启前先评估日志量,避免拖慢写路径。
SELECT * FROM mysql.slow_log
ORDER BY query_time DESC LIMIT 10;
按耗时排序找出最贵的查询,逐条做执行计划分析。
EXPLAIN怎么看:索引、扫描行数、类型
EXPLAIN SELECT order_id, amount FROM orders
WHERE user_id = 100 AND status = 1
ORDER BY created_at DESC LIMIT 20;
看关键列:type出现ALL说明全表扫描,rows越大代价越高,key为NULL说明索引没被用上。possible_keys给出可选索引,key是实际选中的,二者差别大说明优化器没选对。
索引设计与覆盖索引
查询是”等值过滤+排序+取少量列”的模式,建联合索引时把等值列放前面、排序列放后面,再把要取的列全部放进去做成覆盖索引,可以跳过回表。
ALTER TABLE orders ADD INDEX idx_user_status_created (user_id, status, created_at);
联合索引遵循最左前缀:user_id、status、created_at都能被查询用到。覆盖索引查询时extra出现Using index,表示全部数据从索引里取,性能最好。无索引的created_at单独排序,explain里会显示filesort,要尽量避免。
分页与关联查询的改写
深分页offset越大越慢,改成基于主键的游标分页:
-- 慢:LIMIT 100000, 20
SELECT * FROM orders ORDER BY id LIMIT 100000, 20;
-- 快:游标分页
SELECT * FROM orders
WHERE id > 100000
ORDER BY id LIMIT 20;
关联查询里,先过滤小表再JOIN,MySQL优化器会决定驱动表,但统计信息不准时要手动强制index hint。避免在索引列上用函数:WHERE DATE(created_at)=CURDATE()会放弃索引,改成created_at >= 区间写法。
COUNT、OR与隐式转换的坑
-- 慢:OR导致索引失效
SELECT * FROM t WHERE name = 'a' OR name = 'b';
-- 快:IN
SELECT * FROM t WHERE name IN ('a','b');
-- 隐式转换:字符串列用数字比较,索引失效
SELECT * FROM t WHERE uid = 1001; -- uid是varchar时改成'1001'
COUNT(*)统计行数时用count(主键)或count(1),不要count(带NULL的列),返回语义不同。优化完用Performance Schema或sys.schema_index_statistics看索引命中率,逐步把冗余索引清掉,减少写入维护成本。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql-man-cha-xun-ding-wei-yu-sql-you-hua-cong-ri-zhi-dao/