全同步复制(Fully synchronous replication) 指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
官网
半同步复制(Semisynchronous replication) 介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。
在2010年MySQL 5.5版本之前,一直采用的是这种异步复制的方式。主库的事务执行不会管备库的同步进度,如果备库落后,主库不幸crash,那么就会导致数据丢失。于是在MySQL在5.5中就顺其自然地引入了半同步复制,主库在应答客户端提交的事务前需要保证至少一个从库接收并写到relay log中
异步复制存在的缺陷:
1)正常的复制为:事务一(t1)写入binlog buffer;dumper线程通知slave有新的事务t1;binlog buffer进行checkpoint;slave的io线程接收到t1并写入到自己的的relay log;slave的sql线程写入到本地数据库。 这时,master和slave都能看到这条新的事务,即使master挂了,slave可以提升为新的master。
2)异常的复制为:事务一(t1)写入binlog buffer;dumper线程通知slave有新的事务t1;binlog buffer进行checkpoint;slave因为网络不稳定,一直没有收到t1;master挂掉,slave提升为新的master,t1丢失。
3)很大的问题是:主机和从机事务更新的不同步,就算是没有网络或者其他系统的异常,当业务并发上来时,slave因为要顺序执行master批量事务,导致很大的延迟。
半同步复制的原理:
为了弥补以上几种场景的不足,MySQL从5.5开始推出了半同步复制。相比异步复制,半同步复制提高了数据完整性,因为很明确知道,在一个事务提交成功之后,这个事务就至少会存在于两个地方。即在master的dumper线程通知slave后,增加了一个ack(消息确认),即是否成功收到t1的标志码,也就是dumper线程除了发送t1到slave,还承担了接收slave的ack工作。如果出现异常,没有收到ack,那么将自动降级为普通的复制,直到异常修复后又会自动变为半同步复制。
实验说明: 前提:实现基于gitd的主从复制
在master: (1)安装半同步插件
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; 查看插件状态: mysql> SELECT PLUGIN_NAME,PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%'; +---------------------+---------------+ | PLUGIN_NAME | PLUGIN_STATUS | +---------------------+---------------+ | rpl_semi_sync_slave | ACTIVE | +---------------------+---------------+ 1 row in set (0.00 sec)(2)开启半同步插件
SET GLOBAL rpl_semi_sync_master_enabled =1; 查看环境变量: mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%'; +-------------------------------------------+------------+ | Variable_name | Value | +-------------------------------------------+------------+ | rpl_semi_sync_master_enabled | ON | | rpl_semi_sync_master_timeout | 10000 |##默认等待10s (ms) | rpl_semi_sync_master_trace_level | 32 | | rpl_semi_sync_master_wait_for_slave_count | 1 | | rpl_semi_sync_master_wait_no_slave | ON | | rpl_semi_sync_master_wait_point | AFTER_SYNC | +-------------------------------------------+------------+查看状态变量:
mysql> SHOW STATUS LIKE '%rpl%'; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 0 | | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 0 | | Rpl_semi_sync_master_no_times | 0 | | Rpl_semi_sync_master_no_tx | 0 | | Rpl_semi_sync_master_status | ON | | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 0 | | Rpl_semi_sync_master_tx_wait_time | 0 | | Rpl_semi_sync_master_tx_waits | 0 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 0 | +--------------------------------------------+-------+在slave: (1)安装半同步插件
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';查看插件状态:
SELECT PLUGIN_NAME,PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%'; +----------------------+---------------+ | PLUGIN_NAME | PLUGIN_STATUS | +----------------------+---------------+ | rpl_semi_sync_master | ACTIVE | +----------------------+---------------+(2)开启半同步复制
SET GLOBAL rpl_semi_sync_slave_enabled =1;(3)重启I/O线程,如果不重启,默认还是异步复制状态
STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;查看环境变量:
mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%'; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | rpl_semi_sync_slave_enabled | ON | | rpl_semi_sync_slave_trace_level | 32 | +---------------------------------+-------+测试: 将slave宕机,在master上添加新数据,半同步复制模式,master会等待slave回应,在slave宕机的情况下,master会停顿等待几秒,slave没有回应将不再等待,将数据返回给客户
MySQL Group Replication(MGR)是MySQL 5.7.17版本引入的一个服务器插件,可用于创建高可用、可扩展、容错的复制拓扑结构。 组复制可以在单主模式下操作,其中只有一个服务器接受更新,这个单主是系统自动选举出来的。对于高级用户,也可以部署为多主模式,其中所有服务器都可以接受更新。内置的组成员服务可以在任何给定的时间点保持组的视图一致并可供所有服务器使用。当服务器加入或离开组时,视图也会相应更新。当服务器宕机,故障检测机制会检测到此情况并通知组其视图已更改。这些都是自动进行的。(多主模型 同时上线)
组由多个服务器构成,通过传递消息进行交互,通信层保证原子消息传递。MGR构建于此通信层抽象之上,并实现了多主更新复制协议。
组中的每个服务器独立地执行事务,但是所有读写事务只有在得到组的批准后才会提交。只读事务在组内不需要协调,因此立即提交。对于任何读写事务,当事务准备好在始发服务器处提交时,服务器以原子方式广播写入值(更改的行)和对应的写入集(更新的行的唯一标识符),然后将该事务加入全局事务列表。最终所有服务器都以相同的顺序接收并应用相同的事务集,所以它们在组内保持一致。
组复制可用来创建具有冗余的容错系统。即使某些服务器发生故障,只要它不是全部或大多数,系统仍然可用。根据失败的服务器数量,可能会降低性能或可伸缩性,但它仍然可用。组成员服务跟踪服务器故障,该服务依赖于分布式故障检测器,能够在任何服务器脱离组时发出信号,无论是意外停止还是主动停止。分布式恢复过程确保当服务器加入组时能自动更新。单个服务器发生故障时不会停止服务,也无需服务器故障转移。总之,MGR保证数据库服务持续可用。
需要注意,尽管数据库服务可用,但在服务器崩溃的情况下,必须将连接到它的客户端重定向或转移到其它服务器。组复制不解决数据库连接重定向的问题,连接器、负载平衡器、路由器或某种形式的中间件更适合处理此问题,例如MySQL Router。
以下是组复制的典型使用场景:
弹性复制: 服务器的数量能够动态增加或减少,并且尽可能减小副作用,例如云数据库服务。
高可用分片: 分片是实现写扩展的流行方法。使用MGR实现高可用分片,其中每个分片映射到复制组。
主从复制的替代方案: 在某些情况下,使用单个主服务器会使其成为热点,写入整个组会更具可扩展性。
组复制详解
实验说明:
注意的几点:
组复制的uuid都必须是相同的组复制的uuid应该与组成员中任意一台数据库的uuid不同,应该是从非组成员数据库中获取的合法uuid在server1(master)节点:
(1)关闭mysqld
systemctl stop mysqld(2)删除mysql数据 ##注意 :删除数据之前先复制uuid /var/lib
/mysql/auto.cnf rm -fr /var/lib/mysql/*(3)修改配置文件 ##官网 https://dev.mysql.com/doc/refman
/5.7/en/group-replication-configuring-instances.html vim /etc/my.cnf log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid server_id=1 gtid_mode=ON enforce_gtid_consistency=ON master_info_repository=TABLE relay_log_info_repository=TABLE binlog_checksum=NONE #关闭binlog校验 log_slave_updates=ON log_bin=binlog binlog_format=ROW #组复制依赖基于行的复制格式 transaction_write_set_extraction=XXHASH64 loose-group_replication_group_name="c36cc649-3f17-11e9-960e-525400cf2a01" ##可以看/var/lib/mysql/auto.cnf loose-group_replication_start_on_boot=off loose-group_replication_local_address= "172.25.136.1:24901" loose-group_replication_group_seeds= "172.25.136.1:24901,172.25.136.2:24901,172.25.136.3:24901" loose-group_replication_bootstrap_group=off ##插件是否自动引导,这个选项一般都要off掉,只需要由发起组复制的节点开启,并只启动一次,如果是on,下次再启动时,会生成一个同名的组,可能会发生脑裂 loose-group_replication_ip_whitelist="127.0.0.1,172.25.136.0/24" loose-group_replication_enforce_update_everywhere_checks=ON loose-group_replication_single_primary_mode=OFF #后两行是开启多主模式的参数(4)启动mysqld
systemctl start mysqld(5)初始化数据库
[root@server1 mysql]# grep password /var/log/mysqld.log 2019-03-14T07:50:42.947487Z 1 [Note] A temporary password is generated for root@localhost: J)hjM=V39sw> 登录数据库,先修改密码 mysql> alter user root@localhost identified by 'Wsp+123ld';(6)配置 #看官网复制 https://dev.mysql.com/doc/refman/5.7/en/group-replication-user-credentials.html
mysql> SET SQL_LOG_BIN=0; #关闭二进制日志,防止传到其他server上 mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'Wsp+123ld'; mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%'; mysql> FLUSH PRIVILEGES; mysql> SET SQL_LOG_BIN=1; ##开启日志 mysql> CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='Wsp+123ld' FOR CHANNEL 'group_replication_recovery'; mysql> INSTALL PLUGIN group_replication SONAME 'group_replication.so'; mysql> SHOW PLUGINS; ##查看插件 | group_replication | ACTIVE | GROUP REPLICATION | group_replication.so | GPL | +----------------------------+----------+--------------------+----------------------+---------+ mysql> SET GLOBAL group_replication_bootstrap_group=ON; ##组复制发起节点开启这个参数 mysql> START GROUP_REPLICATION; mysql> SET GLOBAL group_replication_bootstrap_group=OFF; mysql> SELECT * FROM performance_schema.replication_group_members; ##查看server1是否online在server2、sever3上:(server2与server3相同) (1)停止mysqld,清理之前数据
[root@server2 mysql]# systemctl stop mysqld rm -fr /var/lib/mysql/*(2)编辑配置文件
vim /etc/my.cnf server_id=2 gtid_mode=ON enforce_gtid_consistency=ON master_info_repository=TABLE relay_log_info_repository=TABLE binlog_checksum=NONE log_slave_updates=ON log_bin=binlog binlog_format=ROW transaction_write_set_extraction=XXHASH64 loose-group_replication_group_name="c36cc649-3f17-11e9-960e-525400cf2a01" ##注意:这里和server1的保持一致!!!!!! loose-group_replication_start_on_boot=off loose-group_replication_local_address= "172.25.136.2:24901" loose-group_replication_group_seeds= "172.25.136.1:24901,172.25.136.2:24901,172.25.136.3:24901" loose-group_replication_bootstrap_group=off loose-group_replication_ip_whitelist="127.0.0.1,172.25.136.0/24" loose-group_replication_enforce_update_everywhere_checks=ON loose-group_replication_single_primary_mode=OFF(3)启动mysqld
systemctl start mysqld #初始化 grep password /var/log/mysqld.log(4)进入数据库,配置
mysql> alter user root@localhost identified by 'Wsp+123ld'; #修改root用户密码 mysql> SET SQL_LOG_BIN=0; mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'Wsp+123ld'; mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%'; mysql> FLUSH PRIVILEGES; mysql> SET SQL_LOG_BIN=1; mysql> CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='Wsp+123ld' FOR CHANNEL 'group_replication_recovery'; mysql> INSTALL PLUGIN group_replication SONAME 'group_replication.so'; #这里不需要做server1上做的SET GLOBAL group_replication_bootstrap_group=ON; mysql> START GROUP_REPLICATION; '这里开启组复制可能会报错,查看mysql日志' 'cat /var/log/mysqld.log ,找到解决办法' 2019-03-14T08:51:52.838185Z 0 [Note] Plugin group_replication reported: 'To force this member into the group you can use the group_replication_allow_local_disjoint_gtids_join option' mysql -pWsp+123ld mysql> STOP GROUP_REPLICATION; mysql> set global group_replication_allow_local_disjoint_gtids_join=on; ##直接做这一步 不用等报错 mysql> START GROUP_REPLICATION; 恢复正常测试: 配置好后在server1上查看
mysql> SELECT * FROM performance_schema.replication_group_members; +---------------------------+--------------------------------------+-------------+-------------+--------------+ | CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | +---------------------------+--------------------------------------+-------------+-------------+--------------+ | group_replication_applier | 43a9e177-46c2-11e9-9b8a-52540039a8e5 | server3 | 3306 | ONLINE | | group_replication_applier | 926e543f-4635-11e9-bb87-525400cf2a01 | server2 | 3306 | ONLINE | | group_replication_applier | de5a155a-462d-11e9-b9df-5254004c61ed | server1 | 3306 | ONLINE | +---------------------------+--------------------------------------+-------------+-------------+--------------+看到3台都是online,表示正常
添加数据测试:
mysql> CREATE DATABASE test; mysql> USE test; mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL); mysql> INSERT INTO t1 VALUES (1, 'Luis'); mysql> SELECT * FROM t1;在任何节点写入数据,其他节点也能看到 如在server3上:
mysql> INSERT INTO t1 VALUES (2, 'wsp'); 在server2和server1上查看: mysql> select * from t1; +----+------+ | c1 | c2 | +----+------+ | 1 | Luis | | 2 | wsp | +----+------+