如何进行的MySql主备一致是基于binlog日志的,为啥使用binlog,

首先说下基本的MySQL的主备切换的流程

图片

在状态1,客户端的读写都是访问的节点A,节点B是A的备库,只是将A的更新同步过来

切换成了状态2,就是B为master,客户端读写访问的都是节点B,节点A是B的备库

在主备这种设计下

建议将备库设置为read only,主要考虑为

1.对于某些内部的查询会在备库上查询,设置为只读避免出现错误

2.避免在切换过程中出现双写的情况

3.用于利用readonly状态来判断节点的角色

而且设置为了readonly,仍然可以做到同步更新,因为是超级权限用户

图片

整体的一个同步流程如上

1.在备库B上通过change master命令,设主库A的IP 端口 用户名 密码 以及binlog的起始地址和日志偏移量

2.备库上执行start slave,备库启动了两个线程,一个是io_thread 一个是sql_thread,备库通过io_thread和主库建立了连接

3,主库A检验完成了,读取本地binlog文件,然后发送给B

4,B拿到了binlog文件,写到本地文件 称为relay log

5.sql_thread读取中转日志,分析并执行

根据binlog的格式不同,传递的方式也不同

分为 statement row mixed

对于一条语句,不同的格式如何存储和传递的?

delete from t where a>=4 and t_modified <= ‘2018-11-10’ limit 1;

对于statement格式,记录的就是binlog里面记录的是SQL语句的原文,可以用

mysql>show binlog events in ‘master.000001’; 来查看binlog中的内容

图片

第一行为了主备切换而准备

第二行表示事务的开始

第三行表示真实的执行语句,前面的use ‘test’是为了保证能够直接找到test库

然后是Sql语句的原文

然后是commit,后面跟着xid是为了崩溃恢复的时候找到对应的事务

接下来是

图片

warning,出现了警告信息,主要原因是binlog设置为了statement格式的,并且带有limit,导致出现了不安全的问题

为什么会出现这个情况,是因为delete语句使用的索引a,也就是会找到第一个满足条件的行,也就是a=4

但如果使用了t_modified,那么删除的就是t_modified,那么删除的就是 t_modified=’2018-11-19’也就是a=5这一行

这就是statement格式下,因为记录的sql原语句,那么会出现索引选择不对的问题,于是可能是不安全的

对于row格式

图片

statement格式和row格式相比较

BEGIN和COMMIT一样但是这里记录的是Table_map和Delet_rows

Table_map表示的是操作的是test库的t表,

Delete_rows event定义的是删除的行为

对于row格式的binlog,我们通过mysqlbinlog该工具测试

mysqlbinlog -vv data/master/000001 –start-position=8900;(vv表示显示出所有字段的内容)

翻译过来如下:

图片

首先是server_id 表明执行的库

然后Table_map表示了打开的表

在binlog_row_image中包含了删除的所有行的字段信息

这里面是指记录了id =4 说明设置的是MINIMAL

这样能够确保在主备同步的时候,肯定删除了id=4的一行

最后是mixed格式的binlog

这就是为了避免上面可能出现的主备不一致的问题而准备的

而且row格式很占空间,如果是statement格式下删除很多数据,那么可能一条语句就OK了

如果是row格式下,删除,则可能出现了记录很多行的数据,占用更大的空间,占用IO资源

于是有了mixed格式的binlog,为了避免主备不一致

也就是说,如果删除出现了limit这种情况,那么会记录为row,不然会记录为statement格式

图片

当然设置为row格式的binlog在现在来说,被设置的更为广泛

因为在恢复数据的时候,更为方便

如果是delete语句,在row中,会将整个行的所有信息保存下来,可以快速的构建出一个insert语句

如果是insert语句,那更加不必说

如果是update语句的话 那么也可以根据修改前的数据和修改后的整行数据来进行恢复

binlog日志还可以用于日志回滚工具

在MariaDB版本中Flashback工具,就是基于的上面的原理来回滚的

这里说明一个简单的问题

如果使用了mixed格式,执行如下的语句

insert into t values(10,10,now());

是使用了statement还是row格式

图片

发现采用了statement格式来存储

那么如果MySql直接传递这个语句,会导致主备的数据不一致吗

因为now()函数仍然去当前的时间为基准啊

但是可以放心的是,在实际的记录上

图片

会记录上当前的时间戳

方便备库利用时间戳来恢复

顺便一提,就是直接执行mysqlbinlog中的statement语句,可能是有风险的,比较保险的还是用mysqlbinlog解析出来,然后将解析的行发给MySql执行

msqlbinlog master.00001 –start-position=2738 –stop-position=2973 |mysql

其次就是循环复制问题

现在很多MySQl中,并不是采用的M-S结构,而是更多的使用了双M结构,这样在两个库之间切换,不用切换M/S关系

图片

但是有一个问题,因为双方都生成了

binlog日志,那么如何保证主库不会拿从库的binlog在复制一遍呢

解决方案很简单,就是每次生成binlog的时候,记录自己的server id

一个备库接到binlog并且重放的时候,会检测这个日志的server id,如果和自己的server id相同,就直接跳过binlog

按照这个逻辑,就可以变成如下的执行流程

1.从节点A更新的事务,binlog中记录了A的server id

2.传到节点B再执行一次,节点B生成的binlog的server id就是A的server id

3.再传给A,A判断server id相同,切断循环

除非,在主库更新事务之后,修改了server_id,使用了命令set global server_id=x,导致双M双方发现都不是自己的serverid

还有就是如下的场景

图片

发表评论

邮箱地址不会被公开。 必填项已用*标注