mysql group replication观点及实践
一:个人看法
Mysql Group Replication 随着5.7发布3年了。作为技术爱好者。mgr 是继 oracle database rac 之后。
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。又一个“真正” 的群集,怎么做到“真正” ? 怎么做到解决复制的延迟,怎么做到强数据一致性?基于全局的GTID就能解决? 围绕这些问题进行了一些mgr 的实践,
为未来的数据库高可用设计多条选择。
mysql5.7手册17章可以看到其原理,网络上也很多同志写了关于其技术原理,这里自己对比rac理解下:
作为shared nothing (mgr)架构,其数据一致性实现较 shared everything(RAC) 架构要难,
MGR通过一致性(Paxos)协议,保证数据在复制组内的存活节点里是一致的,复制组内的各成员都可以进行读写,
其实现机制是,当某个实例发起事务提交时,会向组内发出广播,由组内成员决议事务是否可以正常提交,
MGR在遇到事务冲突时(多节点同时修改同一行数据),会自动识别冲突,并根据提交时间让先提交的事务成功执行,后提交的事务回滚,其原理示意图如下:
对于 sharad nothing 架构,必须要了解分布式协议PAXOS,分布式状态机 理论,而在这块我翻阅了很多资料,发现其实并不是很成熟的。从上图可以看出来MGR 的冲突检测机制
类似于 rac 的gird 群集组件 也具备通告广播的群集服务。但本质架构上有所不同。一切依赖于 复制组的软件实现。如果这里出了问题,那么整个群集一致性难以得到保证。
二:搭建过程
这个过程比较粗糙,网络上有不少写的比较细的可以参考
1:MGR 必须3节点以上,这个道理不在解释,先配置my.cnf 我这里用一台机器模拟3个mysql 节点进行搭建,着重理解红色参数,对于性能优化和理解mgr 有很大帮助 port=3306 basedir=/home/mysql datadir=/home/mysql/data3 socket = /home/mysql/data3/mysql3.sock log_error=/home/mysql/data3/mysql3.error slow_query_log=on slow_query_log_file=/home/mysql/data3/slow3.log long_query_time=3 character_set_server=utf8 lower_case_table_names=1 max_connections=1000 max_connect_errors=1000 explicit_defaults_for_timestamp=1 explicit_defaults_for_timestamp log-slow-admin-statements=1 log-queries-not-using-indexes=1 expire-logs-days = 15 open_files_limit=65535 innodb_log_file_size = 100m innodb_log_files_in_group = 3 innodb_file_per_table = 1 innodb_buffer_pool_size=10240M #mgr server-id=333 gtid_mode = on enforce_gtid_consistency = 1 log_slave_updates = 1 master_info_repository=table relay_log_info_repository=table binlog_checksum=none log-bin=/home/mysql/data3/mysql-bin binlog_format=row ###指示服务器对于每个事务,它必须收集写集并使用XXHASH64散列算法将其编码为散列。 transaction_write_set_extraction=XXHASH64 ###创建的组名称 loose-group_replication_group_name="335e89d8-2f49-4425-9add-1eeb8134f8fc" ###指示插件在服务器启动时不自动启动操作,add my 美团点评,是开启的 ###配置成员后,您可以设置 group_replication_start_on_boot 为on,以便在服务器引导时自动启动Group Replication。 loose-group_replication_start_on_boot=on #loose-group_replication_member_weight = 40 loose-group_replication_local_address="172.31.50.160:20003" #设置组成员的主机名和端口,新成员使用它们建立与组的连接。这些成员称为种子成员。建立连接后,将列出组成员身份信息 #可以查询 performance_schema.replication_group_members loose-group_replication_group_seeds="172.31.50.160:20001,172.31.50.160:20002,172.31.50.160:20003,172.31.50.160:20004" # singe or multi mode 这里2个参数不开启则为单主模式 #loose-group_replication_single_primary_mode = off #loose-group_replication_enforce_update_everywhere_checks = on #当成员在发生故障转移时被选为主要成员的可能性的权重百分比,和server_uuid 配合进行选举 #如果主要成员离开单主要组,则执行选举,并从组中的其余服务器中选择新的主要成员。 #这次选举是通过查看新视图,并根据值来订购潜在的新原选 group_replication_member_weight。 #假设该组正在运行所有运行相同MySQL版本的成员,则具有最高值的成员将 group_replication_member_weight 被选为新的主要成员。 #如果多个服务器具有相同 group_replication_member_weight的服务器, #则根据服务器的优先级对服务器进行优先级排序 server_uuid按字典顺序和选择第一个。 #选择新主节点后,它将自动设置为读写,其他副节点仍为辅助节点,因此为只读节点。 group_replication_member_weight=80 #少数组成员由于网络中断且无法连接到多数成员的成员在离开组之前等待多长时间参数 #少数组将永远等待网络重新连接。在使用停止组复制之前,将阻止少数组处理的任何事务 loose-group_replication_unreachable_majority_timeout=5 #压缩发生在组通信引擎级别,之前数据被交给组通信线程,所以它发生在mysql用户会话线程的上下文中。事务有效网络负载可以在被发送到组之前被压缩, #并且在被接收时被解压缩。压缩是有条件的,并且取决于配置的阈值。默认情况下启用压缩,此外,它并不要求组中的所有服务器节点都启用压缩机制, #在接收到消息时,成员检查消息信封以验证它是否被压缩,如果需要,则成员解压缩该事务,然后将其传递到上层 #默认情况下启用压缩,阈值为1000000字节(1MB)。压缩阈值以字节为单位。 #美团这里设置为128k。 group_replication_compression_theeshold=131072 #配置组接受的最大事务大小(以字节为单位)。 #回滚大于此大小的事务。使用此选项可避免导致组失败的大型事务。 #大型事务可能导致组的问题,无论是在内存分配还是网络带宽消耗方面, #这可能导致故障检测器触发,因为给定成员在忙于处理大事务时无法访问。 #设置为0时,组接受的事务大小没有限制,并且可能存在导致组失败的大型事务的风险。根据组中所需的工作负载大小调整此变量的值。 group_replication_transaction_size_limit=20971520 2:主节点建立复制账户,安装组复制插件,开启组复制,这里插入测试数据,为了体现mgr的一些限制和特性 比如不支持MYISAM引擎。绿色字体插入数据报错。
create user repl@'%' identified by 'repl123'; grant replication slave on *.* to repl@'%'; change master to master_user='repl', master_password='repl123'
for channel 'group_replication_recovery'; select * from mysql.slave_relay_log_info\G install plugin group_replication soname 'group_replication.so'; set @@global.group_replication_bootstrap_group=on; start group_replication; set @@global.group_replication_bootstrap_group=off; select * from performance_schema.replication_group_members; create database test; use test; create table t1(id int); create table t2(id int)engine=myisam; create table t3(id int primary key)engine=myisam; --->不支持 create table t4(id int primary key);
3:其它节点加入组复制
change master to master_user='repl', master_password='repl123'
for channel 'group_replication_recovery'; install plugin group_replication soname 'group_replication.so'; set global group_replication_allow_local_disjoint_gtids_join=1; start group_replication;
注意:
一个节点加入组复制会有本地的事物产生,比如更改密码,加入测试数据等。上述红字可以可以规避上述事务,强制加入,也可以在本地事务开始之前进行
sql_log_bin=0; start transaction.......... sql_log_bin=1;
4:维护
任意一节点启动,停止组复制。
start group_replication;
stop group_replication;
查看所有在线的组复制节点
select * from performance_schema.replication_group_members;
查看谁是主节点,单主模式下,能查出值,多主模式下无法查出。
select b.member_host the_master,a.variable_value master_uuid from performance_schema.global_status a join performance_schema.replication_group_members b on a.variable_value = b.member_id where variable_name='group_replication_primary_member';
+------------+--------------------------------------+
| the_master | master_uuid |
+------------+--------------------------------------+
| calldb3 | cc4958ae-a1cc-11e8-9334-00155d322c00 |
+------------+--------------------------------------+
三:vip的实施
mgr 并不提供vip 的概念。在单主模式下,必须启用vip实现才能做到 HA 的作用,这里的策略网络上也很多,我们基于keepalived 进行思考
要实现上述策略,考虑故障节点的情况
1:故障节点 mysql 实例崩溃,守护进程也不能拉起改实例,需要VIP 漂移。
2:故障节点系统崩溃,段时间无法进行恢复。需要VIP 漂移。
3:判定谁是主节点,vip就漂移到主节点
基于以上3点。keepalived 除了系统故障vip漂移的功能,还需要mysql 实例崩溃的判定主节点漂移功能
具体实现:
[root@calldb1 ~]# cat /etc/keepalived/keepalived.conf vrrp_script chk_mysql_port { script "/etc/keepalived/chk_mysql.sh" ---判断实例存活 interval 2 weight -5 fall 2 rise 1 } vrrp_script chk_mysql_master { script "/etc/keepalived/chk_mysql2.sh" ---判断主节点 interval 2 weight 10 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 88 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.0.100 } track_script { chk_mysql_port chk_mysql_master } }
判定脚本如下:
[root@calldb1 ~]# cat /etc/keepalived/chk_mysql.sh #!/bin/bash netstat -tunlp |grep mysql a=`echo $?` if [ $a -eq 1 ] ;then service keepalived stop fi
[root@calldb1 ~]# cat /etc/keepalived/chk_mysql2.sh #!/bin/bash host=`/data/mysql/bin/mysql -h127.0.0.1 -uroot -pxxx -e "SELECT * FROM performance_schema.replication_group_members WHERE MEMBER_ID = (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME= 'group_replication_primary_member')" |awk 'NR==2{print}'|awk -F" " '{print $3}'` host2=`hostname` if [ $host == $host2 ] ;then exit 0 else exit 1 fi
