
慢查询并不是一处的问题,在执行计划错误判断、索引失效以及语句书写不当的情况下才会出现。数据库CPU飙升、接口超时、连接池耗尽等状况通常是由一些高频次的慢SQL语句引起的。解决之道应该从慢查询日志和EXPLAIN开始,而不是盲目地扩大规模。
开启 slow_query_log 和设置合理的 long_query_time 之后,使用 pt-query-digest 或者 mysqldumpslow 来汇总出执行时间长、频率高的SQL语句。然后进行执行计划分析,主要关注四个方面:type 是否为 ALL(全表扫描)、key 是否命中预期的索引、rows 预估扫描行数是否异常高、Extra 中是否有 Using filesort 或者 Using temporary。如果 type=ALL 并且扫描行数接近整个表的话,那么可以认为该索引没有发挥作用。
索引优化原则为:等值在前、范围在后、覆盖优先。联合索引按照WHERE等值列→范围列→ORDER BY列的顺序排列,防止最左前缀断裂。索引列不能套函数,如DATE(create_time)和SUBSTR(col)会使B+树失效;字符串与数字比较引起的隐式转换也很危险。对于只读操作使用覆盖索引来消除回表,统计类查询把实时聚合下沉到汇总表或者定时预计算中。
查询重写是承接索引优化的重要环节之一,把深分页 LIMIT 100000,20 改成游标分页 WHERE id > last_id LIMIT 20;把 OR 分成两个 UNION ALL;子查询转换成 JOIN;SELECT * 只保留必要的字段。原来的轨迹查询语句中使用了函数套函数和 %CAR001 前缀进行模糊匹配,执行时间为3~6秒左右,在经过优化之后,时间缩短至0.03秒以内,扫描行数由12万减少到几十行。
治理闭环要有验证和防止回退的措施,在每次修改之前之后都要使用EXPLAIN ANALYZE来比较执行时间、扫描行数以及成本,确保索引真的起作用了。上线之后要一直监测慢日志TOP10、缓冲池命中率、锁等待和连接数等指标,把SQL审核提前到发布过程中去,防止新的代码又把索引搞坏了。
慢查询治理的核心就是把可以运行的SQL语句改成能够利用索引、减少扫描的数据量以及降低排序次数的SQL语句来执行。执行计划决定了方向,索引构成了框架,而重写则细化了具体的操作方式,最后通过数据来检验效果。定期检查比紧急抢修更能有效地控制服务器的负载情况。