最近在做数据库同步,主要业务场景是同步目前中间件分库分表的库表至单库单表中做OLAP。前期的同步是基于binlog filename和pos的,前几天DBA告知了一个可能的风险,仔细想想也的确是的,假如主节点挂了,从节点提升为主,那么其实主从的binlog pos是不一致的,那么继续注册上从同步肯定会有问题,再加之目前db使用的是vip,切换可能无感知的就到了从节点,那么还是会有隐患的。
我们的同步服务是伪装成从节点,不断的将主节点发送过来的binlog数据解析并且转换为mysql语句后进行的同步。
所以细想之下,决定在binlog pos的功能上,再支持基于gtid的同步。奈何这方面的资料实在太少,查了几天资料,啃了mysql和mariadb的主从复制代码,总算是大致把这个问题解决了。
首先我来大概介绍一下mysql和mariadb的基于gtid的同步。
在mysql中,每一个server都有一个全局唯一的server_uuid,这个是用来区分源的。
mysql的gtid格式是一个gtidSet,由于mysql支持多源复制,所以存在set这个概念,每个set内的每个元素都是一个源的gtid信息。
而作为元素的gtid,则保存着对应源的同步信息,大致为 server_uuid:START-END
格式,表示执行过的gtid信息,一个源可能有多个gtid。
当然我们的同步只是单源同步,而且为了简化,我们的gtid格式一般为server_uuid:1-END
,也就是我们只需要记录最后一条执行的gtid的值就行了。
假设master节点已开启了gtid_mode,那么作为从,大概有以下几个步骤:
1.和普通模式一样,发送注册从节点的数据包 COM_REGISTER_SLAVE
2.发送 COM_BINLOG_DUMP 包,用来指定同步的位置信息,在这儿我们必须指定 COM_BINLOG_DUMP_GTID 命令才可以开启gtid同步,并且binlog filename和pos我们需要置为空和4。
这个包的二进制结构如下:
1 byte command-id COM\_BINLOG\_DUMP\_GTID
2 bytes flag
4 bytes server_id server_uuid
mysql-string binlog-filename
8 bytes binlog-pos
encoded gtidSet
当我们需要从头开始复制的时候,我们只需要将空的gtidSet进行编码就可以了。
当我们使用 COM_BINLOG_DUMP_GTID 开启同步后,我们就可以收到mysql传过来的GTIDEvent了。以下为GTIDEvent的二进制结构:
1 byte commitFlag
16 bytes SID
8 bytes GNO
SID是一个经过编码过的server_uuid,GNO则是当前的事务编号,通过这些我们就可以格式化出我们需要的gtid字符串。
解析过GTIDEvent之后,就按着正常的流程走了,当该GTIDEvent后续的事件执行完毕后,继续下当前的gtid,用于下次继续同步。
mariadb的格式和mysql有所不同,大致的概念还是差不多的,还是存在着server_uuid和sequence的概念。下面为单个gtid的字符串值 domainID-serverID-sequence
同时mariadb同步过来的gtid event也和mariadb不一致,是mariadb扩展的,下面为mariadb的GTIDEvent的二进制格式:
8 bytes sequence
4 bytes domainID
可能大家觉得和上面的格式相比,缺少了serverID,实际上serverID在binlog event的header里面,所以信息是相符的。
和mysql的区别比较大,mariadb的同步主要有以下的步骤:
SET @mariadb_slave_capability=4
2.注册当前的同步gtid
SET @slave_connect_state='xxx'
3.发送 COM_BINLOG_DUMP 数据包,binlog filename pos置空。
当前跑的服务都是基于binlog pos的,所以首先要解决的问题就是将其自动切换为gtid模式。由于mariadb的后期版本的同步已经和mysql不一样了,所以需要分别解决。
mysql的解决方式还暂时没找到,所以先放着,大家有什么解决方案也欢迎留言。线上跑的大部分服务都是mariadb,所以不支持也暂时可以。
mariadb的解决方法非常简单,mariadb内置binlog_gtid_pos
这个函数,所以可以通过使用该函数来查询binlog pos对应的gtid。
SELECT binlog_gtid_pos('mysql-bin.000001', 1234)
得到了对应的gtid后,就可以使用该gtid进行同步了,无需人工干预。