MySQL5.6开始主从复制有两种方式:基于日志(binlog)、基于GTID(全局事务标示符)。
根据《High-Level Binary Log Structure and Contents》所述,不同版本的 Binlog 格式不一定一样,所以也没有一个定性。在我写这篇文章的时候,目前有三种版本的格式。
· v1,用于 MySQL 3.2.3
· v3,用于 MySQL 4.0.2 以及4.1.0
· v4,用于 MySQL 5.0 以及更高版本
实际上还有一个 v2 版本,不过只在早期 4.0.x 的 MySQL 版本中使用过,但是 v2 已经过于陈旧并且不再被 MySQL 官方支持了。
通常我们现在用的 MySQL 都是在 5.0 以上的了,所以就略过 v1 ~ v3 版本的 Binlog,如果需要了解 v1 ~ v3 版本的 Binlog 可以自行前往上述的《High-level…》文章查看。
binlog格式分为statement,row以及mixed三种,mysql5.5默认的还是statement模式,当然我们在主从同步中一般是不建议用statement模式的,因为会有些语句不支持,比如语句中包含UUID函数,以及LOAD DATA IN FILE语句等,一般推荐的是mixed格式。暂且不管这三种格式的区别,看看binlog的存储格式是什么样的。binlog是一个二进制文件集合,当然除了我们看到的mysql-bin.xxxxxx这些binlog文件外,还有个binlog索引文件mysql-bin.index。如官方文档中所写,binlog格式如下:
/var/log/mysql/mysql-bin.000019
/var/log/mysql/mysql-bin.000020
/var/log/mysql/mysql-bin.000021
对照官方文档中的说明来看下format_descevent格式:
+=====================================+
| event | timestamp 0 : 4 |
| header +----------------------------+
| | type_code 4 : 1 | = FORMAT_DESCRIPTION_EVENT = 15
| +----------------------------+
| | server_id 5 : 4 |
| +----------------------------+
| | event_length 9 : 4 | >= 91
| +----------------------------+
| | next_position 13 : 4 |
| +----------------------------+
| | flags 17 : 2 |
+=====================================+
| event | binlog_version 19 : 2 | = 4
| data +----------------------------+
| | server_version 21 : 50 |
| +----------------------------+
| | create_timestamp 71 :4 |
| +----------------------------+
| | header_length 75 : 1 |
| +----------------------------+
| | post-header 76 : n | = array of n bytes, one byte per event
| | lengths for all | type that the server knows about
| | event types |
+=====================================+
前面4个字节是固定的magic number,值为0x6e6962fe。接着是一个format_desc event,先看下19个字节的header。这19个字节中前4个字节0x567fb2b8是时间戳,第5个字节0x0f是event type,接着4个字节0x00000004是server_id,再接着4个字节0x00000067是长度103,然后的4个字节0x0000006b是下一个event的起始位置107,接着的2个字节的0x0001是flag(1为LOG_EVENT_BINLOG_IN_USE_F,标识binlog还没有关闭,binlog关闭后,flag会被设置为0),这样4+1+4+4+4+2=19个字节的公共头就完了(extra_headers暂时没有用到)。然后是这个event的data部分,event的data分为Fixeddata和Variable data两部分,其中Fixeddata是event的固定长度和格式的数据,Variabledata则是长度变化的数据,比如format_desc event的Fixed data长度是0x54=84个字节。下面看下这84=2+50+4+1+27个字节的分配:开始的2个字节0x0004为binlog的版本号4,接着的50个字节为mysql-server版本,如我的版本是5.5.46-0ubuntu0.14.04.2-log,与SELECTversion();查看的结果一致。接下来4个字节是binlog创建时间,这里是0;然后的1个字节0x13是指之后所有event的公共头长度,这里都是19;接着的27个字节中每个字节为mysql已知的event(共27个)的Fixed data的长度;可以发现format_desc event自身的Variable data部分为空。
上面提到,binlog有三种格式,各有优缺点:
Mysql binlog日志有ROW,Statement,MiXED三种格式;可通过my.cnf配置文件及 ==set globalbinlog_format='ROW/STATEMENT/MIXED'== 进行修改,命令行 ==showvariables like 'binlog_format'== 命令查看binglog格式;。
GTID(Global Transaction ID)是对于一个已提交事务的编号,并且是一个全局唯一的编号。GTID实际上是由UUID+TID组成的。其中UUID是一个MySQL实例的唯一标识,保存在mysql数据目录下的auto.cnf文件里。TID代表了该实例上已经提交的事务数量,并且随着事务提交单调递增。下面是一个GTID的具体形式:3E11FA47-71CA-11E1-9E33-C80AA9429562:23。
以下简单描述下MySQL Replication的复制过程
1. slave服务器上执行start slave
命令,开启主从复制开关;
2. slave服务器的IO线程会通过 在Master上授权的复制用户权限请求连接Master服务器,并请求从指定binlog日志文件位置(日志文件名和位置在配置主从复制服务时执行changemaster 命令时指定)之后发送binlog日志内容;
3. master服务器接受来自slave服务器的IO线程的请求后,master服务器上负责复制的IO线程根据slave服务器的IO线程请求的信息读取指定binlog日志文件指定位置之后的binlog日志信息,然后返回给slave的IO线程,返回的信息中除了binlog日志内容外,还有本次返回日志内容后再master服务器端的新的binlog文件名称以及在binlog中的下一个指定更新位置;
4. 当slave服务器的IO线程获取到来自master服务器上IO线程发送日志内容以及日志文件以及位置点后,将binlog日志内容一次写入到slave端自身的relaylog(中继日志)文件(mysql-relay-bin.xxxxxx)的末尾,并将新的binlog文件名和位置记录到master-info文件中,以便下次读取master端新binlog日志时能够告诉master服务器需要从新binlog日志的哪个文件哪个位置开始请求新的binlog日志内容;
5. slave服务器的SQL线程会实时的检测本地relaylog中新增加的日志内容,然后及时的把log文件中的内容解析成在master端曾经执行的SQL语句的内容,并在自身的slave服务器上按语句的顺序执行应用这些SQL语句,应用完毕后清理用过的日志;
6. 经过上面的过程,就可以确保在master端和slave端执行了同样的SQL语句。当复制状态正常的情况下,master端和slave端的数据是完全一样的,MySQL的同步机制是有一些特殊的情况,具体请参考官方的说明,大多数情况下,我们不用担心。
a.Master将数据改变记录到二进制日志(binary log)中
b.Slave上面的IO进程连接上Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容
c.Master接收到来自Slave的IO进程的请求后,负责复制的IO进程会根据请求信息读取日志指定位置之后的日志信息,返回给Slave的IO进程。 返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到Master端的bin-log文件的名称以及bin-log的位置
d.Slave的IO进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最末端,并将读取到的Master端的 bin-log的 文件名和位置记录到master-info文件中,以便在下一次读取的时候能够清楚的告诉Master从某个bin-log的哪个位置开始往后的日志内容
e.Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay-log的内容成为在Master端真实执行时候的那些可执行的内容,并在自身执行
binlog索引文件是一个文本文件 binlog.index,其中内容为当前的binlog文件列表. 索引文件的完整性保证了数据同步的完整性。
单向网络中,我们可以确保文本的传输的确切性:文本要么全部传过来,最好的结果,也是预期的结果;要么缺失一部分,文本内容不会出现错乱的现象,缺失的文本处理时做异常处理,等待下一次完整文本传输过来,可以成功解析。Binlog.index文件就是数据同步的基准(LIST PLAN),清单计划上面的内容完成,就认为整个同步任务完成。网络的可靠性,内外网数据的一致性只能人工的排查,或者在网络可靠的情况下,外网收集数据清单,对内网进行比对,生成对比记录,辅助数据的一致性。
程序构成:
改动文件扫描程序:检测binlog文件的生成,筛选改动文件,增量发送到内网,利于程序的持久性传输。
内网Binlog文件校验解析程序:根据binlog.index索引文件校验文件数量的完整性,内网程序持久化记录上次处理binlog文件位置,包含binlog-file-name和binlog 的stop—position,xy两维数据定位binlog的位置。
解析binlog的程序采用 shyiko的mysql-binlog-connector-java;(项目地址: https://github.com/shyiko/mysql-binlog-connector-java )准确的解析binlog的日志格式。
Binlog文件恢复到指定数据库,采用mysqldbinlog命令行指定文件进行恢复,shell命令回传执行结果,打印执行日志,判断binlog文件恢复的情况,持久化本次的binlog恢复记录。