gaodaima.com
由之前的文章可以了解到,二进制日志在复制中起到举足轻重的作用,所以这一篇文章着重了解一下Mysql复制背后核心组件:二进制日志的庐山真面目。
二进制日志的结构
从概念上讲,二进制日志是一系列二进制日志事件。它包括一系列的binlog文件和一个binlog索引文件,当前服务器正在写入的binlog文件称之为active binlog。其文件名是通过配置文件中的log-bin和log-bin-index来定义的。
每个binlog文件是由若干binlog事件组成,以Format_description事件开始,以Rotate事件作为文件尾。
Format_description事件包含写binlog文件的服务器信息,以及关于文件状态的关键信息。如果服务器关闭或者重新启动,会创建一个新的binlog文件,同时写入新的Format_description事件,这个事件是必须的,因为服务器关闭和重启都会产生更新。服务器写完binlog文件后,在文件结尾添加一个Rotate事件,该事件包含下一个binlog文件的文件名及其开始读取的位置。除了Format_description和Rotate事件之外,binlog文件的其他事件都被分成 group进行管理。在事务存储引擎中,每个组大致对应一个事务,对于非事务存储引擎,每个语句本身就是一个组。通常情况下,每个组要么全部执行,要么去不执行。如果由于某种原因Slave在组执行的过程中停机,那么将从该组的起点而不是刚刚执行的语句开始复制。
binlog事件的结构
二进制日志版本4(binlog format 4)是在MySQL 5.0中引入,是专门为扩展而设计的。这里主要讨论二进制日志版本4。(MySQL 3.23 4.0 4.1版本都是使用二进制日志版本3)
每个binlog事件由三个部分组成:
- 通用头(common header):大小固定。事件的基本信息,其中重要的字段是事件类型和事件大小。
- 提交头(post header):大小固定。提交头与特定的事件类型相关
- 事件体(Event body):大小可变。事件体存储事件的主要数据,因事件类型不同而异。
具体看一下Format_description事件:
- binlog文件格式版本
- 服务器版本字符串:一般包括三部分,即版本号、连字符和其他构建项。例如:5.1.42-debug-log
- 通用头的长度:存储了通用头的长度。这里是指Format_description事件,所以不同binlog文件该字段的值不同。除了Format_description和Rotate事件外,其他事件的通用头长度都是可变的。Format_description事件的通用头长度是不变的,是因为任何版本的服务器都需要读取这个事件。Rotate事件的通用头长度也是不变的,是因为Slave连接Master时首先要用到该事件。
- 提交头的长度:binlog文件中所有事件的提交头长度是不变的,该字段存储了各个事件的提交头长度构成的数组。由于不同服务器间的事件数目不同,所以这个字段前面还存储了服务器的事件数目。
通过事件来记录数据库变更
首先,由于二进制日志是公共资源,所有线程都向它写入语句,为了避免两个线程同时更新二进制日志,在写之前需要获得一个互斥锁Lock_log,写完之后再释放。
所有涉及到数据库更新的语句都会以Query事件的形式写入二进制日志中,除了实际执行的语句外,Query事件还包含执行语句必需的上下文附加信息。下面给出了如何记录这些上下文信息
- 当前数据库:在Query事件添加一个特殊字段记录当前数据库。
- 用户自定义变量的值:User_var事件记录单个用户自定义的变量的变量名及其值。
- RAND函数的种子:Rand事件记录Rand函数所用的随机数种子。
- 当前时间:NOW,CURDATE,CURTIME,UNIX_TIMESTAMP和SYSDATE这五个函数会用到当前时间,针对这个事件会存储一个时间戳,表示事件何时开始执行。
- AUTO_INCREMENT字段的插入值:Intvar事件记录在语句开始前,表内部的自动增量计数器的值。
- 调用LAST_INSERTED_ID的返回值本文来源gao.dai.ma.com搞@代*码(网$:Intvar事件记录这个函数在语句的返回值。
- 线程ID:主要是涉及到临时表的处理。线程ID也是作为一个独立的字段存储在Query事件中。