MySQL 8.0 New Feature:Clone 插件

MySQL 8.0 New Feature:Clone 插件

原创安网易游戏运维平台

 

 

 

网易游戏 MySQL DBA, 主要负责网易游戏 MySQL SaaS 平台的设计与维护,也有关注 TiDB,CockRoachDB 等分布式数据库。

 

前言

Oracle 在 19 年 7 月下旬发布了 MySQL 的 8.0.17 版本,在这个版本中包含了一个全新的功能性插件:Clone。这个插件最初的目的是简化添加 Group Replication 新节点的操作,而这个插件实际发布之后,简化的操作不仅是添加 Group Replication 新节点,还包括了所有和备份恢复相关的操作,花费的时间远也低于常规的备份恢复手段。

原理介绍

MySQL Clone 插件如字面意思一般,它的作用就是 “Clone” 一个 MySQL 实例。安装这个插件之后,可以通过 MySQL client 登录到一个空的 MySQL 实例(推荐空实例),通过 Clone 命令获取到一份远端 MySQL 实例的一致性数据镜像,且整个过程和进度可以在 information_schema 的系统表中监测到。

简单的原理图如下:

Recipient 节点就是发起 Clone 操作的实例,推荐使用一个空实例;Donor 节点就是目标实例,是 Clone 操作的对象。完成 Clone 操作后,Recipient 节点就拥有 Donor 节点完整的 InnoDB 数据。从 8.0 开始,所有的 MySQL 系统表已经全部转换为 InnoDB 引擎,所以 Clone 操作也会把用户和授权信息完整的 Clone 到 Recipient 节点。

Clone 操作的流程如下:

  1. 清空 Recipient 节点的数据

  2. 复制 Donor 节点的数据文件

  3. 复制 Donor 节点的数据页

  4. 复制 Donor 节点的 redo log

  5. 同步 Donor 节点的数据文件

  6. 重启 Recipient 节点

  7. 在 Recipient 节点上完成 Crash recovery

使用 Clone 插件需要注意以下几点:

  1. Recipient 节点并不会 Clone Donor 节点的 MySQL 配置文件,因为 Recipient 节点一般会有不同的 IP 或者端口。但是涉及到一些存储相关的参数(例如 innodb_page_size)出现不一致的时候,Clone 插件会报错,这些参数可能会导致 Recipient 节点重启失败,因此要提前准备好 Recipient 节点的配置文件。

  2. Clone 插件复制的文件不包括 binlog,因为加入原有的 HA 结构并不需要 Donor 节点的 binlog。

  3. Clone 插件当前仅支持 InnoDB 引擎的表,MyISAM 和 CSV 等引擎的表在 Clone 之后是一个空表。Clone 插件最初设计的时候会支持 MySQL 的所有引擎,但是目前仅实现了 InnoDB 引擎的功能,并通过了测试。

  4. Clone 操作会阻塞 Donor 节点上的所有 DDL 操作。

  5. Clone 操作会清空 Recipient 节点的所有数据和 binlog,因此要特别注意是否要在 Clone 操作执行之前备份 Recipient 节点的数据。

  6. Clone 操作最好是挂到后台执行。

使用限制

  • MySQL 版本不低于 8.0.17

  • 到 8.0.17 版本为止,Clone 插件仅支持 InnoDB 引擎的表。

  • Donor 节点在 Clone 的过程中,IO 读取的吞吐量和 CPU 使用率会有非常明显的上涨。

使用场景

由于 Clone 插件会直接生成一份完整的一致性备份,因此在搭建新的从库,创建临时测试库,本地、远端的一致性备份等可以直接访问源实例的场景会非常适合 Clone 插件。同时 Clone 插件也能满足 TB 级 MySQL 实例的备份需求,而 xtrabackup 和 mysqlbackup 会受到 redo log rotate 的影响,绝大多数场景下无法对 TB 级的 MySQL 进行备份,dump,快照等方式则存在磁盘空间或者时间上的缺陷。

操作流程

安装 Clone 插件

在 MySQL Client 安装 Clone 插件:

INSTALL PLUGIN CLONE SONAME "mysql_clone.so"; 
CREATE USER clone_user IDENTIFIED BY "clone_password";
GRANT BACKUP_ADMIN ON *.* to clone_user;

授予查看 Clone 进度等相关信息的权限:

GRANT SELECT ON performance_schema.* TO clone_user;
GRANT EXECUTE ON *.* to clone_user;

登录到 Recipient 节点执行 Clone 命令

##登录到Recipient的mysql client端执行
SET GLOBAL clone_valid_donor_list = 'donor.host.com:3306';
##Clone 操作推荐在shell命令行下挂到后台执行
CLONE INSTANCE FROM clone_user@donor.host.com:3306 IDENTIFIED BY "clone_password";

查看 Clone 的信息

检查 Clone 的运行状态:
SELECT STATE, CAST(BEGIN_TIME AS DATETIME) as "START TIME",
CASE WHEN END_TIME IS NULL THEN
LPAD(sys.format_time(POWER(10,12) * (UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(BEGIN_TIME))), 10, ' ')
ELSE
LPAD(sys.format_time(POWER(10,12) * (UNIX_TIMESTAMP(END_TIME) - UNIX_TIMESTAMP(BEGIN_TIME))), 10, ' ')
END as DURATION
FROM performance_schema.clone_status;

结果展示:
+-------------+---------------------+------------+
| STATE       | START TIME          | DURATION   |
+-------------+---------------------+------------+
| In Progress | 2019-07-17 17:23:26 |     4.84 m |
+-------------+---------------------+------------+


检查 Clone 的操作进度:
SELECT STAGE, STATE, CAST(BEGIN_TIME AS TIME) as "START TIME",
  CASE WHEN END_TIME IS NULL THEN
  LPAD(sys.format_time(POWER(10,12) * (UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(BEGIN_TIME))), 10, ' ')
  ELSE
  LPAD(sys.format_time(POWER(10,12) * (UNIX_TIMESTAMP(END_TIME) - UNIX_TIMESTAMP(BEGIN_TIME))), 10, ' ')
  END as DURATION,
  LPAD(CONCAT(FORMAT(ROUND(ESTIMATE/1024/1024,0), 0), " MB"), 16, ' ') as "Estimate",
  CASE WHEN BEGIN_TIME IS NULL THEN LPAD('0%', 7, ' ')
  WHEN ESTIMATE > 0 THEN
  LPAD(CONCAT(CAST(ROUND(DATA*100/ESTIMATE, 0) AS BINARY), "%"), 7, ' ')
  WHEN END_TIME IS NULL THEN LPAD('0%', 7, ' ')
  ELSE LPAD('100%', 7, ' ') END as "Done(%)"
FROM performance_schema.clone_progress;

结果展示:

+-----------+-------------+------------+------------+-----------+---------+
| STAGE     | STATE       | START TIME | DURATION   | Estimate  | Done(%) |
+-----------+-------------+------------+------------+-----------+---------+
| DROP DATA | Completed   |   17:23:26 |  790.86 ms |      0 MB |    100% |
| FILE COPY | In Progress |   17:23:27 |    4.85 m  | 94,729 MB |     47% |
| PAGE COPY | Not Started |       NULL |       NULL |      0 MB |      0% |
| REDO COPY | Not Started |       NULL |       NULL |      0 MB |      0% |
| FILE SYNC | Not Started |       NULL |       NULL |      0 MB |      0% |
| RESTART   | Not Started |       NULL |       NULL |      0 MB |      0% |
| RECOVERY  | Not Started |       NULL |       NULL |      0 MB |      0% |
+-----------+-------------+------------+------------+-----------+---------+

等待 Recipient 节点完成 Clone

重启完成 Crash Recovery 之后,整个 Clone 操作就结束了。

Clone 插件的效率测试

测试环境

类型详情
品牌惠普 DL360 Gen9
CPUE5-2630 v4*2
内存DDR4 2400MHz 16GB*8
HDD希捷 1800G 2.5寸 SAS 10K 12Gb/s,2块盘,RAID 1
SSD英特尔 S3510 800G 2.5寸 SATA 0 6Gb/s,6块盘,RAID 10
数据集4张表,每张表10亿行数据,总共约1000GB
软件版本MySQL 8.0.17, sysbench 1.0.16

 

测试内容

测试主要对比的指标为时间,具体指从一个运行的实例上建立一个完整的镜像实例所花费的时间。测试的对比对象为 LVM 快照和 Xtrabackup 工具。

MySQL 实例的配置使用相同的参数配置,数据目录均使用 SSD 磁盘,备份目录均使用 SAS 磁盘,redo log 为 2G 大小,总共 4 个文件。本次对比中会记录操作的各个阶段的耗时,数据仅作为实际备份恢复耗时的参考(部分数据可作为 TB 级 MySQL 实例的 RTO 的参考)。

Xtrabackup 备份开启了压缩与流数据打包,备份的数据流存储到 SAS 盘,恢复过程中,整个解包和解压缩均在 SAS 盘完成。因此 Xtrabackup 的恢复备份流程总共包括以下几个步骤:打包压缩 -> 解包 -> 解压缩 -> 恢复备份 -> 本地 copy 数据文件 -> 成功启动,本次测试参考的流程为本地备份恢复。

快照完成的时间定为把完整的快照数据拷贝到 SAS 盘之后,本次测试不做实际快照备份的操作,仅以磁盘写入吞吐量的平均值来做粗略估计。快照备份的速度按照 SAS 盘的平均写入速度(200MB/s)来计算。快照恢复的速度按照 SAS 盘的平均读取速度(300MB/s)来计算。总共 1000GB 的数据量。

MySQL Clone 插件直接使用 Recipient 节点的数据目录(SSD 磁盘)为目标目录,Clone 插件的配置如下:

##设置为ON时,MySQL会忽略用户的设置,自动调整Clone的参数, 速度非常慢
##但是对服务器的CPU和IO资源几乎没有影响。
clone_autotune_concurrency = OFF 
##设置传输数据时可用的buffer大小,默认大小为4MB,增加这个参数允许Clone插件更充分的利用IO设备的性能,加快Clone操作的速度。
clone_buffer_size = 268435456
##设置Clone操作尝试获取相关锁的等待时间
clone_ddl_timeout = 300
##设置Clone操作是否开启压缩功能来进行数据传输,会减少网络带宽的消耗,增加CPU的消耗
clone_enable_compression = ON
##设置Clone操作使用的并发线程数
clone_max_concurrency = 24
##设置Clone操作可使用的最大数据传输吞吐量
clone_max_data_bandwidth = 0
##设置Clone操作可使用的最大网络带宽
clone_max_network_bandwidth = 0

测试中模拟的实际业务场景分为四个:

  • 无写入负载

  • 低写入负载

  • 中等写入负载

  • 标准读写负载

Xtrabackup 会因为 redo log 循环写入的原因导致备份失败,因此本次测试中不对高写入负载的场景进行测试,需要 Xtrabackup 支持 8.0.17 的 redo log archives 特性之后才能解决此类问题。

测试场景的几种负载由 sysbench 的脚本来进行模拟,对应的并发压力为:

  • 无写入负载(None)

    • 并发线程 = 0

  • 低写入负载(LOW,write_only)

    • 并发线程 = 4

  • 中等写入负载(MID,write_only)

    • 并发线程 = 8

  • 标准读写负载(RW,read_write)

    • 并发线程 = 16

    • CPU idle% 约 60%

结论

  • 如果从一个正常的 MySQL 实例上获取一个镜像实例或者例行备份,Clone 插件的效率远远高于(超过 100% 的速度提升)常规的备份恢复手段(mydumper,mysqlbackup,xtrabackup,快照等)。

  • Clone 插件会占用 donor 实例的资源,会比较明显的影响 SELECT 操作,对 DML 的影响较小。

  • 如果 MySQL 实例处于异常状态,无法访问,那么 Clone 插件就无法使用了,依旧需要使用常规的手段来进行灾难恢复。

耗时对比

实测过程中,Clone 插件对 donor 实例的主要压力在于 CPU,以及 IO 设备的读取性能上,对写入性能的影响相对比较小,即使达到中等量级的写入压力,Clone 的过程中也只达到了约 8% 的写入性能损失,但是在标准的读写负载场景下,性能损失达到了约 28%,主要是因为 CPU 的资源发生了争抢。

虽然在规划中没有加入高写入负载的测试,但是实际发现即使 sysbench 的写入并发设置为 1(写入 QPS 约 4000),Xtrabackup 依旧不能完成备份操作(由于 redo log 的循环写入),因此实际测试的结果中,Xtrabackup 仅有无负载的测试数据。

横向对比几种不同的备份恢复方案(所有图例的 None 字段代表负载):

从图中可以看到 Clone 插件花费的时间仅有快照的 40%,Xtrabackup 的 18%,这是因为 Clone 的过程几乎就是从远端实例把数据 rsync 到本地的 SSD 磁盘,整个过程没有 SAS 盘的参与。

纵向对比 Clone 插件在不同场景下的表现:

可以看到随着写入负载,或者是读写负载的增加,Clone 插件的耗时也会增长,增加的幅度最多达到了 50%,因此 Clone 操作最好在业务比较少的时候进行,或者是在其他空闲 MySQL 实例进行。

Xtrabackup 各阶段的耗时

Xtrabackup 备份恢复的整个流程的如下。

打包压缩(完成备份) -> 解包 -> 解压缩 -> 恢复备份 -> 本地 copy 数据文件 -> 成功启动(完成恢复)

本次测试中也分别记录了 Xtrabackup 各个阶段的耗时,参见下图:

恢复备份阶段没有耗时的原因是备份过程中没有进行数据变更,因此无需prepare/apply-log操作。从图中可以看到,绝大部分时间都花费在解压缩的部分,因为 SAS 盘的性能偏弱。

如果需要利用历史备份进行灾难恢复,TB 级的 MySQL 实例进行恢复的操作还需要花费约 300 分钟(去掉打包压缩的流程)。

往期精彩

 

 

MySQL 8.0 New Feature:NOWAIT and SKIP LOCK

 

Python 简洁编码之道

 

mycli,一款让你忘记 mysql-client 的命令行客户端神器

 

记一次 Python Popen 阻塞的问题

 

记一次 Gitlab Runner 卡顿的详情分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值