MySQL如何保持主备一致呢?
MySQL主备的基本原理
假如说,节点B为节点A的备库,客户端的读写都直接访问A,B只是将A的更新全部同步过去,以保持B与A的一致性。
B库虽然没有被访问,但是还是要将B库设置为readonly模式,这样做的好处有,一些运营类的查询语句放到备库上去看,减少主库负担,也可以防止在备库中误操作。可以通过readonly来看谁是主库,谁是备库。防止切换bug,切换过程当中出现双写导致主备不一致。
那么我们都将备库设置为readonly了,还怎么对他进行更新保持主备一致呢?readonly对于super权限用户是无效的,用于同步主备数据的线程机会拥有超级权限的。主库和备库直接就有一个线程来用于同步数据。
他是这样运行的,备库B通过change master命令得到从什么地方开始请求binlog,在B上执行start slave命令,备库启动两个线程 io_thread和 sql_thread。
io_thread负责与主库连接,主库通过io_thread这个连接,确定用户名,密码是否正确,然后与读取binlog发送给B库。
B在拿到binlog进行解析完成数据同步。
binlog的三种形式
1.statement 2. row 3. mixed
使用binlog_format参数来设置binlog的形式
当binlog_format=statement 的时候,binlog里面记录的就是SQL语句原文,但是我们要注意在使用这个形式来记录delete语句的时候是不安全的。
delete from t where a>=4 and t_modified<='2018-11-10' limit 1;
比如这个语句,你主库的时候可能使用a索引来查,在备库的时候可能会使用t_modified索引来找,这取决与优化器的决定,而且是范围查找并且只找一个,所以很可能找到的并不是一个值,造成主备不一致,所以这样做是有风险的。
与statement 的binlog相比,row形式下的binlog就很安全了,row形式下的binlog不是SQL的原文,他替换为了Table_map和 Delete_rows,Table_map用于表示接下来操作的是哪个表,Delete_rows用于表示接下来删除哪个行,再将binlog_format设置为row的时候,里面真实记录了删除行的主键id,传到备库的时候并不会删错行。
但是row形式下的binlog也有缺点,就是很费空间,在statement下的语句要删除1000行,只需要一句话,几个字节而已,在row当中,要将每一行的主键都存储下来,是很费空间的,所以第三种形式mixed出现了,顾名思义他就是将row形式和statement形式混合起来。他会首先判断这条SQL语句是否会造成主备不一致,要是有可能造成就为row形式,不可能造成就是statement形式。
循环复制问题
我们之前所说的一主一备的形式为M-S形式,在实际操作当中,M-M形式更为常见。即为A库与B库互为主备库,在MM下,A更新的binlog给到B,B更新后又会产生binlog给到A,A又进行更新……
这样就变成了循环复制,为了解决这个问题,执行流程被优化为:
1. 节点A更新,binlog记录了A的Server id
2.传到B,B进行更新,节点B生成的Server id就是A的Server id,然后再传给A
3. A进行判断,是自己的Server id就不更新,否则更新
这样就结束了循环复制。