项目需要做Es和数据库的同步,而手动在代码中进行数据同步又是Es的一些不必要的数据同步操作和业务逻辑耦合,所以使用的了读取mysql的binlog日志的方式进行同步Es的数据。
问题1:根据binlog同步数据的时候会不会出现业务逻辑利用事务操作数据的时候,当事务还没有提交的时候,是否能够读到binlog,也就是binlog的写入时机(是事务提交的之前写,还是事务提交后写)。 问题2:如果事务提交之前写入binlog,那么事务提交之前,事务回滚,那么binlog又会出现什么情况?
首先我们做一下实验(前提是必须要打开binlog),SQL初始化语句
create table user ( id bigint not null auto_increment primary key, name varchar(64) null, status tinyint(1) null ); insert into user (name,status)values ('张三',1); set autocommit = 0;
我们先确定一下MySQL执行更新SQL语句之后,执行commit命令前后,binlog会有什么变化。
然后我们先使用 show master status 看一下binlog的位置
然后我们执行一下更新语句
update user set status = status + 1;
再次使用 show master status 查看发现并没有Position并没有变,执行commit命令之后,发现Position由141169变为了141506,说明从库只有在主库提交之后才能读到主库写入的binlog日志。
我们再确定一下MySQL执行更新SQL语句之后,执行rollback命令前后,binlog会有什么变化。
然后我们先使用 show master status 看一下binlog的位置
然后我们执行一下更新语句
update user set status = status + 1;
再次使用 show master status 查看发现并没有Position并没有变,执行rollback命令之后,发现Position仍然没有变化,说明事务回滚之后binlog并不会写入磁盘。
难道binlog是在事务提交之后才写入磁盘的嘛?那redo log 又是什么时候写入磁盘的呢?有上面的问题又引发一系列的问题,带着这些问题,我们来进行mysql日志的深入学习。
首先我们要先了解一下当我们做一条数据的更新操作的时候,数据库的底层到底是如何执行的?
MySQL更新数据执行流程:
1:判断数据页是否在内存中,若为否,则从磁盘读取数据到内存中,返回数据行 2:若是数据页在内存中,则直接返回数据行 3:执行数据更新操作 4:数据写入内存,同时redolog写入到内存 5:执行commit操作(此commit是SQL命令操作,而不是数据的commit状态) 6:执行commit命令之后,则进行两段提交操作。 6.1:写入内存中的redolog到磁盘中,此时redolog处于prepare状态 6.2:写入binlog到磁盘 6.3:提交事务,此时事务处于commit状态 7:结束。
注:以上操作为参数innodb_flush_log_at_trx_commit 为1和sync_binlog为1的时候。
对应MySQL的更新语句执行流程图,如图1-1。
redo log:被称之为重做日志,是在数据库发生意外时,进行数据恢复,redo log会备份是事务执行过程中的修改数据。
binlog: 被称为归档日志,是一个二进制格式的文件,用于记录用户对数据库更新的SQL语句信息,格式分为(statement、row、mixed)
redo log 和binlog的差异如下表:
redo log | binlog |
---|---|
InnoDB引擎 | MySQL Serve |
物理日志 | 逻辑日志 |
循环写入 | 追加写入 |
MySQL执行commit命令之后是使用两段提交的办法来保证事物的原子行的,至于为什么使用两段提交,而不是其他的提交方式,由于篇幅有限,不做多余解释,请参考自行查询资料。
到目前为止,关于上面binlog的问题也就迎刃而解了,
对于问题一:MySQL中binlog在事务提交之前会写入redo log和binlog到内存中,在执行commit命令之后进行两段提交操作,将redo log和binlog写入磁盘,因此在事务提交之前不能读取到binlog日志(前提binlog没有进行被动刷盘)。
对于问题二:binlog是在执行commit命令之后进行的刷盘,但是是在事务在commit状态之前写入的磁盘。根据上面的实验可以看出,事务回滚对于binlog并没有什么影响。
小结:在执行commit命令前,执行更新数据到内存之后,那么就会写入redo log和binlog到 redo log buffer和binlog buffer中,当执行了commit命令之后,就是进行两段提交操作,然后进行redo log和binlog写入磁盘操作。
关于MySQL的日志刷盘机制是由参数innodb_flush_log_at_trx_commit 和sync_binlog控制的,具体请参考下一篇文章。
我们上面说的commit命令是指MySQL语法中的commit命令,用于提交事务,一般跟 begin/start transaction 配对使用。
而我们图中用到的这个“commit 步骤”,指的是事务提交过程中的一个小步骤,也是最后一步。当这个步骤执行完成后,这个事务就提交完成了。
“commit 命令”执行的时候,会包含“commit 步骤”。