####################################相关概念###################################################
(1)异步复制:主库上的事务不会等待从库的确认即返回客户端提交成功!
(2)同步复制:主库上提交的事务向客户端返回成功之前,需要收到所有从库提交事务的确认信息。
(3)半同步复制:异步复制和同步复制的折中,主库上提交事务时,需要等待至少一个从库发来的收到事件确认信息,才向客户端返回成功。
MySQL 5.5之前的复制都是异步的,主服务器在将更新操作写入二进制日志文件中后,不用管从服务器是否已经完成复制,就可以自由处理其它事务处理请求。异步复制 能提供较高的性能,但无疑易造成主/从服务器数据的不一致。MySQL 5.5开始引入半同步复制功能,此功能是由google开发的一个插件实现的。半同步复制要求主库提交的每一个事务,至少有一个备库成功接收后,才能继续 提交下一个。
半同步复制的概念详解:
当slave主机连接到master时,能够查看其是否已开启半同步复制功能。
当master上开启半同步复制的功能时,至少应该有一个slave开启此功能。此时,一个线程在master上提交事务将受到阻塞,直到得知一个已开启半同步复制功能的slave已收到此事务的所有事件,或等待超时。
当一个事务的事件都已写入relay-log中且已刷新到磁盘,slave才会告知已收到。在 master实例上,有一个专门的线程(ack_receiver)接收备库的响应消息。
如果等待超时,也就是master没被告知已收到,此时master会自动转换为异步复制模式。当至少一个半同步的slave赶上了,master与其slave自动转换为半同步复制。
半同步复制的功能要在master,slave都开启,若只开启一边,它依然为异步复制。
半同步复制原理图:
###################################实验搭建######################################################
-安装前提
1、MySQL5.5 版本或更高
2、主、备库的 have_dynamic_loading 系统变量值为 yes
3、主、备异步复制已部署
步骤:
在master(主节点)上:
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so'; ##导入半同步master模块
Query OK, 0 rows affected (0.11 sec)
mysql> set global rpl_semi_sync_master_enabled=1;
Query OK, 0 rows affected (0.00 sec)
mysql>set global rpl_semi_sync_master_timeout=10000 # 10s
#备注: rpl_semi_sync_master_enabled 参数控制主节点是否开启半同步复制;rpl_semi_sync_master_timeout 参数控制主节点等待备
节点返回确认信息的超时时间,单位为毫秒,超过这个时间后半同步复制转变成异步复制,这里设置成 10 秒。
mysql> show variables like 'rpl%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 10000 |
| 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_stop_slave_timeout | 31536000 |
+-------------------------------------------+------------+
7 rows in set (0.00 sec)
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 | ###一共有几次从Semi-sync跌回普通状态
| 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 | ###开启Semi-sync,事务返回需要等待的平均时间
| 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 | #### #表示通过半同步复制到从库的事务数
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
在slave(从节点)上:
mysql> select * from mysql.gtid_executed;
+--------------------------------------+----------------+--------------+
| source_uuid | interval_start | interval_end |
+--------------------------------------+----------------+--------------+
| 0e09c92a-8139-11e8-9d84-52540013d792 | 1 | 1 |
| 0e09c92a-8139-11e8-9d84-52540013d792 | 2 | 2 |
+--------------------------------------+----------------+--------------+
2 rows in set (0.00 sec)
mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
Query OK, 0 rows affected (0.11 sec)
mysql> show variables like 'rpl%';
+---------------------------------+----------+
| Variable_name | Value |
+---------------------------------+----------+
| rpl_semi_sync_slave_enabled | OFF |
| rpl_semi_sync_slave_trace_level | 32 |
| rpl_stop_slave_timeout | 31536000 |
+---------------------------------+----------+
3 rows in set (0.02 sec)
mysql> set global rpl_semi_sync_slave_enabled=1;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'rpl%';
+---------------------------------+----------+
| Variable_name | Value |
+---------------------------------+----------+
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
| rpl_stop_slave_timeout | 31536000 |
+---------------------------------+----------+
3 rows in set (0.01 sec)
mysql> show status like 'rpl%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | OFF |
+----------------------------+-------+
1 row in set (0.00 sec)
mysql> stop slave io_thread; ##重新开启io线程,使它完全打开
Query OK, 0 rows affected (0.01 sec)
mysql> start slave io_thread;
Query OK, 0 rows affected (0.00 sec)
mysql> show status like 'rpl%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON |
+----------------------------+-------+
1 row in set (0.00 sec)
测试:
master机写入数据,并接收到slave机返回的ack值
mysql> insert into user.usertb values('chaokaidi','21');
Query OK, 1 row affected (0.26 sec)
mysql> show status like 'rpl%';
+--------------------------------------------+-------+
| 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 | 1372 |
| Rpl_semi_sync_master_tx_wait_time | 1372 |
| 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 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
将slave机的io线程停掉,master机写入数据,会默认等待10s,如果超时还未等到slave机的ack,将自动切换到异步复制,如果slave机的io线程再次开启,复制将自动切换到半同步方式。
slave机
mysql> stop slave io_thread;
Query OK, 0 rows affected (0.04 sec)
master机
mysql> insert into user.usertb values('chao','22');
Query OK, 1 row affected (10.45 sec)
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 | 1 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 1 |
| Rpl_semi_sync_master_status | OFF |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 1372 |
| Rpl_semi_sync_master_tx_wait_time | 1372 |
| 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 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
先重复下MySQL复制原理,其通过三个线程来完成,在master节点上运行的binlogdump线程以及在slave节点上运行的I/O线程和SQL线程。具体如下:
1. master节点上的binlogdump线程,在slave与其正常连接的情况下,将binlog发送到slave上。
2.slave节点上的I/O线程,通过读取master节点发送的内容,并将数据复制到本地的relaylog中。
3.slave节点上的SQL线程,读取relaylog中的日志,并将其事务在本地执行。
Master节点的多个数据库并发进行事务提交,提交的事务根据LSN号顺序的写入binlog,slave节点通过I/O线程将master上的 binlog写到本地relaylog中,在slave节点只有一个SQL线程来执行relaylog中的日志,这样很容易造成slave延迟。在MySQL5.6中,引入了并发复制,这个并发复制是数据库级别的,这意味着一个SQL线程可以处理一个数据库的连续事务,而不用等待其它数据库完成。这个版本的并发复制,可以理解成一个数据库一个SQL线程。其与并发有关的参数如下:
slave_parallel_workers // worker 线程个数
slave-checkpoint-group // 隔多少个事务做一次 checkpoint
slave-checkpoint-period // 隔多长时间做一次 checkpoint
slave-pending-jobs-size-max // 分发给worker的、处于等待状态的event的大小上限
MySQL5.6基于DATABASE级别的并发复制可以解决业务表放在不同的database下同步延迟的问题,但是在实际生产中大部分表还是放 在同一个库中的,这种情况即使设置slave_parallel_workers大于0,也无法进行并发。在高并发的情况下,依然会造成主从复制延迟。在MySQL5.7中,引入了新的并发复制方法,基于LOGICAL_CLOCK的并发复制,可以支持在一个database中,并发执行relaylog中的事务。相同的二进制日志组在master上提交并行应用到slave节点上,没有跨数据库的限制,并且不需要把数据分割到多个数据库。要实现这个功能,需要在master节点标记binlog中提交的事务哪些是可以并发执行,虽然的MySQL5.6中已经引入binarylog group commit,但是没有将可并发的事务标记出来。
可以通过此命令来查看:mysqlbinlog-vvv mysql-bin.000106 | grep -i last_commit
在MySQL5.7中,已经解决了主从复制延迟的问题,具体配置参数如下:
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=16
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON
mysql> show processlist;
+----+-------------+-----------+------+---------+-------+--------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------------+-----------+------+---------+-------+--------------------------------------------------------+------------------+
| 1 | system user | | NULL | Connect | 52393 | Slave has read all relay log; waiting for more updates | NULL |
| 2 | system user | | NULL | Connect | 415 | Waiting for master to send event | NULL |
| 7 | root | localhost | NULL | Query | 0 | starting | show processlist |
+----+-------------+-----------+------+---------+-------+--------------------------------------------------------+------------------+
3 rows in set (0.00 sec)
[root@server2 ~]# vim /etc/my.cnf
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=16 ##线程数
master_info_repository=TABLE ##master_info存储方式为:table
relay_log_info_repository=TABLE ##relay_log_info存储方式为:table
relay_log_recovery=ON
[root@server2 ~]# /etc/init.d/mysqld restart
Stopping mysqld: [ OK ]
Starting mysqld: [ OK ]
mysql> show processlist;
+----+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
| 1 | system user | | NULL | Connect | 86 | Slave has read all relay log; waiting for more updates | NULL |
| 2 | system user | | NULL | Connect | 87 | Waiting for master to send event | NULL |
| 4 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 5 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 6 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 7 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 8 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 9 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 10 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 11 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 13 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 15 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 16 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 17 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 18 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 19 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 20 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 21 | system user | | NULL | Connect | 87 | Waiting for an event from Coordinator | NULL |
| 22 | root | localhost | NULL | Query | 0 | starting | show processlist |
+----+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
19 rows in set (0.00 sec)
查看mysql的表,新增了下面两个表
mysql> use mysql;
mysql> show tables;
----------------------------
slave_master_info
slave_relay_log_info
----------------------------