【参考文章】:
【参考文章】:
1. 数据更新时的日志处理流程
1.1 redo log(prepare状态)
此时SQL已经成功执行了,已经产生了语句的redo和undo内存日志,已经进入了事务commit步骤。然后告诉引擎做Prepare完成第一阶段,Prepare阶段就是写Prepare Log(Prepare Log也是Redo Log),将事务状态设为TRX_PREPARED,写Prepare XID(事务ID号)到Redo Log。写XID到Redo Log的时候会一并把Redo Log刷新到磁盘,这个时候Redo Log的日志量大小取决于执行SQL语句时产生的Redo是否被刷盘,这个刷盘是随机的,后台Master线程每秒钟都会刷新一次。
1.2 binlog
如果事务涉及的所有存储引擎的Prepare(即Redo Log写入磁盘之后)都执行成功,则调用TC_LOG_BINLOG::log_xid方法将SQL语句写到Binlog(write()将binary log内存日志数据写入文件系统缓存,fsync()将binary log文件系统缓存日志数据永久写入磁盘),同时也会把XID写入到Binlog。此时,事务已经铁定要提交了。否则,调用ha_rollback_trans方法回滚事务,而SQL语句实际上也不会写到binlog。
Binlog是事务commit时才刷新到磁盘,如果binlog太大则commit时会慢。
1.3 redo log(commit状态)
最后,调用引擎的Commit完成事务的提交。并且会对事务的undo log从prepare状态设置为提交状态(可清理状态),刷新Commit Log到Redo Log,释放锁,释放mvcc相关的read view等等;将事务设为TRX_NOT_STARTED状态。
1.4 两阶段提交
由上面的二阶段提交流程可以看出,通过两阶段提交方式保证了无论在任何情况下,事务要么同时存在于存储引擎和binlog中,要么两个里面都不存在,可以保证事务的binlog和redo log顺序一致性。一旦阶段2中持久化Binlog完成,就确保了事务的提交。
此外需要注意的是,每个阶段都需要进行一次fsync操作才能保证上下两层数据的一致性。
阶段1的fsync由参数innodb_flush_log_at_trx_commit=1控制,阶段2的fsync由参数sync_binlog=1控制,俗称“双1”,是保证CrashSafe的根本。
1.5 CrashSafe
CrashSafe指MySQL服务器宕机重启后,能够保证:
– 所有已经提交的事务的数据仍然存在。
– 所有没有提交的事务的数据自动回滚。
2. binlog
二进制日志是server层的,主要用来做主从复制和即时点恢复时使用的。
2.1 日志记录的三种模式
基于SQL语句的复制(statement-based replication,SBR):记录执行的SQL语句
基于行的复制(row-based replication,RBR):记录更新的每一条记录的变化情况
混合模式复制(mixed-based replication,MBR):根据具体的更新语句选择上述两种中的一种方式记录
2.2 设置 binlog 日志模式
静态设置,配置文件形式
vi my.cnf binlog_format="STATEMENT"
动态设置,命令形式
mysql> SET GLOBAL binlog_format = 'STATEMENT';
3. redo log
事务日志(redo log)是InnoDB存储引擎层的,用来保证事务安全的。
3.1 redo log 文件
redo log 是固定大小的,从头开始写,写到末尾又回到开头循环写;
redo log 有两个指针:
一个为 write pos:表示当前记录的位置,一边写一边后移;
一个为 checkpoint:表示当前要擦除的位置,一边擦除一边后移,擦除之前要将记录写到磁盘文件中;
4. 区别
binlog 属于MySQL的 sever 层,所有引擎都可以使用;redo log 属于 InnoDB引擎特有。
binlog 是逻辑日志,记录的是SQL语句的原始逻辑;redo log 是物理日志,记录的是在某个数据页上做了什么修改。
binlog 是追加写,一个文件写满之后就写到下一个文件,不会覆盖之前的文件;redo log 是循环写,写到文件末尾之后又从文件起始位置开始写,会覆盖之前的日志。
5. 故障恢复
开启Binary log的MySQL在crash recovery时:MySQL在prepare阶段会生成xid,然后会在commit阶段写入到binlog中。在进行恢复时事务要提交还是回滚,是由Binlog来决定的。
– 事务的Xid_log_event存在,就要提交。
– 事务的Xid_log_event不存在,就要回滚。
恢复的过程非常简单:
– 扫描最后一个Binlog文件(进行rotate binlog文件时,确保老的binlog文件对应的事务已经提交),提取其中的Xid_log_event
– 重做检查点以后的redo日志,读取事务的undo段信息,搜集处于prepare阶段的事务链表,将事务的xid与binlog中的xid对比,若存在,则提交,否则就回滚
总结一下,基本顶多会出现下面是几种情况:
- 当事务在prepare阶段crash,数据库recovery的时候该事务未写入Binary log并且存储引擎未提交,将该事务rollback。
- 当事务在binlog阶段crash,此时日志还没有成功写入到磁盘中,启动时会rollback此事务。
- 当事务在binlog日志已经fsync()到磁盘后crash,但是InnoDB没有来得及commit,此时MySQL数据库recovery的时候将会读出二进制日志的Xid_log_event,然后告诉InnoDB提交这些XID的事务,InnoDB提交完这些事务后会回滚其它的事务,使存储引擎和二进制日志始终保持一致。
总结起来说就是如果一个事务在prepare阶段中落盘成功,并在MySQL Server层中的binlog也写入成功,那这个事务必定commit成功。