MySQL慢查询优化案例一

这是学习笔记的第 1946 篇文章


  最近整理了一个慢日志排行榜,可以从一个整体的角度来看到整个数据库方向的慢日志情况,我的初步目标是坚持一段时间,每天争取优化一个慢查询语句。通过这个过程来梳理一些SQL性能问题的模型,准备在这个基础上把MySQL的优化服务做深入一些。

首先排行榜中看到的是这样的慢查询语句,就从这个开始落实了。

慢查询的内容如下:

执行时长 IP 端口 数据库 用户 SQL语句 执行次数 返回行数 检查行数 创建时间
69 xxxx 4306 test_log srv_test_log_rwl SELECT * FROM test_log.log order by id LIMIT 0, 10 5 10 10 4/11/2019
73 xxxx 4306 test_log srv_test_log_rwl delete from cmec_log.log where cdate<'2018-09-04 00:00:00' 13 0 276835 4/11/2019

对应到慢查询分析平台中查看的快照数据如下:

640?wx_fmt=png

其实通过这些信息也可以看到一些关联,比如这个场景中系统的负载和慢日志的个数是成正比的,两个曲线是可以互相印证。同时系统中存在的慢日志确实不少。通过慢日志排行榜能够把瓶颈SQL快速定位出来。

针对这些信息,在慢日志排行榜中对两条top sql进行分析。初步的结论是:

1.log表存在大量的碎片,表中的自增列是6000万左右,但是表中的真实数据量偏低,碎片率超40%。

mysql> select count(*)from log where id<20000000;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)


2.SQL语句优化

如下的语句执行效率较低,看需求是查询最近的日志信息。

SELECT * FROM test_log.log order by id LIMIT 0, 10

如果转化为如下的方式效果会更好:

select max(id)from  cmec_log.log ;


3.建议对该表做分表设计

通过SQL可以看出数据是有保留周期的,在这种情况下是建议采用分表,使用周期表的方式来进行拆分的。

delete from test_log.log where cdate<'2018-09-04 00:00:00'

在和业务方进行沟通后,他们已经在着手改进了,会把日志的信息主要存入ES里面。

在全文检索方面,MySQL尽管有全文索引,但是代价相对比较大的。在这个场景里面,为了满足业务需求,里面创建了3个全文索引。

单表包含碎片已经有40G左右了,全文索引对应的文件相对会复杂许多。

640?wx_fmt=png

经过沟通和确认,我们决定先可以把数据碎片做下清理,只保留近1个月的日志信息,优化后的数据文件大小从40G降到了 7.4G,算是不小的改进了。

在这里也有一些小技巧和经验,因为是日志类数据写入,所以在这个业务场景里面不存在事务。

我们可以把表里的数据做下切换,比如创建一个新表,把增量数据复制过去。

create table  test_log_arch.log like test_log.log ; 

然后把数据补录到新表中,因为这个过程中数据写入还在原表,所以整个数据补录的过程可以理解是离线操作,不影响在线的数据写入。

insert into test_log_arch.log select * from test_log.log where cdate between '2019-03-01 00:00:00' and '2019-04-12 12:00:00';

在这里需要考虑对事务降维,如果我们保留一个很长的时间窗口,很可能会出现异常:

mysql> insert into test_log_arch.log select * from test_log.log where cdate between '2019-03-01 00:00:00' and '2019-04-12 12:00:00';

ERROR 1197 (HY000): Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again

可以尽可能把一个大事务拆解成多个可控的操作,逐步迭代,直到追上时间窗口,比如当前时间是13:45,则我们可以缩小范围,逐步写入增量数据。

 insert into test_log_arch.log select * from test_log.log where cdate between '2019-04-12 00:00:00' and '2019-04-12 13:40:00';


 insert into test_log_arch.log select * from test_log.log where cdate between '2019-04-12 13:40:00' and '2019-04-12 13:45:00';

切换表之后,还有一小部分的增量数据需要补录,我们可以把时间范围故意放大,保证时间范围内的数据都可以正常写入。

alter table test_log.log rename to test_log_arch.log_bak;

alter table test_log_arch.log rename to test_log.log;


 replace into test_log.log select * from test_log_arch.log_bak where cdate between '2019-04-12 13:45:00' and '2019-04-12 14:00:00';

最后的数据补录,可以充分利用replace的方式来避免重复数据的潜在可能性。



640?


猜你喜欢

转载自blog.csdn.net/weixin_36250635/article/details/89369208