异步复制(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