1. 慢SQL危害
慢SQL顾名思义,执行耗时比较久的SQL,长时间占有一个数据库连接。可千万不能小看一个慢SQL,会对数据库造成非常大的影响。
数据库是一个公共资源,会有多个接口、多个节点、甚至多个服务同时使用,资源非常宝贵,是后端系统的核心,在整个系统的流程中又很容易成为瓶颈。
慢SQL可谓是数据库性能杀手,长时间占有数据库连接,导致吞吐量急剧下降,如果仅有的一些连接都被慢SQl占有,客户端其他的请求会被阻塞,导致接口超时,服务不可用,发生雪崩。
另一方面看,慢SQL通常还伴随数据库服务端的内存和CPU资源的极大开销,进而影响到其他的SQL请求,非常影响服务的可用性。
2. 监控和分析
当出现慢SQL的时候,一般都会伴随着接口超时,MySQL服务端内存和CPU飙高,一般生产环境中会提前埋点,监控业务服务的运行情况,以及业务服务器调用MySQL的情况,另外MySQL本身也会提前添加监控,监控CPU/内存,长耗时SQL等。假如监控中显示是数据库的性能问题时,可以通过自身的经验肉眼分析,也可以通过一些手段来辅助分析。以下介绍两个辅助分析的方式。
2.1 开启慢SQL日志监控,发现和分析慢SQL
MySQL慢查询日志,就是当开关打开,MySQL会自动记录查询时间超过指定阈值的SQL执行日志记录下来。(生产环境下对性能要求较高的不建议开启,会有一定的性能损耗)
下面介绍几个相关的命令:
- slow_query_log 是否开启慢查询日志,默认不开启
- slow_query_log_file 慢查询日志记录的文件的位置
- long_query_time 阈值,单位s,默认10s,超过该阈值就会认为是慢SQL,在慢查询日志开启后会把查询日志记录到slow_query_log_file指定的位置。
慢查询日志的内容是怎样的?
D:\Program Files\MySQL\bin\mysqld.exe, Version: 5.7.39-log (MySQL Community Server (GPL)). started with:
TCP Port: 3306, Named Pipe: (null)
Time Id Command Argument
# Time: 2024-06-17T07:11:12.231138Z
# User@Host: root[root] @ localhost [127.0.0.1] Id: 2
# Query_time: 16.134351 Lock_time: 0.000896 Rows_sent: 0 Rows_examined: 7005
use xxx;
SET timestamp=1718608272;
/* ApplicationName=IntelliJ IDEA 2024.1 */ select * from xxx.friend_apply a join wanglai.friend_apply b order by a.friend_id;
分析慢SQL中可以关注以下几个指标
- Query_time sql查询消耗的时间
- Lock_time 获取锁消耗的时间
- Rows_sent 发送给 Client 端的行数
- Rows_examined 服务器层检查的行数(不计算存储引擎内部的任何处理)
2.2 查看sql执行计划(Explain)
让MySQL帮你分析这一条SQL的执行情况,只需要在执行的SQL语句之前加上explain即可,如下:
explain select * from xxx;
那么explain的结果应该如何查看呢?explain结果会有以下字段:
id,select_type,table,partitions,type,possible_keys,key,key_len,ref,rows,filtered,Extra
详细解析可参考网上文章。
2.2.1 type
性能表现: system > const > eq_ref > ref > range > index > all
system / const / eq_ref / ref 这几个类型在SQL编写本身上不会出现什么性能问题,都是命中索引(主键、唯一、普通索引)的单条记录。
range:就是范围查询,走了一个索引,但是只需要遍历部分索引就可以得到需要的结果。
index:就是查询整个索引,但是又只需要遍历整个索引就可以得到结果(比如,覆盖着索引的情况,无需返表查询)
all:就是遍历全表才能得到需要的结果,性能最差。
2.2.2 possible_keys / key
possible_keys: 可能使用到的索引
key: 真实使用到的索引
可以看到SQL语句的索引命中情况,以便调整SQL或者调整表结构,来命中索引,加速查询。
2.2.3 rows
遍历的数据行数,这个值越小越好,如果这个值很大,说明当前查询条件下需要遍历的数据行太多了,会影响效率。
2.2.4 extra
额外信息,会展示一些不好分类但是又很重要的信息,例如:
using filesort: 排序没有用上索引,这对性能也影响挺大
using temporary:使用临时保存了中间结果,常见于group by 和order by,这对性能影响也很大
using index:查询覆盖了索引,效率不错
3. 常见慢SQL原因和优化建议
第一反应肯定是SQL写的有没有问题,就是查询优化和执行查询这两个步骤。可能会出现索引失效的情况,导致查询全表。常见的索引失效情况如下:
- where条件类型不一致,导致MySQL隐式转换数据类型
- 条件上做了运算
- 不符合最左原则,like %开头
- 联合索引也有按照最左前缀
- 使用or,有些条件没有索引
- not null not in not exist等
- 使用了非索引列排序
- ...
索引失效,通常来说是编写SQL不细心,或者随着业务变化,数据内容发生了很大变化,导致原来的方案不再适用。解决办法可以通过explain分析相关SQL,分析索引命中情况,并按照上面的分析来避免写错SQL。
也有可能是条件不当,查询的数据行太多了。这需要根据具体业务来调整了。
以上是SQL本身就有问题。
也有可能存在其他问题:
- 连接阶段,从连接池获取连接比较久,这是业务服务请求的并发比较高,如果允许,可增加连接池数量,或者优化查询方式等。
- 执行查询阶段,可能因为并发太高导致获取锁耗时较高,这时候可以考虑读写分离等。