背景
为什么会有双主这种架构?
大体上双主架构方案有两种玩法:
-
双主单写
-
双主双写
1. 双主单写(相对推荐)
实际上,”keepalived + 双主”和”keepalived + 主从”的架构差异并不明显。如果你能确保在”双主”架构中只允许单点写入,这种架构和”keepalived + 主从”一样,是完全没有问题的。然而,”双主”架构的特性,也就是从库同时也是主库这一件事,可能带来一些问题。比如:
-
主从角色判断问题。我的备份脚本通过 show slave status 命令来判断哪个是从库,只在从库上进行备份。现在的情况是,两个节点的 show slave status 都有输出,导致两个节点都被备份。这就需要我重构备份脚本,加入判断哪个节点持有 VIP 的逻辑来辅助判断,只备份非 VIP 节点。这只是个注意事项,不算大问题。 -
备库有误操作写入脏数据风险。在进行运维操作时,如果登录到备库,需要特别小心。虽然可以设置 super_read_only 来防止误操作写入数据库,但如果 super_read_only 出于某种原因被关闭了,就可能误写入数据,并且这些数据还会复制到当前的主库。这种问题在主从架构中不会发生。你可能认为这种误操作不太可能发生,但我的经验告诉我,只要存在可能,就必然发生。因此,我并不推荐使用主主复制,主从复制已经足够满足需求。 -
脑裂发生后数据处理雪上加霜。在”keepalived + 主从”架构或其他主从架构如 MHA 中,我们通常会在故障转移后调用脚本,断开主从复制链接,万一发生脑裂,两个数据库会成为独立的分区,不会相互同步数据。而在”keepalived + 双主”架构中,我们通常不会处理双向复制链路。如果在脑裂后网络恢复,两个数据库的数据会互相同步,导致数据混乱和丢失。脑裂后的数据恢复本身就已经十分困难,现在情况变得更糟糕。
2.双主双写
双主双写的架构方案就完全不能用吗,如何解决呢?
解决办法 1: 隔开自增键,按行写入(不推荐)
有人认为两个主库设置不一样的自增键可以解决:
auto_increment_offset = 1 # 主库为1,从库为2auto_increment_increment = 3 # 自增步长
解决办法 2: 按表或按库写入(相对推荐)
双活架构,由于复杂性,可能有一些方案会考虑跨机房之间的异步复制采用双向同步复制方案,只要规避好坑,严格地单元化拆分业务,我觉得可以接受。不在这篇文章讨论范围,我们只讨论本地高可用。
二、双主复制有可能导致复制回环
1.部署回环架构
实验步骤(不感兴趣的,可以快速跳过,直接看结论):
[root@192- 168- 199- 175 playbooks] # pwd
/usr/ local/dbops/mysql_ansible/playbooks
# vi ../inventory/hosts.ini
[dbops_mysql]
192.168. 199.171 ansible_user=root ansible_ssh_pass= "'gta@2015'"
192.168. 199.172 ansible_user=root ansible_ssh_pass= "'gta@2015'"
192.168. 199.173 ansible_user=root ansible_ssh_pass= "'gta@2015'"
[root@192- 168- 199- 175 playbooks] # vi vars/var_master_slave.yml
# vars loading order: common_config -> this file
master_ip: 192.168. 199.171
slave_ips:
- 192.168. 199.172
- 192.168. 199.173
sub_nets: 1%
[root@192- 168- 199- 175 playbooks] # ansible-playbook master_slave.yml
# playbook会做一些校验通过后,要求您确认信息是否正确,然后你手工输入"confirm"后正式运行
使用 dbops 快速部署好生产级别的一主两从。
首先,我需要在 dbops server 上查看复制账号的账号名和密码
# 在dbops server上查看复制账号的账号名和密码[root@192-168-199-175 playbooks]# cat common_config.yml |grep rplemysql_rple_user: replmysql_rple_password: Repl@8888
[root@192-168-199-172 ~]# su mysql[root@192-168-199-172 ~]# db3306 # dbops的默认设定,提供的管理员免密快捷登录数据库的方式mysql> CHANGE MASTER TO MASTER_HOST='192.168.199.173', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='Repl@8888', MASTER_AUTO_POSITION=1 for channel 'master173';mysql> start slave;
# su mysql# db3306 # dbops的默认设定,提供的管理员免密快捷登录数据库的方式mysql> stop slave;mysql> CHANGE MASTER TO MASTER_HOST='192.168.199.172', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='Repl@8888', MASTER_AUTO_POSITION=1;mysql> start slave;
2.验证回环
mysql> stop slave sql_thread for channel 'master173';
登录 171,建个 fander 库。
[root@192-168-199-171 ~]# su mysql[root@192-168-199-171 ~]# db3306 # dbops的默认设定,提供的管理员免密快捷登录数据库的方式mysql> create database fander;
登录 172 和 173,都能看到 fander 库同步过来的。
mysql> show databases;+--------------------+| Database |+--------------------+| fander || information_schema || mysql || performance_schema || sys |+--------------------+5 rows in set (0.00 sec)
# 172[mysql@192-168-199-172 3306]$ pwd/database/mysql/log/relaylog/3306[mysql@192-168-199-172 3306]$ lltotal 24-rw-r----- 1 mysql mysql 204 Jun 19 23:20 relay-bin.000001-rw-r----- 1 mysql mysql 1090 Jun 19 23:53 relay-bin.000002-rw-r----- 1 mysql mysql 102 Jun 19 23:20 relay-bin.index-rw-r----- 1 mysql mysql 214 Jun 19 23:47 relay-bin-master173.000001-rw-r----- 1 mysql mysql 618 Jun 19 23:53 relay-bin-master173.000002-rw-r----- 1 mysql mysql 122 Jun 19 23:47 relay-bin-master173.index[mysql@192-168-199-172 3306]$ mysqlbinlog -vvv relay-bin-master173.000002 |grep fander#git编号: 'b0ae588e-0eb4-11ee-b23f-000c297b0a30:3'create database fander
请问这个 GTID ‘b0ae588e-0eb4-11ee-b23f-000c297b0a30:3’ 会重复应用,导致复制报错吗?
答案是不会。因为 GTID 的原理是, 他不会执行他标记已经执行过的 GTID,’b0ae588e-0eb4-11ee-b23f-000c297b0a30:3’ 在我启动 sql_thread for channel ‘master173’ 之前就已经标记执行过了,所以这个回环过来的 SQL 是不会执行的。
但如果我不是 GTID 模式的复制而是传统复制呢?那就会执行了!由于 database fander 已经存在,所以重复执行的结果就是复制报错,或者数据不一致了。
虽然在 GTID 模式下,不会重复执行 SQL,但存在一个问题,那就是回环问题。这导致了一些本不应该传输的 binlog 重复传输,使得网络流量翻倍。这是为什么呢?
3.出现回环的原因