解读GaussDB(for MySQL)表级恢复,看线程数及分块分行策略如何提升恢复性能?
本文分享自华为云社区《【华为云MySQL技术专栏】GaussDB(for MySQL)表级恢复中mydumper、myloader的应用与性能优化》,作者:GaussDB 数据库。
背景介绍
表级时间点恢复技术为“误删表”场景提供了一种快速且精确的恢复方案。通过将指定时间点的数据恢复到临时实例,再把用户所需的表导出至本地,后重新导入至原集群,从而实现对误删表的快速恢复,保证业务运行的连续性。图1是表级恢复的整体流程。
图1 表级时间点恢复恢复整体流程
与MySQL官方提供的单线程mysqldump工具相比,mydumper和myloader支持多线程导入导出,不仅避免了字符集转换,还大幅提升恢复速度,同时也减少了备份过程中对数据库并发访问性能的影响。
本文将详细介绍mydumper与myloader在GaussDB(for MySQL)中的应用,通过分析其原理,结合具体的测试结果,深入探讨线程数及分块分行策略对数据恢复性能的影响。同时,通过调整相应的策略,提升表级恢复性能。
原理介绍
mydumper机制
mydumper导出数据时,由一个主线程和多个子线程完成。线程的关系如图2所示。
图2 mydumper执行流程及各线程关系
-
主线程的流程为:
1) 连接数据库;
2) FLUSH TABLES WITH READ LOCK 将脏页刷到磁盘并获得只读锁;
3) START TRANSACTION /!40108 WITH CONSISTENT SNAPSHOT / 开启事务并获取一致性快照;
4)SHOW MASTER STATUS 获得binlog信息;
5)创建子线程并连接数据库;
6)为子线程分配任务并push到队列中;
7)在子线程处理完所有非InnoDB表之后,UNLOCK TABLES;
-
子线程主要流程:
1)连接数据库;
2)SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ,设置隔离级别为RR;
3)START TRANSACTION /!40108 WITH CONSISTENT SNAPSHOT / 开启事务并获取一致性快照;
4)从队列中pop任务并执行。每个线程执行一个任务,包括导出表、schema、触发器等;
5)导出数据时,如果当前数据文件大小超过--chunk-filesize指定的大小,则写入新的数据文件;
6)在所有非InnoDB表的任务执行完之后,通知主线程;
myloader机制
myloader的执行过程与mydumper类似,主线程负责主逻辑,默认四个子线程执行具体任务(threads参数可指定子线程个数),线程的关系如图3所示。
图3 myloader执行流程及各线程关系
主线程负责导入库表结构,创建异步导入任务及结束任务,并放到阻塞队列。等待子线程执行完所有任务并退出后,接着导入其他对象。
对象的导入顺序与mydumper的导出顺序正好相反,先导入库表结构,然后是每个库表的具体数据,最后是存储过程、函数、事件、视图、触发器等。
mydumper按照分块导出的数据文件,在导入阶段便可以充分利用myloader的多线程优势,提升导入速度。每个线程处理一个分块文件,导入完后继续处理其他分块文件。每个线程执行时间接近,避免出现单个线程工作,其他线程空闲等待的现象,提高并发度。
一致性
对于备份恢复,数据的一致性至关重要。mydumper通过一致性快照实现了备份数据的一致性。主要包括以下几步:
1. 先通过SHOW PROCESSLIST,得到长查询,并逐一Kill。如果Kill失败,则终止dump,退出程序。
2. 主线程通过 "FLUSH TABLES WITH READ LOCK" 将脏页刷到磁盘,并获取一个全局只读锁,从而保证在锁释放前,子线程通过"SHOW MASTER STATUS",获取binlog位点信息时,能够得到一致性位点,从而保证备份数据的一致性。
3. 工作线程执行"START TRANSACTION WITH CONSISTENT SNAPSHOT" 开启事务并获取一致性快照。
4. 等所有非InnoDB表的工作线程任务执行完成,主线程会"UNLOCK TABLES",释放"FLUSH TABLES WITH READ LOCK",然后继续执行job queue中的作业。
通过InnoDB的MVCC功能,可以实现快照读,因此,只有在任务创建阶段才需要加锁,就可实现快照一致性。而非InnoDB表则需要在表导出任务完成前,一致对这些表加锁。
mydumper常用参数
mydumper常用参数如表1所示:
表1 mydumper常用参数及含义
mydumper常用参数 |
含义 |
-B, --database |
要备份的数据库,不指定则备份所有库(information_schema和performance_schema系统库除外) |
-T, --tables-list |
需要备份的表,名字用逗号隔开,可以用正则表达式 |
-x, --regex |
使用正则表达式匹配备份/不备份的对象 |
-o, --outputdir |
备份文件输出的目录,不指定默认为当前目录 |
-r, --rows |
将表按行分块时,指定多少行数据作为一个块,指定这个选项会关闭 --chunk-filesize |
-F, --chunk-filesize |
将表按指定大小分块,单位是MB |
-k, --no-locks |
不使用临时共享只读锁,这会造成备份数据不一致 |
--less-locking |
最小化对InnoDB表的加锁时间 |
-L, --logfile |
使用的日志文件名(mydumper所产生的日志), 默认使用标准输出 |
--use-savepoints |
使用savepoints来减少采集metadata所造成的锁时间,需要 SUPER 权限 |
--trx-consistency-only |
Transactional consistency only |
-h, --host |
连接的主机名 |
-u, --user |
备份所使用的用户 |
-p, --password |
密码 |
-P, --port |
端口 |
-S, --socket |
使用socket通信时的socket文件 |
-t, --threads |
开启的备份线程数,默认是4 |
-C, --compress-protocol |
压缩与mysql通信的数据 |
-v, --verbose |
输出信息模式, 0 = silent, 1 = errors, 2 = warnings, 3 = info, 默认为 2 |
对性能提升帮助较大的参数:
--rows和--chunk-filesize 可将表分成多块。在恢复时,利用多线程并发恢复数据,可以提高速度。
--trx-consistency-only 只导出已提交的事务数据。在导出大量数据的情况下,可以减少导出的数据量和时间。
--threads 并发线程数。增加线程数,可提升恢复性能。但需注意不要影响到正常业务。
myloader常用参数
myloader常用参数如表2所示:
表2 myloader常用参数及含义
myloader常用参数 |
含义 |
-d, --directory |
备份文件的文件夹 |
-B, --database |
需要还原到哪个数据库(目标数据库) |
-h, --host |
主机 |
-u, --user |
还原的用户 |
-p, --password |
密码 |
-P, --port |
端口 |
-s, --source-db |
需要还原哪个数据库(源数据库) |
-S, --socket |
socket文件 |
-t, --threads |
还原所使用的线程数,默认是4 |
-v, --verbose |
输出模式, 0 = silent, 1 = errors, 2 = warnings, 3 = info, 默认为2 |
性能对比
本章节使用 GaussDB(for MySQL) 8U64G规格的实例,对于不同数据模型的表,使用不同的参数,对比导入导出性能。
首先,对比窄表的导入导出性能。表的规格为4列,4000万行,数据量大小8G。图4为窄表4线程、8线程mydumper、myloader不同参数的性能对比结果。
图4:窄表4线程、8线程mydumper、myloader性能对比
分析图表数据,我们可以得出以下结论:
(1)通过增加线程数,无论是导出还是导入,性能均有提升。
(2)导出阶段:分块导出的速度与不分块/不分行相比,并没有显著差异;而分行导出的速度大约是分块导出速度的两倍。
(3)导入阶段:使用分块和分行的方法相比不分块/不分行,导入速度提升了3到5倍。在处理常见的大表误删除场景时,如果不采用分块或分行,导入过程将仅限于单线程恢复。通过分块或分行,可以将单一大表分割成多个小文件,利用多线程导入,显著加快恢复速度。
(4)对比导入阶段分块与分行两种策略,分行导入的速度大约是分块导入速度的90%。
就整个数据恢复流程而言,导出阶段耗时远低于导入阶段。例如,对于一个10GB的表,导出阶段大约需要20至30秒,而导入阶段则需要近10分钟。尽管分行导出的性能是分块导出的两倍,但分行导入的性能却只有分块导入的90%。因此,从整体恢复效率来看,分块的性能要优于分行。
接下来对比宽表的导入导出性能,表结构为17列,30万行,数据量大小9.45G。图5为宽表4线程、8线程mydumper、myloader的性能对比结果。
图5:宽表4线程、8线程mydumper、myloader性能对比
从图中可以看到,增加线程数后,导出导入速度也均有上升。
对比分块分行策略:
(1)导出阶段,分块与分行的导出速度基本持平。
(2)导入阶段,分块导入速度显著高于分行。这是因为分块策略将宽表分割成了更多的文件块,从而在导入时能够实现更高的并行度。
因此对于宽表来说,分块的恢复性能也要高于分行。
综上,选用--chunk-filesize参数将表按块分割为多个文件,并提高导入导出所用线程数,可以显著提高恢复速度。
性能优化
根据上文的性能对比结果,GaussDB(for MySQL) 表级时间点恢复在导入导出阶段,分别做了如下性能优化:
在临时实例上使用mydumper将数据导出到本地时,由于临时实例并无业务,因此可以充分利用多线程提高性能。默认使用CPU * 2个线程数,加快数据导出速度,减少原实例加锁时间,减轻对业务影响。同时导出阶段使用--chunk-filesize=100M,将表数据进行分块,从而使得无论是宽表还是窄表,都能在导入阶段充分利用多线程进行恢复。
此外,还可避免在恢复单张大表时,出现仅能有一个工作线程执行导入,其他线程空闲,导致恢复慢的问题。
通过myloader将数据导入回原实例时,会对原实例的业务存在一定程度的影响,因此要慎重选择所用线程。GaussDB(for MySQL) 默认使用4线程进行导入操作,同时也给客户提供一个选择,可以使用更多的线程来进行紧急恢复。客户选择紧急恢复时,可选使用CPU*2个线程执行导入操作,此时恢复速度明显加快,但会提高原实例的CPU利用率,且内存使用率也有轻微上涨。这种情况需由客户根据紧急程度自行选择。
经过上述优化,GaussDB(for MySQL) 4U8G实例,恢复一张10T大小的数据表时,恢复时间缩短为原来的1/4,恢复速度大幅提升。
总结
mydumper、myloader在GaussDB(for MySQL)表级时间点恢复过程中负责数据导出导入过程。通过在导出阶段对表数据进行分块,并调整导入阶段线程数,使得恢复过程充分利用多线程优势,提升表级恢复性能,大幅改善用户体验。