龙空技术网

MySQL:5.7中delete长期处于preparing状态简析

杨建荣的学习笔记 117

前言:

今天兄弟们对“mysqldelete慢”都比较关怀,各位老铁们都需要剖析一些“mysqldelete慢”的相关内容。那么小编也在网上汇集了一些有关“mysqldelete慢””的相关资讯,希望朋友们能喜欢,同学们快快来了解一下吧!

仅做状态切换观察,不做深入研究

一、问题展示

实际这里是是一个delete in语句。

mysql> show processlist;
+----+------+-----------+------+---------+------+-----------+-----------------------------------------------------------------------------+-----------+---------------+
| Id | User | Host | db | Command | Time | State | Info | Rows_sent | Rows_examined |
+----+------+-----------+------+---------+------+-----------+-----------------------------------------------------------------------------+-----------+---------------+
| 2 | root | localhost | ttt | Query | 14 | preparing | delete from testout2 where id not in(select id from testin2 where name='t') | 0 | 37151 |
| 7 | root | localhost | | Query | 0 | starting | show processlist | 0 | 0 |
+----+------+-----------+------+---------+------+-----------+-----------------------------------------------------------------------------+-----------+---------------+
2 rows in set (0.00 sec)

mysql> show processlist;
+----+------+-----------+------+---------+------+-----------+-----------------------------------------------------------------------------+-----------+---------------+
| Id | User | Host | db | Command | Time | State | Info | Rows_sent | Rows_examined |
+----+------+-----------+------+---------+------+-----------+-----------------------------------------------------------------------------+-----------+---------------+
| 2 | root | localhost | ttt | Query | 15 | preparing | delete from testout2 where id not in(select id from testin2 where name='t') | 0 | 37744 |
| 7 | root | localhost | | Query | 0 | starting | show processlist | 0 | 0 |
+----+------+-----------+------+---------+------+-----------+-----------------------------------------------------------------------------+-----------+---------------+
2 rows in set (0.01 sec)

大概就是这样一个语句,处于preparing 状态,问delete为什么会处于这个状态?

二、简析

对于preparing状态一般归结为优化阶段,并还没有进入实际的语句执行阶段。但是这里有所不同。并且我们知道一般delete执行慢一般状态会处于,

updating子查询的sending data中

但是这里是preparing状态。那么为什么会处于这种状态呢?简单分析一下。

我们知道5.7的delete in实际上还是用的DEPENDENT SUBQUERY。这个类似exists的操作,都会用外层查询驱动内层。稍微跟了一下5.7的delete in(不做详细研究),发现所有的query block并没有完全完成优化,实际上这种子查询会在调用Item_subselect::exec的时候通过

if (!unit->is_optimized() && unit->optimize(thd))

进行判断,如果unit->is_optimized()判断为false,则会调入unit->optimize(thd),对subquery的query block进行优化,而优化结束后查询的状态会被设置为preparing。也就出现了这种现象,如下:

T@2: | | | | | THD::enter_stage: 'init' /opt/percona-server-locks-detail-5.7.22/sql/sql_delete.cc:79
T@2: | | | | | | | THD::enter_stage: 'System lock' /opt/percona-server-locks-detail-5.7.22/sql/lock.cc:323
T@2: | | | | | THD::enter_stage: 'updating' /opt/percona-server-locks-detail-5.7.22/sql/sql_delete.cc:425
T@2: | | | | | | | | | | THD::enter_stage: 'optimizing' /opt/percona-server-locks-detail-5.7.22/sql/sql_optimizer.cc:151
T@2: | | | | | | | | | | THD::enter_stage: 'statistics' /opt/percona-server-locks-detail-5.7.22/sql/sql_optimizer.cc:386
T@2: | | | | | | | | | | THD::enter_stage: 'preparing' /opt/percona-server-locks-detail-5.7.22/sql/sql_optimizer.cc:494
T@2: | | | | | THD::enter_stage: 'end' /opt/percona-server-locks-detail-5.7.22/sql/sql_delete.cc:538

Item_in_subselect::val_bool
->st_select_lex_unit::optimize
->st_select_lex::optimize
->JOIN::optimize
->THD::enter_stage(stage_preparing)

这种现象当前看起来是5.7(5.7以下没有测试)中DML语句带in/not in常见的,也可以归结为状态转换稍微考虑欠妥,可能开发大佬也没意识到这个状态已经变为DBA诊断问题的一个主要手段。而到了8.0状态已经更加精细,有了一些细微的变化,而且8.0.21后支持DML 中的in使用semi join(not in还不行,但是状态是executing)优化。所以我们简单的将其归结为正在执行类似sending data状态即可。需要优化的是将这种delete改写为联合delete形式。

标签: #mysqldelete慢