
上周有个朋友问我:他的网站一个页面加载要5秒,查了半天发现是一个SQL查询就要3.8秒。他问我怎么办——其实大部分MySQL慢查询的问题,都是索引没建对。
先找到慢查询
MySQL有个"慢查询日志"功能,会自动记录执行时间超过指定阈值的SQL。在my.cnf里加上这几行:
slow_query_log = 1
long_query_time = 1
slow_query_log_file = /var/log/mysql/slow.log
重启MySQL以后,所有执行超过1秒的查询都会被记录下来。看日志就能知道哪些SQL拖慢了你的网站。
用EXPLAIN分析查询计划
找到慢查询以后,用EXPLAIN命令分析它。在你的SQL前面加一个EXPLAIN,MySQL会告诉你它打算怎么执行这个查询。
重点看这几个字段:
type列:如果显示ALL,说明是全表扫描——这是最慢的。好的查询应该显示ref或者range。
rows列:这个数字越大越慢。如果显示100000,说明MySQL要扫描10万行数据才能返回结果。
Extra列:如果出现"Using filesort"或者"Using temporary",说明MySQL在用临时表或者文件排序,通常意味着需要优化。
索引怎么建才对
最常见的慢查询原因:WHERE条件里的字段没有索引。比如你的查询是SELECT * FROM orders WHERE user_id = 123 AND status = 'paid',那(user_id, status)就应该建一个联合索引。
建索引的几个原则:
第一,最左匹配原则。联合索引(a, b, c)只能加速WHERE a=1、WHERE a=1 AND b=2、WHERE a=1 AND b=2 AND c=3这三种查询。WHERE b=2用不上这个索引。
第二,选择性高的字段放前面。user_id的选择性比status高(user_id有几万个不同的值,status只有几个),所以索引应该是(user_id, status)而不是(status, user_id)。
第三,不要建太多索引。每个索引都会让写入变慢。一张表的索引不要超过5个。
常见的慢查询模式和解法
SELECT * FROM table WHERE id IN (子查询)——改成JOIN。MySQL 5.7以下的版本对子查询优化很差,改成JOIN以后速度可能提升10倍。
SELECT * FROM table ORDER BY create_time DESC LIMIT 10——给create_time建索引。如果不建索引,MySQL会扫描整张表然后排序。
SELECT COUNT(*) FROM table——如果表很大(超过100万行),这个查询会很慢。可以估算:SHOW TABLE STATUS里的Rows字段是近似值,大部分场景够用了。
什么时候该分表了
单表超过500万行以后,查询性能会明显下降。这时候可以考虑分表——按时间分(每个月一张表)或者按用户ID分(hash取模)。
分表是最后的手段。在分表之前,先把索引优化好、把慢查询都解决掉。我见过不少分了表以后查询反而更慢的案例——因为分表以后JOIN变复杂了。
MySQL优化是个大话题,但80%的慢查询问题都能通过正确建索引解决。先把EXPLAIN用熟,大部分问题就迎刃而解了。
还木有评论哦,快来抢沙发吧~