本文作者:鲁越
insert delay在GTID下异常binlog格式
一、问题描述
1) 客户反馈,两个RO同时复制异常,程序读不到最新的数据。
2) 上线看了一下报错信息、数据库版本5.6。尝试执行了一下stop slave;start slave; 没办法快速恢复。问题1:此时,如果是你,如何去处理这个case?
3)观察了一下主库的CPU、IO等状态,让客户先把读写流量都切到主库。先恢复业务
4)通过迁移RO的方式来恢复RO与主实例的主从
5)同时通过报错信息,解析报错对应的binlog位点,详细分析1837报错的原因。
5.1)发现报错位点对应的事物为对同一张表进行的两个insert操作,并无什么异常。
5.2) 很自然的想到看看表结构,一看发现了怪异之处。问题2:怪异之处在哪里?
CREATE TABLE `log_online_players` (
`count` int(4) unsigned NOT NULL COMMENT '在线人数',
`time` int(4) NOT NULL COMMENT '时间',
KEY `NewIndex1` (`time`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin
5.3) Myisam这种非事务引擎,每一个DML操作都会被强制提交,也就是说,在这个case里面,每一个insert操作应该对应binlog里面的一个event。但是我们分析binlog的时候发现,一个event里面出现了2条insert语句。这条binlog在备机回放的时候,SQL线程执行完这个event里面的第一个insert语句以后就对整个事物提交了,这样造成了上述的报错。
5.4)报错的原因找出来了,但是为什么会出现这个问题呢?这个问题我非常的迷惑,现在核心的点就是为什么在对Myisam引擎执行insert操作的时候,一个事物里面会出现了2条insert。
5.5) 复现步骤
1# 复制客户的库表接口到测试环境
2# 模拟客户的插入流量 insert_normal.sh,发现主从复制并没有断。分析了一下binlog,也是跟我所认知的情况一致:在myisam引擎中,一个event里面只会存在一条insert,不可能出现一个event里面有2条或者多条DML记录的情况发生。
3# 我开始思考,有没有可能是我测试的环境下并发不够,有没有可能是因为在高并发情况下MySQL的机制有bug。在把并发提高,测试实例的TPS量与客户实例TPS量基本一致,甚至超过客户实例的情况下依然没办法复现。这个时候我陷入了瓶颈。
4# 这个时候,去主库上开启了Glog,执行了show processlist,发现客户真正执行的语句并不是insert into,而是insert delay into。在执行insert delay into语句的情况下,可能连续执行N个INSER操作,生成多个INSERT事件,而在生成GTID时,就只对应一个event。明显的,DELAYED语法生成了违反GTID限制的binlog。这个语法在GTID模式下应该是禁止。
5# 马上去主库上开了一下Glog,发现客户的确是执行的insert delay操作。整个case就真相大白了。
6# 自己做一次复现
二、扩展知识
1)针对GTID,官方主要有三个限制
- CREATE TABLE ... SELECT statements
- CREATE TEMPORARY TABLE statements inside transactions
- Transactions or statements that update both transactional and nontransactional tables.
2)insert delay
往数据库里插入数据的标准命令是INSERT,而DELAYED的意思,则是异步插入。也就是说,MySQL接受这个命令后,保存命令就直接返回给客户端,因此用户会发现在某些场景下INSERT DELAYED性能优于”INSERT,实际上只是更快的返回,而非更快的完成。
三、思考
1、看到报错信息的时候,如果优先google一下,整体的排查速度会快的多
2、RO出了问题,就一直在排查RO,如果早一点上master上执行一下show processlist,这样是有很大可能看到insert delay这个语句的
3、5.6版本的CDB本身就是不支持myisam表的,迫于客户压力,还是给客户做了非标操作,最终造成故障。在没有能力100%的cover住的情况下,不要去做非标。