线上有哪些突发问题
在日常对网站或者应用进行维护时,可能会遇到一些 MySQL 突发性能问题:比如某个慢查询拖垮整个数据库,导致核心业务无法响应用户请求;或者由于活动、推广、推送产生的流量高峰导致数据库处理不了这么多并发请求。
对于这些突发情况,我们不可能有时间从头开始追溯和解决问题,老板要求的,以及你要立刻做的,就是让线上业务以最快时间恢复正常。
下面我们就来简单看一下有哪些短期可用的方案来快速解决这些问题,长期方案可以等到业务跑起来之后再去研究和设计,从而尽可能快地止损。
慢查询的原因和对应解决方案
首先,我们来看慢查询问题。
造成慢查询的原因通常是 SQL 查询没有有效的使用索引,问题的根源要么是没对查询字段设置索引,要么是 SQL 语句编写的时候没有合理使用索引,再者就是 MySQL 在生成执行计划的时候选错了索引,这种情况概率很低,但是也不排除这种可能。
未设置索引
对于没有设置索引的情况,可以通过紧急创建索引来解决,MySQL 5.6 以后的版本支持在线执行 DDL 语句
SQL 语句问题
对于 SQL 语句没有写好的情况,可以基于 MySQL 5.7 提供的 query_rewrite
功能将输入的 SQL 语句改写为另一种模式。
比如对于如下这条 SQL 语句
select * from `demo` where a + 1 = ?
a
是主键,但是不能使用索引:
如果改写成下面这样:
select * from demo where a = ? - 1
功能完全一样,但是可以使用索引:
如果要修改对应的业务代码再上线,这个流程就变成了,通过 query_rewrite 我们可以立即在线上更改 SQL 语句的执行模式:
insert into query_rewrite.rewrite_rules(pattern, replacement, pattern_database) values ("select * from `demo` where `a` + 1 = ?", "select * from `demo` where `a` = ? - 1", "");
call query_rewrite.flush_rewrite_rules();
使用这个功能需要先安装 Rewriter 查询重写插件
选错索引
对于 MySQL 选错索引的情况,可以通过强制使用索引的方式进行纠正。如果是 MySQL 5.7 以上版本,使用上面的 SQL 语句重写给对应的 SQL 语句添加强制使用索引语句是最快捷的解决办法。
慢查询日志记录和分析
当然,对于上述导致慢查询问题的前两种原因,是完全可以在本地开发/测试环境预先发现和解决的:只需要在本地/测试环境开启慢查询日志,记录慢查询语句并进行优化即可。
开启慢查询日志
你可以通过如下命令查看 MySQL 慢查询日志的默认位置以及是否开启了记录慢查询:
如果没有开启的话,可以手动进行开启:
set global slow_query_log = on;
你还可以自定义慢查询日志的位置:
set global slow_query_log_file = "/var/lib/mysql/slow_log.log";
慢查询默认执行时间是 10s,即 SQL 查询超过 10s 便认为这是一个慢查询,这个时间也可以手动设置,比如将其设置为 0.01s:
set global long_query_time = 0.01;
此外,还可以开启对未使用索引的 SQL 进行记录:
set global log_queries_not_using_indexes = on;
当然,上述配置也可以通过 MySQL 配置文件进行全局配置:
[mysqld]
slow_query_log = ON
log_queries_not_using_indexes = ON;
slow_query_log_file = /usr/local/mysql/data/slow.log
long_query_time = 1
慢查询日志分析
开启慢查询日志后,执行一些耗时的 SQL 查询,就可以在慢查询日志中看到记录的查询语句了:
我们简单看一下每个字段的含义:
- Time:执行查询的日期和时间;
- User@Host:执行查询的用户和客户端 IP;
- Id:是执行查询的线程 ID;
- Query_time:SQL 查询所消耗的时间;
- Lock_time:执行查询对记录锁定的时间;
- Rows_sent:查询返回的行数;
- Rows_examined:为了返回查询的数据所读取的行数。
这里我们核心关注的就是查询耗时、返回行数和读取行数,耗时越长,性能越差,返回行数/读取行数值越小,性能越差,这里由于 b
字段未设置索引,实际上执行了一次全表扫描。
由于慢查询日志中可能包含很多重复的 SQL,我们可以通过 MySQL 提供的 mysqldumpslow 工具对其重复记录进行计数统计:
其中 Count 字段对应的就是该模式 SQL 的慢查询次数。