MySQL 复制对事务的处理
由于职业成了专职的mysql的dba,没有原先oracle那么又大又全面的文档,很多东西只能看着源代码一点一点理解,最近对复制这个dba接触较多的特性做了一些研究,这次说的是复制实现中对事务的处理。
由于线上主流版本是5.5.3x,所以这次我主要研究的也是这个批次的版本,对于新增了gtid的5.6,并行复制的5.7以后再作研究。
众所周知,mysql的最原始实现myisam是不支持事务的,在innodb出现之前,复制的处理很简单,任何执行的语句全部发送给slave就可以了,没有事务那么多事情,但是从innodb开始,mysql的事务引擎越来越强,mysql最重要的复制特性也就需要对事务的支持,但以现在的目光来看,直到gtid实现之前,只能说是在无事务的实现上套了一个事务的壳子,下面要介绍的,就是这个壳子。
首先从mysql的处理线程开始说。
mysql对于所有客户端的连接,都有一个服务端的处理线程对应,这个线程的实现是在sql_class.h里面的THD类,复制的一部分最基础的数据结构及方法都在这个类里面。
复制事务实现最重要的结构之一,event缓存会在受到任何触发event产生的动作之后,调用初始化函数生成该线程独有的复制event缓存,一般而言,当线程所属会话首次发起事务的时候,这个缓存就会被创建,可以参见THD类的
binlog_start_trans_and_stmt方法。如果是row格式的复制,在之后还会初始化table_map。
如果一切正常,当事务中执行事务语句(DML)的时候,所以这些语句形成的event都会被记录到缓存里面,等到commit事务以后,这个缓存的数据会立即被刷入磁盘上的binlog文件里面,之后通过slave请求发送到slave,在slave重复执行。
顺便提一下,mysql的binlog里面的事务都是相互独立按顺序排列的。
现在开始假设意外的发生。
正常意外一:事务执行中rollback了。
如果在一个事务中,所有执行的语句都是在innodb表上执行(对于多个事务引擎如tokudb等并用尚未测试),直接执行rollback,会导致mysql直接清除缓存,不会写入任何event记录到binlog。
正常意外二:事务执行中使用了非事务表(myisam),然后rollback了。
在一个事务中,即有事务表,也有非事务表,如果正常执行,则没有任何问题,当如果在非事务表上的修改执行了,复制设置为statement的时候,binlog会把所有执行的event全部记录下来写入日志,包括事务与非事务语句,然后在最后面加上rollback。如果复制格式设置为row,那么所有事务表相关的数据都会被清理掉,而非事务表上的数据会被标记为已提交事务写入binlog。
正常意外三:事务执行顺序如下:事务语句1-》非事务语句2-》save point -》事务语句3 -》 rollback to savepoint -》 事务语句4 -》提交
对于上面的情况,statement情况下,所有执行的语句包括savepoint与rollback都会按顺序被记录到binlog。row情况下,非事务语句与事务语句是分别记录到binlog中,非事务执行event会全部被记录,事务执行event会连带savepoint与rollback都记录下来。
正常意外四:事务执行顺序如下:事务语句1-》savepoint-》非事务语句2-》事务语句3-》rollback to savepoint-》事务语句4 -》 commit
同上。
异常意外一:master正常提交了,但是在slave执行失败了。
master执行完成,提交之后,slave接收到relay然后执行,执行期间由于意外原因失败了。这个时候,statement格式下,mysql会重新回到事务begin处重新执行,如果再次失败的话就会报错。row模式下情况类似,区别在于,如果是包含非事务表执行的事务,非事务表上的event不会被重新执行。
来自:http://my.oschina.net/llzx373/blog/282768