异步复制(Asynchronous Replication )
mysql默认的复制就是异步的,主库在执行完客户端提交的事物后会立即返回结果给客户端,并不判断从库是否已经接受并处理,这样就会有一个问题,如果这个时候主库crash,主库上已经提交的事物可能还没有传到从库上,如果此时,强行将从提升为主,可能导致新主上的数据丢失。
全同步复制(Fully Synchronous Replication )
指当主库执行完一个事物,所有的从库都执行了该事物才返回给客户端。因为需要等待所有从库执行完该事物才返回,所以全同步复制对性能影响很严重。
半同步复制(Semisynchronous Repalication)
介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟。
分为两种半同步:
(1)官方半同步
即参数 rpl_semi_sync_master_wait_point = after_commit时
原理:
客户端发送过来一个请求,经过mysql的SQL分析,存储引擎处理,写入binlog,然后再存储引擎层提交,写入从库的relaylog,最后返回给客户端。
缺点:
(1)在storage commit提交之后,其他会话都可以读到更新的数据,但是本客户端还没有,需要等待从库接收完日志才能返回结果。
(2)当在storage commit之后,此时如果主crash了,从库还没有接收到relaylog,这个时候要进行主从切换,会导致新主数据比原来主数据少,即丢失部分数据。
(2)增强半同步(又称社区半同步,无损lossless半同步等)
即参数 rpl_semi_sync_master_wait_point = after_sync (参数默认值)
原理:
客户端发送过来一个请求,经过mysql的SQL分析,存储引擎处理,写入binlog,然后写入从库的relaylog,存储引擎层提交,最后返回给客户端。
优点:
主库把更新先发送给从库的relaylog,然后再提交,返回结果给客户端,这个过程即使在storage commit之后主crash了,日志也已经写入到relaylog中,从库和主库数据一致。
要想使用半同步复制,必须满足以下几个条件:
1. MySQL 5.5及以上版本
2. 变量have_dynamic_loading为YES
3. 异步复制已经存在
4.不支持多源复制
1.基本环境
master | slave | |
数据库版本 | 5.7.16 | 5.7.16 |
IP | 192.168.91.16 | 192.168.91.22 |
serverid | 330616 | 330622 |
端口号 | 3306 | 3306 |
2.主从配置
master:
配置文件中设置:
server-id = 330616
binlog_format = row
log-bin = /data/mysql3306/logs/mysql-bin
skip_slave_start=1
log-slave-updates=0
gtid_mode=ON
enforce-gtid-consistency=ON
slave:
配置文件中设置:
server-id = 330622
binlog_format = row
relay-log=relay-bin
relay-log-index=relay-bin.index
skip_slave_start=1
log_slave_updates=0
gtid_mode=on
enforce-gtid-consistency=on
3.主库创建用户
master1:
create user rep@'192.168.91.%' identified by '147258';
grant replication slave on *.* to rep@'192.168.91.%';
4.从库 change master to
slave:
change master to
master_host='192.168.91.16',
master_port=3306,
master_user='rep',
master_password='147258',
master_auto_position=1;
5.安装插件
master:
install plugin rpl_semi_sync_master soname 'semisync_master.so';
show plugins;
也可以通过下载配置文件中重启服务加载插件:
plugin-load=rpl_semi_sync_master=semisync_master.so
slave:
install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
show plugins;
也可以通过下载配置文件中重启服务加载插件:
plugin-load=rpl_semi_sync_slave=semisync_slave.so
6.编辑配置文件
master
#semi
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000 #1 second
动态生效:
set global rpl_semi_sync_master_enabled=1;
set global rpl_semi_sync_master_timeout=1000;
slave
#semi
rpl_semi_sync_slave_enabled=1
动态生效:
set global rpl_semi_sync_slave_enabled=1;
在有的高可用架构下,如果需要主从切换继续使用半同步,主从都需要加载插件并添加配置文件:
#plugins
plugin-load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"
#semi
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000 #1 second
rpl_semi_sync_slave_enabled=1
7.参数和状态
variables:
rpl_semi_sync_master_enabled = ON
表示在master上已经开启半同步复制模式
rpl_semi_sync_master_timeout = 10000
默认为10000毫秒,等于10秒,这个参数动态可调,表示主库在某次事务中,如果等待时间超过10秒,那么则降级为异步复制模式,不再等待SLAVE从库。如果主库再次探测到,SLAVE从库恢复了,则会自动再次回到半同步复制模式。
rpl_semi_sync_master_trace_level = 32
用于开启半同步复制模式时的调试级别,默认是32
rpl_semi_sync_master_wait_for_slave_count=1;
该参数控制主需要等待多少个slave应答,才能返回给客户端,默认为1。
rpl_semi_sync_master_wait_point = AFTER_SYNC
半同步的方式
status:
Rpl_semi_sync_master_clients
当前半同步复制从的个数,如果是一主多从的架构,并不包含异步复制从的个数。
Rpl_semi_sync_master_yes_tx
表示成功使用半同步的事物
Rpl_semi_sync_master_no_tx
表示没有使用半同步的事物为:
例:
master:
怎么查看是否开启半同步:
root@localhost [testdb]> show global variables like '%semi%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON | --表示主库上面的半同步已经开启
| rpl_semi_sync_master_timeout | 1000 |
| 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 |
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+-------------------------------------------+------------+
测试插入一条数据:
root@localhost [testdb]>insert into t1 values(4,'ddd');
root@localhost [testdb]>show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 1 |
| 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 | 2920 |
| Rpl_semi_sync_master_tx_wait_time | 2920 |
| Rpl_semi_sync_master_tx_waits | 1 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 1 | --表示成功使用半同步的事物
| Rpl_semi_sync_slave_status | OFF |
+--------------------------------------------+-------+
slave:
root@localhost [(none)]>stop slave;
root@localhost [testdb]>show status like '%semi%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | OFF |
+----------------------------+-------+
master:
root@localhost [testdb]>insert into t1 values(5,'eee');
Query OK, 1 row affected (1.01 sec) --这里插入第一条数据用了1.01秒,是由rpl_semi_sync_master_timeout=1000ms控制,因为1秒后超时不使用半同步,变成普通复制。
root@localhost [testdb]>insert into t1 values(6,'fff');
Query OK, 1 row affected (0.00 sec) --后面的操作默认不使用半同步,所以用时比较快。
root@localhost [testdb]>show global status like '%semi%';
+--------------------------------------------+-------+
| 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 | 1 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 2 | ----表示没有使用半同步的事物为2个,如果这个值比较大,那么从库可能有问题,需要优化。
| Rpl_semi_sync_master_status | OFF |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 2920 |
| Rpl_semi_sync_master_tx_wait_time | 2920 |
| Rpl_semi_sync_master_tx_waits | 1 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 1 |
| Rpl_semi_sync_slave_status | OFF |
+--------------------------------------------+-------+
rpl_semi_sync_master_wait_for_slave_count=N;
该参数控制主需要等待多少个slave应答,才能返回给客户端,默认为1。
例:
root@localhost [testdb]>set global rpl_semi_sync_master_wait_for_slave_count=2;
root@localhost [testdb]>show variables like '%semi%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 1000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 2 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
+-------------------------------------------+------------+
root@localhost [testdb]>insert into t2 values(6,'fff');
Query OK, 1 row affected (1.03 sec) --超时之后采用异步复制模式
总结:
1. 在一主多从的架构中,如果要开启半同步复制,并不要求所有的从都是半同步复制。
2. MySQL 5.7极大的提升了半同步复制的性能。
(1)5.6版本的半同步复制,dump thread 承担了两份不同且又十分频繁的任务:传送binlog 给slave ,还需要等待slave反馈信息,而且这两个任务是串行的,dump thread 必须等待 slave 返回之后才会传送下一个 events 事务。dump thread 已然成为整个半同步提高性能的瓶颈。在高并发业务场景下,这样的机制会影响数据库整体的TPS 。
(2)5.7版本的半同步复制中,独立出一个 ack collector thread ,专门用于接收slave 的反馈信息。这样master 上有两个线程独立工作,可以同时发送binlog 到slave ,和接收slave的反馈。
参考链接:
https://dev.mysql.com/doc/refman/5.7/en/replication-semisync.html