• 运维特工,战胜心魔!!
  • 有些事情其实没有那么难,只是我们感觉难,走出第一步!
  • 你所浪费的今天,是昨天死去的人奢望的明天!!!
  • 欢迎访问 unixfbi.com 运维特工社区http://unixfbi.com/
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧
  • 运维特工:http://www.unixfbi.com
  • 人生没有白走的路,每一步都算数!!
  • 空杯心态,沉淀自己!

MySQL半同步复制

MySQL unixfbi 1年前 (2018-06-02) 5242次浏览 已收录 0个评论 扫描二维码
文章目录[隐藏]

MySQL 半同步复制

一、MySQL 半同步复制介绍

1.MySQL 半同步复制

从 MySQL5.5 开始,MySQL 以插件的形式支持半同步复制。下面我们结合异步和全同步的概念来理解半同步复制。

  • asynchronous replication (异步复制):
    MySQL 默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主如果 crash 掉了,此时主上已经提交的事务可能并没有传到从上,如果此时,强行将从提升为主,可能导致新主上的数据不完整。

  • fully synchronous replication (全同步复制): 指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会受到严重的影响。

  • Semisynchronous replication (同步复制): 5.6 版本里称为半同步,在 5.7 版本里被称为增强半同步。半同步是介于异步复制和全同步复制之间的一个折中方案,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到 relay log 中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个 TCP/IP 往返的时间。所以,半同步复制最好在低延时的网络中使用,比如局域网。

2.MySQL 半同步的诞生背景

普通的 replication,是 MySQL 的异步复制,依靠 MySQL 二进制日志(binary log)进行数据复制。例如两个机器,一台为主机上为 master,另一台主机上为 slave;
(1)正常的复制为:事务(t1)写入 binlog,dump 线程通知 slave 有新的事务 t1,slave 的 io 线程接收到 t1 并写入到自己的的 relay log,slave 的 sql 线程写入到本地数据库。 这时,master 和 slave 都能看到这条新的事务,即使 master 挂了,slave 可以提升为新的 master。

(2) 异常的复制为:事务(t1)写入 binlog,dump 线程通知 slave 有新的事务 t1,slave 因为网络不稳定,一直没有收到 t1;这时 master 挂掉,slave 提升为新的 master,那么 t1 就会丢失。

(3)很大的问题是:master 和 slave 事务更新的不同步,就算是没有网络或者其他系统的异常,当业务并发上来时,slave 因为要顺序执行 master 批量事务,而且 sql_thread 是单线程,导致很大的延迟。

为了弥补异步复制以上几种场景的不足,这时 MySQL 半同步复制就诞生了。MySQL 从 5.5 开始推出了半同步复制(Semisynchronous replication

3.MySQL 半同步原理

MySQL 半同步原理:
MySQL 半同步复制

Master 将每个事务写入 binlog,传递到 Slave 刷新到磁盘(relay log),同时主库提交事务。Master 等待 Slave 反馈收到 relay log,只有收到 ACK 后 Master 才将 commit OK 结果反馈给客户端。

4.MySQL 半同步复制的潜在问题

根据 MySQL 半同步原理,我们可以知道,半同步复制不是万能的。也是有一些问题的。
半同步复制的潜在问题:
(1)如果异常发生,会降级为普通的复制,那么从库出现数据不一致的几率会减少,并不是完全消失。
(2)主库 dump 线程承担的工作变多了,这样显然会降低整个数据库的性能。
(3)在 MySQL 5.5~5.6 使用after_commit的模式下,客户端事务在存储引擎层提交后,再得到从库确认的过程中,主库宕机了,此时,可能的情况有两种都会导致数据不一致。

下面介绍 MySQL 半同步复制导致数据不一致的两种场景:
(1)事务还没有发送到从库上:
此时,客户端会收到事务提交失败的信息,客户端会重新提交该事务到新的主库上,当宕机的主库重新启动后,以从库的身份重新加入到该主从结构中,会发现,该事务在从库中被提交了两次,一次是之前作为主的时候,一次是被新主同步过来的,数据出现不一致情况。

(2)事务已经发送到从库上:
此时,从库已经收到并应用了该事务,但是客户端仍然会收到事务提交失败的信息,重新提交该事务到新的主上,数据出现不一致情况。

5.增强半同步复制的产生

针对 MySQL 半同步复制存在的潜在问题。MySQL5.7 引入了一种新的半同步方案:Loss-less 半同步复制,也被称为增强半同步复制

随着 MySQL 5.7 版本的发布,半同步复制技术升级为全新的Loss-less Semi-Synchronous Replication 架构,其成熟度、数据一致性与执行效率得到显著的提升。
MySQL 半同步复制

从上图可以看出,增强半同步复制和半同步复制的区别是 :Waiting Slave Dump被调整到了Storage Commit之前了。

增强半同步复制中,Master 把 binlog 发送给 slave,只是在 Slave 把 binlog 写到本地的 relay-log 后,Master 才会将事务提交到存储引擎层,然后把请求返回给客户端,客户端才可以看见刚才提交的事务。在一个事务提交的过程中,在 MySQL Server 层的 binlog 阶段后,Master 节点需要接收到至少一个 Slave 节点回复的 ACK 后,才能继续下一个事务。

之前的半同步方案同样支持增强半同步,其实新版本的半同步就是引入了一个新的参数来进行控制半同步模式下 主库在返回给会话事务成功之前提交事务的方式。参数为rpl_semi_sync_master_wait_point 这个参数有两种取值:

  • AFTER_SYNC:这个就是新的半同步方案,Waiting Slave dumpStorage Commit之前。

  • AFTER_COMMIT:这是老的半同步方案。

这两个参数后面会详细说明;

半同步复制与增强半同步复制的对比:
(1)ACK 的时间点不同:

  • 半同步复制在 InnoDB 层的 Commit Log 后,等待 ACK;

  • 增强半同步是在 MySQL Server 层的 Write binlog 后,等待 ACK;

(2)主从数据一致性:

  • 半同步复制意味着在 Master 节点上,这个刚刚提交的事务对数据库的修改,对其他事务是可见的;

  • 增强半同步复制在 Write binlog 完成后,就传输 binlog,但还没有去写 commit log,意味着当前这个事务对数据库的修改,其他事务也是不可见的。

二、半同步的安装配置

安装半同步复制,必须满足几点条件:

  • MySQL5.5 或者更高版本;

  • MySQL 需要支持动态加载,参数have_dynamic_loading 设置为 YES

  • 要求环境中已经配置了 MySQL 主从复制

1.安装半同步模块

半同步复制是以插件的形式实现的,所以需要安装半同步模块。模块就在/usr/local/mysql/lib/plugin/目录下面;
分别在主库和从库上执行INSTALL PLUGIN命令加载插件;
on master:

mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';

on each slave:

mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';

查看插件安装情况:
方法一:
master :

mysql> SHOW PLUGINS;
| rpl_semi_sync_master       | ACTIVE   | REPLICATION        | semisync_master.so | GPL     |

slave:

mysql> SHOW PLUGINS;
| rpl_semi_sync_slave        | ACTIVE   | REPLICATION        | semisync_slave.so | GPL     |

上面显示的结果就表示已经成功安装了半同步复制插件;
方法二:
master:

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS  WHERE PLUGIN_NAME LIKE '%semi%';
+----------------------+---------------+
| PLUGIN_NAME          | PLUGIN_STATUS |
+----------------------+---------------+
| rpl_semi_sync_master | ACTIVE        |
+----------------------+---------------+
1 row in set (0.00 sec)

slave:

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)

在 Master 和 Slave 上都已经显示插件状态为ACTIVE,表示已经成功安装了半同步插件。

2.开启半同步插件

主从都开启半同步插件
master:

mysql> set global   rpl_semi_sync_master_enabled=1;
mysql> set global   rpl_semi_sync_master_timeout=1000;  #单位是毫秒

slave:

mysql> set global   rpl_semi_sync_slave_enabled=1;

rpl_semi_sync_master_timeout 表示主库等待从库响应的超时时间,如果在这个时间内没有收到从库回应,半同步复制切换到异步模式。

3.重启 slave 上的 IO_THREAD 线程

mysql> stop slave io_thread;
mysql> start slave io_thread;

如果没有重启,则默认还是异步复制,重启后 slave 会在 master 上注册为半同步复制的 slave 角色。

4.查看半同步是否运行

master:

mysql> show status like 'Rpl_semi_sync_master_status';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | ON    |
+-----------------------------+-------+
1 row in set (0.00 sec)

slave:

mysql> show status like 'Rpl_semi_sync_slave_status';      
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON    |
+----------------------------+-------+
1 row in set (0.00 sec)

这两个变量常用来监控主从是否运行在半同步复制模式下;
我们还可以这样查看半同步状态:

mysql> 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 |
+-------------------------------------------+------------+
6 rows in set (0.00 sec)

到此,MySQL 半同步复制已经搭建完成。

5.验证半同步是否可以切换为异步复制

我这里暂时把超时时间设为 20s;

mysql> set global rpl_semi_sync_master_timeout = 20000;

master:

mysql> create database dbtest;
Query OK, 1 row affected (0.00 sec)

mysql> create table dbtest.tst(id int);
Query OK, 0 rows affected (0.21 sec)

mysql> insert into dbtest.tst values(1);
Query OK, 1 row affected (0.01 sec)

停止 slave 上的 io_thread 线程;

mysql> stop slave io_thread;

在 master 上再次插入数据

mysql> insert into dbtest.tst values(2);
Query OK, 1 row affected (20.00 sec)

插入一条数据花了 20s,和我们设置的超时时间 20s 相吻合;
查看一下 master 半同步状态:

mysql> show status like 'Rpl_semi_sync_master_status';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | OFF   |
+-----------------------------+-------+
1 row in set (0.00 sec)

查看 slave 半同步状态:

mysql> show status like 'Rpl_semi_sync_slave_status';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | OFF   |
+----------------------------+-------+
1 row in set (0.00 sec)

查看 master 和 slave 的状态均变为:OFF
同时,master 的 error.log 中打印的日志如下:

2017-08-24T06:54:07.154677Z 14 [ERROR] Semi-sync master failed on net_flush() before waiting for slave reply
2017-08-24T06:54:07.154721Z 14 [Note] Stop semi-sync binlog_dump to slave (server_id: 2)
2017-08-24T06:54:07.213255Z 14 [Note] Aborted connection 14 to db: 'unconnected' user: 'repl' host: 'dev-hd-node3' (Found net error)
2017-08-24T06:54:27.154681Z 23 [Warning] Timeout waiting for reply of binlog (file: mysql-bin.000002, pos: 2142), semi-sync up to file mysql-bin.000002, position 1890.
2017-08-24T06:54:27.154727Z 23 [Note] Semi-sync replication switched OFF.

开启 slave 上 io_thread 线程:

mysql> start slave io_thread;

master 上插入一条数据:

mysql> insert into dbtest.tst values(3);
Query OK, 1 row affected (0.00 sec)

在 slave 上执行 start slave,主的 insert 操作很快就能返回;
同时,master 上的 error.log 中会打印如下日志:

2017-08-24T06:56:40.781565Z 24 [Note] Start binlog_dump to master_thread_id(24) slave_server(2), pos(, 4)
2017-08-24T06:56:40.781606Z 24 [Note] Start semi-sync binlog_dump to slave (server_id: 2), pos(, 4)
2017-08-24T06:56:40.957024Z 0 [Note] Semi-sync replication switched ON at (mysql-bin.000002, 2142)

此时查看 master 和 slave 的半同步状态:
master:

mysql> show status like 'Rpl_semi_sync_master_status';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | ON    |
+-----------------------------+-------+
1 row in set (0.00 sec)

slave:

mysql> show status like 'Rpl_semi_sync_slave_status';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON    |
+----------------------------+-------+
1 row in set (0.01 sec)

Master 和 Slave 半同步状态都变成了ON

6.在配置文件中配置半同步复制

以上的半同步复制的启动方式都是在命令行操作的,重启数据库后配置就会失效,所以我们可以配置到配置文件中。
master:

#symi replication
plugin-load=rpl_semi_sync_master=semisync_master.so
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000 # 1 second

slave:

#symi replication
plugin-load=rpl_semi_sync_slave=semisync_slave.so
rpl_semi_sync_slave_enabled=1

在 MySQL 高可用架构中,Master 和 Slave 需要同时启动相同半同步复制,以便在切换后能继续使用半同步复制。
Master 和 Slave 同时配置:

#symi replication
plugin-load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"
rpl-semi-sync-master-enabled = 1
rpl_semi_sync_master_timeout=1000 # 1 second
rpl-semi-sync-slave-enabled = 1

三、半同步各变量介绍

1.环境变量

mysql> 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 | 1          |
| rpl_semi_sync_master_wait_no_slave        | ON         |
| rpl_semi_sync_master_wait_point           | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set (0.00 sec)

rpl_semi_sync_master_enabled: 表示 master 是否开启半同步;
rpl_semi_sync_master_timeout: 启用半同步后 master 接收 slave ACK 的超时时长,单位是毫秒。如果超过这个值的话,半同步会变为异步复制;
rpl_semi_sync_master_wait_for_slave_count: 默认值为 1,该变量设置 master 需要等待多少个 slave 应答 ACK ,才能 commit 并返回给客户端;

rpl_semi_sync_master_wait_no_slave: 默认值为 ON , 当状态变量Rpl_semi_sync_master_clients中的值小于rpl_semi_sync_master_wait_for_slave_count时,Rpl_semi_sync_master_status依旧显示为 ON。当状态变量Rpl_semi_sync_master_clients中的值大于rpl_semi_sync_master_wait_for_slave_count时,Rpl_semi_sync_master_status立即显示为 OFF,即异步复制。

如果主从架构为 1 主 2 从,2 个从都采用了半同步复制,且设置的是rpl_semi_sync_master_wait_for_slave_count=2,如果其中一个挂掉了,对于rpl_semi_sync_master_wait_no_slave设置为 ON 的情况,此时显示的仍然是半同步复制,如果rpl_semi_sync_master_wait_no_slave设置为 OFF,则会立刻变成异步复制。

rpl_semi_sync_master_wait_point : 这是系统级别的参数,复制其实就是对semi sync增加了rpl_semi_sync_master_wait_point参数。该参数来控制半同步模式下主库在返回给会话事务成功之前提交事务的方式。

这个参数有两个值: AFTER_SYNCAFTER_COMMIT , 默认是AFTER_SYNC

AFTER_SYNC: 5.7 默认值,5.5/5.6 无该模式,Master 把每个事务写入 binlog 传递到 Slave 刷新到磁盘(relay log)。Master 等待 Slave 反馈收到 relay log 的 ACK 之后,再提交事务并且返回 commit OK 结果给客户端。即使在主库上已经提交的事务都能保证已经同步到 Slave 的 relay log 中。
AFTER_COMMIT: 5.6 默认值,Master 将每个事务写入 binlog,传递到 slave 刷新到磁盘(relay log),同时主库提交事务。Master 等待 Slave 反馈收到 relay log,只有收到 ACK 后 Master 才将 commit OK 结果反馈给客户端。

2.状态变量

mysql> 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             | 3     |
| Rpl_semi_sync_master_no_times              | 1     |
| Rpl_semi_sync_master_no_tx                 | 2     |
| Rpl_semi_sync_master_status                | ON    |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 672   |
| Rpl_semi_sync_master_tx_wait_time          | 1345  |
| Rpl_semi_sync_master_tx_waits              | 2     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 2     |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)

上面的状态变量,介绍几个比较重要的变量:
Rpl_semi_sync_master_clients: 当前半同步复制从库的个数,如果是一主多从的架构,并不包含异步复制从库的个数。
Rpl_semi_sync_master_no_tx: 没有成功接收 slave ACK ,提交事务的数量;
Rpl_semi_sync_master_yes_tx: 成功接收到 slave 的 ACK ,并且成功执行 commits 的数量。

参考文档

https://www.cnblogs.com/ivictor/p/5735580.html


UnixFBI 运维特工 , 版权所有丨如未注明 , 均为原创丨 转载请务必注明原文链接http://www.unixfbi.com/514.html
喜欢 (1)
[支付宝]
分享 (0)
unixfbi
关于作者:
运维工程师一枚,做有价值的事情!
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址