MySQL:MTS 能够DML和DDL 混合并发执行吗?
对这个问题,我一直比较模糊,因为我们通常说的基于LOGICAL_CLOCK的并发方式是说的DML语句。现进行分析和测试用的5.7.22的版本,简单记录如下。
一、考量维度
我们知道在MTS判定并发的时候主要是根据last commit进行判定是否能够并发,那么我们需要确认的就是;
- 主库端DDL是否能够和DML生成同样的last commit。
- 从库端是否有特殊的逻辑,阻碍DDL和DML一起并发执行。
二、主库端last commit的生成方式
对于对一个问题,我翻了一下代码,因为DDL实际是statment模式的binlog,因此它获取last commit的位置和row格式的DML是不同的,在如下位置:
MYSQL_BIN_LOG::commit:
if (!cache_mngr->stmt_cache.is_binlog_empty()) //如果是语句模式 走这里
{
/*
Commit parent identification of non-transactional query has
been deferred until now, except for the mixed transaction case.
*/
trn_ctx->store_commit_parent(m_dependency_tracker.get_max_committed_timestamp());
if (cache_mngr->stmt_cache.finalize(thd))
DBUG_RETURN(RESULT_ABORTED);
stmt_stuff_logged= true;
}
很显然这里判定是stmt模式的cache如果不为空,也就是说是statment的binlog格式,DDL刚好就是这种格式(传统的DML 用statment的不考虑了),而last commit生成基于的依旧是m_dependency_tracker这个变量。而m_dependency_tracker就是order commit的时候commit的最后更新事务的last commit,这和DML生成的方式没有区别。也就是说貌似他们可以生成同样的last commit。
需要测试一下,因为怕有其他逻辑的处理,测试的方式肯定就是通过GDB不中断其他线程的方式来进行,断点就在
- Transaction_ctx::store_commit_parent:这里可以看到DML和DDL存储的last commit是否是相同值。
- MYSQL_BIN_LOG::ordered_commit:当然这个时候DDL和DML都获取了last commit,但是binlog还没写GTID也没有生成。
测试的时候肯定是要做不同表的DDL和DML,否则会出现metadata lock。我这里用的语句就是
主库(瞎写的表名和列名):
delete from dmlt2 limit 1;
alter table ddlt1 add oop int;
我们先让DML的线程先做MYSQL_BIN_LOG::ordered_commit函数,这样DML的binlog生成在DDL之前,GTID在DDL之前。 测试得到结果如下:

GDB截图:

这里就很明显看到了DDL和DML生成了同样的last commit 10,DML的GTID是40,DDL的GTID是41。到这里貌似主库端生成last commit已经有了并发的基础。
三、从库端是否能够并发
在大概看了一下协调线程分发事务的逻辑后,我感觉好像没问题,可以并发,因此我们保证slave_preserve_commit_order=0。这个时候,如果能够并发那么DDL的GTID是41,DML的GTID为40,如果我使用innodb row lock 来堵塞,DML的执行,如果DDL能够并发执行,肯定就会出现gap,也就是:1-39:41,因为DDL也并发了,不用等DML执行完成,如果不能并发就是1-39,而协调线程处于等待状态。
同时我们也可以观察replication_applier_status_by_worker视图,因为每个worker线程分配了一个GTID,那么各自应该能看到。
方式从库先先执行如下:
begin;
select * from dmlt2 for update; 模拟 DML trx 不能回放
好了我们来观察一下,截图如下:

GTID 2 3 4先不管他,因为我设置了slave_skip_errors ALL,这个在某些情况下是会触发BUG的,参考如下:
好像提交了500多天了还没fixed。
这里重点关注的是5-39:41 说明 DDL也并发了。 我们来看看replication_applier_status_by_worker视图如下:

因此他们确实并发了。因为用的版本比较低5.7.22,至少这个版本看起来DDL和DML是能够同时并发执行的,当然可以去看DEBUG代码,但是稍微费时,就这样测试就可以了。