【用心原创,真正的MySQL三大日志的疑问痛点】一文搞懂MySQL的undolog、redolog、binlog的联系和区别

125 阅读5分钟

我们先从不同的角度来对这三种日志做一个横向的对比:

作用场景

undolog(回滚日志)redolog(重做日志)Binlog(二进制日志)

 

undo直译为不做,不做不做,目的是回退事务,保证回退之后的数据不发生改变,即保证的时原子性。 undolog同时在mvcc机制中担任重要角色,保证了并发事务的隔离性重做日志,顾名思义再做一遍再做一遍,突出一个“持久”。它是为了解决事务提交的持久性的。看它的名字就知道,这个日志的最终日志记录格式是二进制的(不是直接可读的,需要借助工具如mysqlbinlog)。而且它是MySQL服务器自带的日志记录工具,跟存储引擎无关。所以它适用于解决数据库的通用场景问题,如数据复制和数据恢复。 

实现方式

innodb引擎实现innodb引擎实现Mysql server层实现

日志内容

那怎么才能保证原子性呢?由于事务未提交的时候并不会真实的修改数据物理页,理论上直接抛弃所做的事务操作就行了,为什么还需要记录日志呢? 因为虽然数据还没有落盘,但是已经存在了数据库的内存中,并参与到了并发事务中服务于mvcc机制,且修改的数据可能对其他事务可见,比如(读未提交)。undolog则通过版本链的方式保证了事务中数据的可见性,以及当事务失效时的回滚机制。由于数据版本的多样性,且数据未提交落盘,所以物理日志已经不合适,而是采用记录修改操作相反的逻辑日志的方式。具体如:事务中的insert语句,则对应undolog中的delete语句。 怎么才能保证隔离性呢?上文中提到的undolog是mvcc机制的基础,而该机制在不同的隔离级别下可以提供不同的隔离策略,如在可重复读的情况下,可以提供比当前读数据更早版本的数据供当前事务的再次查询访问。如何才能做到保证持久性呢?将事务对数据库的修改信息记录下来。怎么才能最严格的记录下来呢?把被修改的物理页(数据库存储的基本单位)拷贝一份存下来;但是这样做拷贝的性能太低了,兼容高效的话,则只记录该事务“在哪个数据页改了什么内容”即可。  它记录的内容是数据库的写入性操作(不包括查询)。 binlog有三种日志记录的方式,其中两种为基础型,一种为混合型。statement(SBR,5.7.7前默认) :记录的是执行的SQL语句。优点是节省空间,缺点是当使用了某些函数如NOW()时,可能会出现同步数据出现不一致的情况。 row(PBR,5.7.7后默认) :记录了修改了哪一行 ,以及每行的具体修改内容。优点是数据一致性强,缺点是会产生更大的日志文件。 mixed(MBR): 结合了两者的优势,在不出现丢失一致性的函数SQL语句时按照sbr方式记录,反之则使用pbr方式记录。优点是可以兼顾效率和体积大小。缺点是增加了复杂性 自定义:这三种方式可以通过binlog format 指定 

日志的格式和样例数据

undolog是记录的逻辑日志,存储的格式通常不是人类可读的,且记录了跟执行的事务中的SQL语句对应取反的回滚动作信息。比如执行的insert,则它就存delete。log文件通常存在innodb的系统表空间内部,而不是作为独立的文件存在。样例:insert操作:insert:table_name,row:[column1:value1],action:delete。update操作:update:table_name,row:[column1:value1],action:set[column1:origin_value1]。 delete操作:delete:table_name,row:[column1:value1]:action:insert;  redolog考虑到持久化的性能问题,是三者中唯一的物理日志格式,其内容通常不是人类可读的,文件格式也是二进制的,文件通常以‘ib_logfile0’等独立文件形式存在于MySQL的数据目录中。样例:insert:Page 1234:insert record at offset 5678,data:[column1:value1]update:page1234:modify record at offset 5678, set [column2:value2]Delete:page1234: delete record at offset 5678;binlog日志存储的也是逻辑日志,通常可以以文本格式查看,通过工具如mysqlbinlog。 statement(SBR) :Start transaction;Insert into table_name (column1) values (value1); Update table_name SET column2 = value2 WHERE column1 = value1;DELETE FROM table_name WHERE column1 = value1; COMMIT; **row(PBR):**START TRANSACTION;Insert:Table: table_name;Action:insert;Row: (column1:value1,column2:null); Update:Table: table_name;Action: update;Before: (column1:original_value1);After: (column1:value1); Delete:Table: table_name;Action:delete;Row: (column1:value1); 

记录方式

存在innodb的系统表空间内部,而不是作为独立的文件存在。采用循环写的方式记录,当写到结尾时,会回到开头循环写日志。 通过追加的方式记录,当文件大小达到给定值后,会继续记录日志到新的文件

记录时机

   

 

事务在没提交之前MySQL会先记录更新前的数据到undolog日志中。更新内存记录时,会把修改记录在redolog。事务在提交的时候在server层写入binlog。

 

完整的流程执行图如下:

image.png