binlog主要用在下边两个方面:

  • 用途一: 用于复制。
  • 用途二: 用于恢复。

    mysqlbinlog工具的使用

    查看binlog日志文件的详细信息的话,就需要使用MySQL给我们提供的实用工具——mysqlbinlog

binlog的文件格式

image.png
其中:

  • event header部分描述了该事件是什么类型、什么时候生成的、由哪个服务器生成的等信息。
  • event data部分描述了该事件所特有的一些信息,比方说在插入一条记录时,需要将这条记录的内容记录在event data中。

    基于语句(Statement)和基于行(Row)的binlog

    同一条SQL语句,随着启动选项binlog-format的不同,可能生成不同类型的binlog事件:

  • 当以启动选项—binlog-format=STATEMENT启动MySQL服务器时,生成的binlog称作基于语句的日志。此时只会将一条SQL语句将会被完整的记录到binlog中,而不管该语句影响了多少记录。

  • 当以启动选项—binlog-format=ROW启动MySQL服务器时,生成的binlog称作基于行的日志。此时会将该语句所改动的前后记录的值都记录下来了。
  • 当以启动选项—binlog-format=MIXED启动MySQL服务器时,生成的binlog称作基于行的日志。此时在通常情况下采用基于语句的日志,在某些特殊情况下会自动转为基于行的日志(这些具体情况请参考:dev.mysql.com/doc/refman/…

    基于语句的binlog的问题

    在有主从复制的场景中,使用基于语句的日志可能会造成主服务器和从服务器维护的数据不一致的情况。

redo、undo、buffer pool、binlog,谁先谁后

实际上虽然先修改Undo页面,后写redo日志,但是此时InnoDB并不认为Undo页面是脏页,就不会将其刷新到硬盘,redo日志也没有写入到redo log buffer,这些redo日志也不会被刷新到redo日志文件。只有当MTR提交时,才先将redo日志复制到redo log buffer,再将修改的Undo页面加入到flush链表。在一条更新语句执行完成后(也就是将所有待更新记录都更新完了),就需要该语句对应的binlog日志了

具体更新一条记录的流程如下:

  1. 先在B+树中定位到该记录(这个过程也被称作加锁读),如果该记录所在的页面不在buffer pool里,先将其加载到buffer pool里再读取。
  2. 读取到记录后判断记录更新前后是否一样,一样的话就跳过该记录,否则进行后续步骤。
  3. 首先更新聚簇索引记录。 更新聚簇索引记录时: ①先向Undo页面写undo日志。不过由于这是在更改页面,所以修改Undo页面前需要先记录一下相应的redo日志。 ②真正的更新记录。不过在真正更新记录前也需要记录相应的redo日志。
  4. 更新其他的二级索引记录。

至此,一条记录就更新完了。
然后开始记录该语句对应的binlog日志,此时记录的binlog并没有刷新到硬盘上的binlog日志文件,在事务提交时才会统一将该事务运行过程中的所有binlog日志刷新到硬盘。

有binlog参与的内部XA事务

当客户端执行COMMIT语句或者在自动提交的情况下,MySQL内部开启一个XA事务,分两阶段来完成XA事务的提交:

  • Prepare阶段:存储引擎将该事务执行过程中产生的redo日志刷盘,并且将本事务的状态设置为PREPARE。
  • Commit阶段:先将事务执行过程中产生的binlog刷新到硬盘,再执行存储引擎的提交工作。

对于处于PREPARE状态的事务,存储引擎既可以提交,也可以回滚,这取决于目前该事务对应的binlog是否已经写入硬盘。这时就会读取最后一个binlog日志文件,从日志文件中找一下有没有该PREPARE事务对应的xid记录,如果有的话,就将该事务提交,否则就回滚好了。