Mysql 出现ER_GTID_NEXT_TYPE_UNDEFINED_GROUP的第二种可能

之前讨论过Mysql出现以下错误的一种可能:

When @@SESSION.GTID_NEXT is set to a GTID, you must explicitly set it to a different value after a COMMIT or ROLLBACK

下面描述的另一种可能来自于taobao的Mysql内核月报. 主要涉及到insert delayed语句.

关于insert delayed

下面是关于insert delayed的几个描述:

bug描述

在master上执行以下脚本, 可以在slave上看到复制的error:

/opt/mysql/bin/mysql -uroot -h127.0.0.1 -e "CREATE TABLE a (a int) ENGINE=MyISAM"

for i in {1..2}
do
/opt/mysql/bin/mysql -uroot -h127.0.0.1 -e "insert delayed into test.a values(1)" &
done

分析

insert delayed的执行可以看做分为两个部分: 生产者和消费者.

同时执行的两个insert delayed, 会触发两个生产者线程将两次执行排队到队列中, 等待消费者进行消费

消费者线程的大概流程是:

handle_delayed_insert
     Delayed_insert::handle_inserts
          while(row = rows.get()) {
               write binlog
               write table
          }
     trans_commit_stmt

其形成的binlog形式是:

GTID_DESC
BEGIN
row_event 1
row_event 2
COMMIT

这段binlog在slave上重放时, row_event 1结束后会进行commit, 对GTID执行set_undefined (如果不理解这一段, 请参看之前的讨论)

执行row_event 2时就找不到GTID的描述, 故error

何时commit

上面的分析有一部分是有点奇怪的, 就是``row_event 1结束后会进行commit”.

对比另外一个场景, 如果进行一个大的insert, 比如insert into a values(1),(2),(3),...,(100000), 形成的binlog形式与上面一模一样, 但仅在最后一个row_event时进行commit

造成这种差异的原因在于标识STMT_END_F, 在bug的场景中, 两个row_event都带有标识STMT_END_F, 故会在每个row_event执行后进行commit

复盘

这个bug主要的成因是两个并行insert delayed会组合在一起向master提交, 且提交成功. 而根据binlog, slave执行时会进行两次commit, 但共用了同一个GTID_DESC, 所以会发生错误.

comments powered by Disqus