码迷,mamicode.com
首页 > 其他好文 > 详细

027:高级复制

时间:2018-02-27 19:19:30      阅读:187      评论:0      收藏:0      [点我收藏+]

标签:session   block   未使用   在线   ati   提交   inno   开始   5.0   

[TOC]


一.半同步和无损复制

主从复制,基本上都是 异步复制 , Master并不关Slave节点有没有获取到数据 ,所以复制效率很高,但是数据有可能会丢失。

从 MySQL5.5 开始,MySQL推出了semi-sync replication (半同步复制)

  • 至少有一个Slave节点收到binlog后再返回( IO线程接收到即可 )
  • 减少数据丢失风险
  • 不能完全避免数据丢失
  • 超时后,切换回异步复制

从 MySQL5.7.2 开始,MySQL推出了lossless semi-sync replication (无损复制)

  • 二进制日志(binlog)先写远程( IO线程接收到即可 )
  • 可保证数据完全不丢失

技术分享图片

1.1. loss less / semi-sync replication插件安装

  1. 手工安装
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
  1. 方式二 – 写入配置文件
[mysqld]
plugin_dir=/usr/local/mysql/lib/plugin
plugin_load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"

上述操作 仅仅是加载了插件 ,还 未启动 对应的功能,需要配置额外的参数:

[mysqld]
# 等同于 rpl_semi_sync_master_enabled = 1
loose_rpl_semi_sync_master_enabled = 1
# 等同于 rpl_semi_sync_slave_enabled = 1
loose_rpl_semi_sync_slave_enabled = 1
# 超时5秒后,则切换回异步方式
loose_rpl_semi_sync_master_timeout = 5000

使用 loose_ 前缀表示如果没有加载 semi_sync 的插件,则 忽略该参数
当Slave在Timeout后,又追上了Master了( IO线程 ),则会 自动切换回半同步复制

注意:半同步复制 / 无损复制主从 上都要 安装插件和开启功能

注意:要保证任意时刻发生一台机器宕机都不丢失数据的前提是 master sync_binlog设置为1,slave sync_relay_log设置为1。

1.2. semi-sync replication

semi-sync replication 称为 半同步复制 ,在一个事务 提交(commit) 的过程时,在 InnoDB 层的 commit log 步骤后,Master节点需要收到 至少一个 Slave节点回复的 ACK (表示 收到了binlog )后,才能继续下一个事务;
如果在一定时间内(Timeout)内 没有收到ACK ,则 切换为异步模式 ,具体流程如下:

技术分享图片

对应的配置参数如下:

[mysqld]
# 开启主的半同步复制
rpl_semi_sync_master_enabled=1
# 开启从的半同步复制
rpl_semi_sync_slave_enabled=1
# 超时1秒,切回异步
rpl_semi_sync_master_timeout=1000
# 至少收到 1 个 slave发回的ack
rpl_semi_sync_master_wait_for_slave_count=1

1.3. loss less semi-sync replication

loss less semi-sync replication 称为 无损复制 ,在一个事务提交(commit) 的过程时,在 MySQL 层的write binlog 步骤后,Master节点需要收到 至少一个 Slave节点回复的 ACK (表示 收到了binlog )后,才能继续下一个事务;
如果在一定时间内(Timeout)内 没有收到ACK ,则 切换为异步模式 ,具体流程如下:

技术分享图片

对应的配置参数如下:

[mysqld]
# 开启主的半同步复制
rpl_semi_sync_master_enabled=1
# 开启从的半同步复制
rpl_semi_sync_slave_enabled=1
# 超时1秒,切回异步
rpl_semi_sync_master_timeout=1000
[mysqld57]
# 控制 半同步复制 还是 无损复制 的参数
# - AFTER_SYNC 表示的是无损复制;(5.7 默认)
# - AFTER_COMMIT 表示的是半同步复制;
rpl_semi_sync_master_wait_point=AFTER_SYNC
# 至少收到 1 个 slave发回的ack
rpl_semi_sync_master_wait_for_slave_count=1

1.4. 半同步复制与无损复制的对比

  • ACK的时间点不同

    • 半同步复制InnoDB层Commit Log后等待ACK,主从切换会有数据丢失风险。
    • 无损复制MySQL Server层Write binlog后等待ACK,主从切换会有数据变多风险。
  • 主从数据一致性

    • 半同步复制意味着在Master节点上,这个刚刚提交的事务对数据库的修改,对其他事务是可见的。因此,如果在等待Slave ACK的时候crash了,那么会对其他事务出现幻读数据丢失
    • 无损复制在write binlog完成后,就传输binlog,但还没有去写commit log,意味着当前这个事务对数据库的修改,其他事务也是不可见的。因此,不会出现幻读,数据丢失风险。
    • 因此5.7.2引入了无损复制(after_sync)模式,带来的主要收益是解决 after_commit导致的master crash数据丢失问题,因此在引入after_sync模式后,所有提交的数据已被复制,故障切换时数据一致性将得到提升。

参考- MySQL半同步复制数据一致性分析

1.5. 演示无损/半同步复制

  • master server
mysql root@localhost:employees> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected
Time: 0.063s

mysql root@localhost:employees>  INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
Query OK, 0 rows affected
Time: 0.005s

mysql root@localhost:employees> set global rpl_semi_sync_master_enabled = 1;
Query OK, 0 rows affected
Time: 0.020s

mysql root@localhost:employees> set global rpl_semi_sync_master_timeout = 5000;
Query OK, 0 rows affected
Time: 0.001s

mysql root@localhost:employees> show global 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    |   -- status ok
| 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     |
| Rpl_semi_sync_slave_status                 | OFF   |
+--------------------------------------------+-------+
15 rows in set
Time: 0.013s
mysql root@localhost:employees>
  • slave server
(root@localhost) 09:59:14 [employees]> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected (0.05 sec)

(root@localhost) 18:07:15 [employees]> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
Query OK, 0 rows affected (0.00 sec)

(root@localhost) 18:07:29 [employees]> set global rpl_semi_sync_slave_enabled = 1;
Query OK, 0 rows affected (0.00 sec)

(root@localhost) 18:14:59 [employees]> show global variables like '%semi%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | OFF        |
| 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_semi_sync_slave_enabled               | ON         |
| rpl_semi_sync_slave_trace_level           | 32         |
+-------------------------------------------+------------+
8 rows in set (0.02 sec)

半复制切换异步同步过程的状态

  • master server
(root@localhost) 10:11:10 [tablespace]>  show global status like "%rpl%";
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 1     |  -- 半同步复制的client数量
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |  
| Rpl_semi_sync_master_net_waits             | 1     |  -- master总的等待slave的次数
| Rpl_semi_sync_master_no_times              | 0     |  -- 切成异步的次数(no = number of)
| 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      | 2209  |  -- master等待事物的平均时间
| Rpl_semi_sync_master_tx_wait_time          | 2209  |  -- master等待事物的总的时间
| Rpl_semi_sync_master_tx_waits              | 1     |  -- master等待事物的次数
| 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   |
+--------------------------------------------+-------+
15 rows in set (0.03 sec)
  • slave server
(root@localhost) 13:47:48 [tablespace]> stop slave io_thread;  --停掉IO线程
Query OK, 0 rows affected (0.05 sec)
  • master server
(root@localhost) 14:48:11 [tablespace]>  show global 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      | 2209  |
| Rpl_semi_sync_master_tx_wait_time          | 2209  |
| 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   |
+--------------------------------------------+-------+
15 rows in set (0.00 sec)

(root@localhost) 14:48:14 [tablespace]> insert into qqq values(99);  --插入sql
Query OK, 1 row affected (5.04 sec)    -- 等待5秒后,切成异步

(root@localhost) 14:49:28 [tablespace]>  show global 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   |  --status 为off
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 2209  |
| Rpl_semi_sync_master_tx_wait_time          | 2209  |
| 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   |
+--------------------------------------------+-------+
15 rows in set (0.00 sec)

(root@localhost) 14:49:57 [tablespace]>

1.6. 两种复制方式的性能

技术分享图片

备注:上图是Facebook的测试性能图;其中Y轴是QPS,X轴是并发数

  • 蓝色的 Normal Slave异步复制
    • 性能很好,但是随着并发数的增长,性能有所下降
  • 绿色的 Enhanced mysqlbinlog无损复制
    • 随着并发数的增长,性能几乎是线性增长的,在高并发下,性能会优于异步复制
  • 紫色的 Normal Semi Slave 是 半同步复制
    • 性能较低

无损复制性能优于半同步复制的原因

  1. 等待ACK回包问题上,其实两种复制的开销是一样的,没有区别,都是网络的等待开销。
  2. 无损复制由于在 write binlog (commit 的第二步)后,需要等待ACK,后续的事务无法提交,这样就 堆积N多个需要落盘的事务(半同步复制由于已经提交了事务,没有堆积事务的效果),通过 组提交 机制一次 fsync的多个事务(半同步复制也有组提交,只是一次 fsync 的事务数没那么多), 相当于提高了I/O性能 ;所以线程(事务)越多,效果越明显,以至于有上图中超过异步复制的效果。(无损复制的组提交比例比原版的高3~4倍)

产生上述测试效果的前提:测试用例是 IO Bound 的(比如数据量有 100G,而 buffer pool 只有 10G),且并发数足够多。

下面这两个参数不要去设置,设置了反而性能差

(root@localhost) 10:10:47 [tablespace]> show variables like "%binlog_group%";
+-----------------------------------------+-------+
| Variable_name                           | Value |
+-----------------------------------------+-------+
| binlog_group_commit_sync_delay          | 0     |
| binlog_group_commit_sync_no_delay_count | 0     |   -- 等待一组里面有多少事务我才提交
+-----------------------------------------+-------+
2 rows in set (0.01 sec)

(root@localhost) 10:10:50 [tablespace]> show variables like "%binlog_max%";
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| binlog_max_flush_queue_time | 0     |               -- 等待多少时间后才进行组提交
+-----------------------------+-------+
1 row in set (0.00 sec)

(root@localhost) 10:11:10 [tablespace]>

1.7. rpl_semi_sync_master_wait_for_slave_count

该参数控制Master在收到 多少个 Slave的ACK 后,才可以继续commit。配置多个ACK和配置一个ACK的效果是类似的,因为他们是 并行执行 的(理论上来说不会有两倍的等待时间), 取决于最慢的那个 。


二. 并行复制(Multi-Threaded Slave)

2.1. MTS介绍

在官方文档中,并行复制的叫法为 Multi-Threaded Slave (MTS)

  • MySQL的并行复制基于组提交:

一个组提交中的事务都是可以并行执行的 ,因为既然处于组提交中,这意味着事务之间没有冲突(不会去更新同一行数据),否则不可能在同一个组里面。
Slave上开启并行复制,需要在配置文件中增加以下参数:

[mysqld]
slave-parallel-type=LOGICAL_CLOCK   
slave_preserve_commit_order=1
slave-parallel-workers=4
  • slave-parallel-type 参数

    • DATABASE 基于库级别的并行复制,如果只有一个库,就还是串行(为了兼容5.6)。
    • LOGICAL_CLOCK 基于逻辑时钟,主上怎么并行执行,从上也是怎么并行回放的。
  • slave-parallel-workers 并行复制的线程数,一般设置为一个组内提交的事务数,线上设置为32足够了

  • slave_preserve_commit_order Slave上commit的顺序保持一致,必须为1,否则可能会有GAP锁产生

2.2. 动态调整复制线程数

配置并行复制后,Slave节点可以看到4个 Coordinator 线程

mysql root@localhost:(none)> show processlist;
+----+-------------+-----------+--------+---------+--------+--------------------------------------------------------+------------------+
| Id | User        | Host      | db     | Command | Time   | State                                                  | Info             |
+----+-------------+-----------+--------+---------+--------+--------------------------------------------------------+------------------+
| 1  | system user |           | <null> | Connect | 148155 | Waiting for master to send event                       | <null>           |
| 2  | system user |           | <null> | Connect | 57792  | Slave has read all relay log; waiting for more updates | <null>           |
| 3  | system user |           | <null> | Connect | 57791  | Waiting for an event from Coordinator                  | <null>           |
| 4  | system user |           | <null> | Connect | 148155 | Waiting for an event from Coordinator                  | <null>           |
| 7  | system user |           | <null> | Connect | 148155 | Waiting for an event from Coordinator                  | <null>           |
| 8  | system user |           | <null> | Connect | 148155 | Waiting for an event from Coordinator                  | <null>           |
| 12 | root        | localhost | <null> | Query   | 0      | starting                                               | show processlist |
+----+-------------+-----------+--------+---------+--------+--------------------------------------------------------+------------------+
7 rows in set
Time: 0.019s
mysql root@localhost:(none)>

-- 动态调整方式如下:

mysql root@localhost:(none)> set global slave_parallel_workers=8;
Query OK, 0 rows affected
Time: 0.003s
mysql root@localhost:(none)> stop slave;  一定要重启一下slave才能有效
Query OK, 0 rows affected
Time: 0.038s
mysql root@localhost:(none)> start slave;
Query OK, 0 rows affected
Time: 0.080s
mysql root@localhost:(none)> show processlist;
+----+-------------+-----------+--------+---------+------+--------------------------------------------------------+------------------+
| Id | User        | Host      | db     | Command | Time | State                                                  | Info             |
+----+-------------+-----------+--------+---------+------+--------------------------------------------------------+------------------+
| 12 | root        | localhost | <null> | Query   | 0    | starting                                               | show processlist |
| 13 | system user |           | <null> | Connect | 3    | Waiting for master to send event                       | <null>           |
| 14 | system user |           | <null> | Connect | 3    | Slave has read all relay log; waiting for more updates | <null>           |
| 15 | system user |           | <null> | Connect | 3    | Waiting for an event from Coordinator                  | <null>           |
| 16 | system user |           | <null> | Connect | 3    | Waiting for an event from Coordinator                  | <null>           |
| 17 | system user |           | <null> | Connect | 3    | Waiting for an event from Coordinator                  | <null>           |
| 18 | system user |           | <null> | Connect | 3    | Waiting for an event from Coordinator                  | <null>           |
| 19 | system user |           | <null> | Connect | 3    | Waiting for an event from Coordinator                  | <null>           |
| 20 | system user |           | <null> | Connect | 3    | Waiting for an event from Coordinator                  | <null>           |
| 21 | system user |           | <null> | Connect | 3    | Waiting for an event from Coordinator                  | <null>           |
| 22 | system user |           | <null> | Connect | 3    | Waiting for an event from Coordinator                  | <null>           |
+----+-------------+-----------+--------+---------+------+--------------------------------------------------------+------------------+
11 rows in set
Time: 0.019s
mysql root@localhost:(none)>

特别注意:

这里的 并行复制 指的是 SQL Thread (回放线程),而非IO Thread (IO线程)
Waiting for master to send event 这个 Stateshow processlist 中只有一个,即只有一个 IO Thread

线上环境可以配置成两台Slave做无损复制(保证数据不丢),其他的Slave做异步复制(配置为只读,用于负载均衡),都指向同一台Master。


三. GTID

3.1. GTID的介绍

  • 1.Global Transaction Id entifier -- 全局事物ID

  • 2.GTID = Server_UUID + Transaction_ID

    • Server_UUID 是全局唯一的
    • Transaction_ID 是自增的
  • 3.GTID 的作用是替代 Filename + Position

(root@localhost) 14:54:25 [tablespace]> show variables like "server_uuid";
+---------------+--------------------------------------+
| Variable_name | Value                                |
+---------------+--------------------------------------+
| server_uuid   | 9dc847d8-bf72-11e7-9ec4-000c2998e4f1 |
+---------------+--------------------------------------+
1 row in set (0.01 sec)

在MySQL中看到的 UUID ,实际是保存在 $DATADIR/auto.cnf 中的,且该文件是服务器初始化的时候自动生成的。

[root@node1 mysqldata]# cat /r2/mysqldata/auto.cnf
[auto]
server-uuid=9dc847d8-bf72-11e7-9ec4-000c2998e4f1
[root@node1 mysqldata]#

通过 冷备 做备份,拷贝 $DATADIR 时,记得要把备份中的 auto.cnf 给删除、

3.2. GTID的意义

技术分享图片

  • 未使用GTID
    • Master宕机 后,一个 Slave选举提升New Master,如果需要重建复制关系,就需要把另外两个SlaveCHANGE MASTER 指向 New Master
    • 那问题来了,原来Slave是指向 MasterFilename_M + Position_M 的位置,现在要指向 New Master 上新的 Filename_N + Position_N 的位置,这 两个位置是比较难对应起来的; 此时两个Slave要继续重建复制关系(CHANGE MASTER)会比较麻烦。
  • 使用GTID

    • 和上面一样的场景,选举机制提升为New Master GTIDZ执行到最新, 两个Slave需要重新指向 New Master ,由于 使用了GTID ,目前 Slave-A 获取到的日志对应的GTID为GTID_ASlave-B 获取到的日志对应的 GTID为GTID_B;
    • 此时 New Master 上是存在 GTID_A 和 GTID_B (通过选举出来的,获取的日志应该是最多的),那两个Slave就可以直接使用 GTID_A 和 GTID_B 这两个GTID,通过指向 New Master接着继续复制;

3.3. GTID的配置

[mysqld]
log_bin = bin.log
log_slave_updates = 1
gtid_mode = ON 
enforce_gtid_consistency = 1
  • 注意:

1.MySQL5.6 必须开启参数 log_slave_updates (5.6版本的限制)

2.MySQL5.6 升级到gtid模式需要停机重启

3.MySQL5.7 版本开始可以不开启 log_slave_updates

4.MySQL5.7.6 版本开始可以在线升级gtid模式

027:高级复制

标签:session   block   未使用   在线   ati   提交   inno   开始   5.0   

原文地址:https://www.cnblogs.com/gczheng/p/8480210.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!