GreatSQL 异步复制及搭建
一、简介
复制就是将一个数据库数据复制到一个或多个数据库上,复制的过程是异步的,其工作原理是通过binlog(二进制日志)记录事务变更然后传送到从库并重放事务,保持数据一致
二、复制过程
1-1 复制过程图
2.1 binlog日志
GreatSQL 复制是基于 binlog 日志来实现复制的
2.1.1 二进制日志输出格式比较
二进制日志格式 | 记录内容 | 主从同步使用场景 | 大小 | 性能 |
---|---|---|---|---|
statement | 基于SQL | 如果使用了一些不确定性的函数和自定义函数,函数返回的数据在主从库上不一致。 比如 now(),last_insert_id()等。 | 较小 | 最好 |
row | 基于数据行 | 默认使用基于row | 较大 | 最差 |
mixed | 混合格式 | 默认使用statement。 当statement无法正确复制时,采用rows | 居中 |
2.2 复制过程
主库把数据更新事件写入二进制日志。
从库的IO线程向主库请求二进制日志事件。
主库的binlog dump线程向从库IO线程发送二进制日志事件。
从库的IO线程将二进制日志事件写入中继日志。
从库的SQL线程应用中继日志中的事件。
2.3 二阶段提交
GreatSQL 包含两种记录数据变更的日志,redolog、binlog为了保证两个日志的对数据变更事件达到一致性,GreatSQL 内部使用XA来实现,其核心是两阶段提交。
2.3.1 事务提交流程
主从复制中,从库是通过binlog日志重放来实现复制。如何保证主从的数据一致性,就需要考虑如下两方面
binlog记录的事务一定不比redolog少。
binlog与redolog记录的事务的顺序性是一致的。
在两阶段协议中一般分为事务管理器(协调者)和若干事务执行器(参与者)两种角色。GreatSQL 内部实现的两阶段提交中,二进制日志充当了协调组角色,由它来通知InnoDB执行准备、提交和回滚。从实现角度分析,事务提交有准备阶段和提交阶段组成。两阶段提交流程如下图:
1-2事务提交流程
1、事务发起commit请求。
2、调用binlog-hton和innobase-hton的prepare方法完成第一阶段。binlog-hton的方法什么也没有做,innodb的prepare持有prepare_commit_mutex锁,将重做日志刷盘,并将事务状态设置为prepared。
3、如果事务涉及的所有存储引擎的prepare都执行成功,则调用TC_LOG_BIN:log_xid将事务写入到二进制日志。
4、最后,调用存储引擎的commit完成事务的提交,向重做日志写入commit标记,释放prepare_commit_mutex,并将事务设置为trx_not_started状态。
2.3.2 InnoDB恢复
数据库在奔溃恢复时,不同状态的事务,会进行不同的处理。
trx_commit_in_memory的事务,清除回滚段后,将事务设置为trx_commit_not_started。
trx_commit_active的事务,直接回滚。
trx_not_started的事务,表示事务已提交过,跳过。
trx_commit_prepared的事务,要根据二进制日志来决定事务是否提交,暂时跳过。
在数据库发生奔溃时,数据库根据重做日志进行数据恢复,逐个检查重做日志的每个事务状态。根据1-2的流程,如果已经进行到trx_not_started阶段,也就是存储引擎commit阶段,那么说明重做日志和二进制日志是一致的,正常根据重做日志的内容进行恢复即可。如果事务状态为trx_active,没有写入到二进制日志,就直接回滚。如果事务状态为trx_commit_prepared,要分两种情况:先检查二进制日志是否写入成功,如果没有写入成功,直接回滚,如果写入成功了,那就进行最后一步,调用存储引擎commit,更改事务状态为trx_not_started,也就是真正提交的状态,可以用作数据恢复。
由此可以,GreatSQL是以二进制日志的写入与否作为事务提交成功与否的标志。通过这种方式让InnoDB重做日志和GreatSQL服务器的二进制日志中的事务状态保持一致。两阶段提交很好的保持了数据的一致性和顺序性。
GreatSQL通过innodb_support_xa系统变量来控制innodb是否支持XA事务的两阶段提交,默认是支持。在GreatSQL8中已移除该变量,表示数据库内部一直启用innodb对XA事务两阶段提交的支持。
2.3.3 日志刷盘频率
两阶段提交只是从流程上保证了日志的一致性,如果日志在写盘期间,数据库发生了崩溃或者服务器宕机,存在日志数据丢失的风险。为了规避此类风险,我们需要配置正确的日志刷盘频率。
重做日志的刷盘频率由innodb_flush_log_at_commit_trx参数控制。二进制日志的刷盘频率由sync_binlog参数控制。
重做日志刷盘方式
innodb_flush_log_at_trx_commit参数为1时,表示只要事务提交,立即将重做日志刷盘。
innodb_flush_log_at_trx_commit参数为2时,表示每秒将重做日志缓冲区的内容写入到操作系统页面缓冲,至于什么时候刷盘,由操作系统控制。
innodb_flush_log_at_trx_commit参数为0时,表示每秒将重做日志刷盘。
二进制日志刷盘方式
- sync_binlog参数为1时,表示只要事务提交,立即将二进制日志刷盘。
- sync_binlog参数为N(N>1)时,表示N个二进制日志组提交后,将二进制日志刷盘。
- sync_binlog参数为0时,表示二进制日志的刷盘由操作系统控制。
三、异步复制参数
3.1 源库和复制库常用参数说明
参数名 | 参数值 | 参数级别 | 作用 |
---|---|---|---|
binlog_do_db | schema | 配置文件修改 | 二进制日志只记录该参数指定的库名产生的更新事件 |
binlog_ignore_db | schema | 配置文件修改 | 二进制日志不记录该参数指定的库名产出的更新事件。 |
log_slave_update | ON/OFF | 系统级别 | 复制库应用中继日志中的事件是否写入二进制日志。用于复制级联,建议主库和从库都开启该参数。 |
replicate_do_db | schema | 配置文件修改 | 只重放中继日志中该参数指定的库名的事件。 |
replicate_ignore_db | schema | 配置文件修改 | 不重放中继日志中该参数指定的库名的事件。 |
replicate_wild_do_table | schema.% | 配置文件修改 | 只重放中继日志中该参数指定的库名的事件。 建议使用这种方式进行复制过滤。 |
replicate_wild_ignore_table | schema.% | 配置文件修改 | 不重放中继日志中该参数指定的库名的事件。 建议使用这种方式进行复制过滤。 |
skip_slave_start | ON/OFF | 只读参数 | 复制库启动时,是否启动复制线程。 建议不开启该参数。 |
sql_log_bin | ON/OFF | 会话级别 | 会话端执行的更新事件是否写入二进制日志。 |
slave_parallel_workers | 0-N | 全局级别 | 是否开启多线程复制, 并指定对少个线程并发应用复制事务。 参数值为0时,表示不开启。 |
slave_parallel_type | logical_lock database | 全局级别 | 在源库二进制日志事件是以组的方式进行提交。 为了保证在从库并发的执行事务,有两种方式进行控制。 基于logical_lock的方式: 通过跟踪某一个时间点内的事务与事务之间的关系, 来决定尽可能的并发执行事务。 基于database的方式: 在不同数据库之间值的事务可以并发执行。 |
slave_preserve_commit_order | ON/OFF | 全局级别 | 在多线程复制库上, 事务的执行和提交顺序与中继日志中记录的顺序一致。 |
slave_pending_job_size_max | integer | 全局级别 | 在多线程复制库上, 应用队列用于保留暂未应用的事务的最大可用内存。 该参数的值不能小于源库上max_allowed_packet参数指定的值, 不然在执行大事务事会报错。 |
slave_max_allowed_packet | integer | 全局级别 | 指定复制库上的sql线程和io线程能够处理的最大数据包, 该参数不能小于源库上max_allowed_packet指定的参数值。 |
四、异步复制部署
4.1 环境准备
使用两台服务器来部署一主一从的异步复制。
Master:192.168.135.183
Slave:192.168.135.142
4.2 部署数据库
安装GreatSQL数据库,见GreatSQL官方文档 https://greatsql.cn/docs/8.0.32-25/4-install-guide/3-install-with-tarball.html
4.3 配置复制
4.3.1 空库
初始安装的数据库没有任何应用数据。可以直接配置就能使用。
Master上的操作:
greatsql> reset master;
Slave上的操作:
greatsql> reset master; greatsql> reset slave all; greatsql> change master to master_host="192.168.135.183",master_port=3305,master_user='repl',ma ster_password='!QAZ2wsx',master_auto_position=1; greatsql> start slave; greatsql> show slave status\G
4.3.2 脱机
如果源数据库上存在应用数据,允许在一个接收的脱机时间窗口进行复制,那么我们可以直接把源库的数据文件复制到从库,再启动从库进行主从配置。
Master上的操作
创建用于复制的账户,并授予相应的权限。
停止源数据库服务。
复制源数据库上的数据文件到从库。
启动源数据库服务。
Slave上的操作
删除从库数据目录中的auto.cnf,从库在启动时会自动生成唯一的server_uuid。检查数据库配置文件确保server_id是唯一的。
启动从数据库服务。
配置主从同步关系,并启动主从同步线程。
4.3.3 联机
大多数情况下,复制被要求在不影响线上业务的情况下联机创建,而且还要求对线上源库影响越小越好。在联机情况下,我们通常采用mysqldump和xtrabackup工具复制源库到从库,配置主从同步。
mysqldump联机复制过程
Master上的操作
1.创建用于复制的账户
greatsql> create user repl@'%' identified with mysql_native_password by '!QAZ2wsx';
greatsql> grant replication client,replication slave on *.* to repl@'%';
greatsql> flush privileges;
- Slave上的操作
- 创建源库信息
greatsql> change master to master_host='192.168.135.183',master_port=3305,master_user='repl',master_password='!QAZ2wsx';
- 使用mysqldump导入数据到从库,建立复制
greatsql> mysqldump --single-transaction --all-databases --master-data=1 --host=192.168.135.183 --port=3306 --user=root --password=!QAZ2wsx --default-character-set=utf8mb4 --apply-slave-statements|mysql -uroot -p!QAZ2wsx -h192.168.135.142 -P3305
重要参数说明:
--single-transaction参数对innodb表执行非锁定导出。此选项将事务隔离模式设置为repeatable read,并在转储数据之前向服务器发送start transaction sql语句。它仅适用于innodb等事务表,因为它会在发出start transaction时转储数据库一致状态,而不会阻塞任何应用程序。
--master-data参数为1时会导致转储输出包含类型change master to master_log_file='binlog.000004',master_log_pos=1230;的sql语句,该语句指示主库的二进制日志坐标(文件和偏移量)。如果选项值为2,则chang master to语句将被注释,仅提供信息,不会执行。如果未指定选项值,则默认值为1。
--apply-slave-statements参数会在change master to之前添加stop slave语句,被宰输出的结尾处添加start slave语句,用于开启复制。
--deafult-character-set参数指定默认字符集,GreatSQL的默认字符集为utf8mb4。
xtrabackup联机复制过程
Master上的操作
- 在源库上创建全量备份
$ xtrabackup --defaults-file=/mysql/conf/node13306.cnf --host='192.168.135.183' --port=3306 --user='root' --password='!QAZ2wsx' --backup --target-dir=/mysql/backup 2>backup.log
- 准备数据信息备份
$ xtrabackup --prepare --target-dir=/mysql/backup 2>restore.log
--prepare
参数准备备份数据进行恢复。数据文件在准备之前是不一致的,因为它们是在备份程序运行的不同时间复制的,并且在这个时间段,有些数据已经发生变更。其原理是使用redolog进行后滚,使用undolog进行前滚。
Slave上的操作
- 关闭从库,在从库上删除数据库初始化生成的数据库文件。
$ rm -rf /mysql/dbdata/data/*;rm -rf /mysql/dbdata/log/*
- 从源库上拉取备份恢复后的数据文件。
$ scp -r [root@192.168.135.183:/mysql/backup/*](mailto:root@192.168.1.111:/mysql/backup/*) /mysql/dbdata/data $ mv binlog.* ib_logfile* undo_00* ../log/ $ chown -R mysql:mysql /mysql/dbdata
- 启动从数据库服务,并配置主从同步。
greatsql> service mysql start greatsql> change master to master_host="192.168.135.183",master_port=3305,master_user='repl',ma ster_password='!QAZ2wsx',master_auto_position=1;
五、异步复制常见问题说明
5.1 show slave 重点指标说明
我们在从库执行show slave status来观察主从同步的状态。
master_log_file:I/O线程正在读取的master binlog。
read_master_log_pos:I/O线程已经读取到master binlog的位置。
relay_log_file:SQL线程正在读取和执行的relay log。
relay_log_pos:SQL线程已经读取和执行relay log的位置。
relay_master_log_file:SQL线程最近执行的操作对应的是哪个master binlog。
relay_master_log_pos:SQL线程最新执行的操作对应的是master binlog的位置。
retrieved_gtid_set:接收到的gtid集合。
executed_gtid_set:已执行的gtid集合。
seconds_behind_master:SQL线程比IO线程慢多少。在网络正常的情况下,可以表示从库比源库慢多少。
5.2 常见错误
主从异步复制中,存在数据库丢失的可能性,导致主从数据不一致。如果数据量比较少的情况下,我们在从库执行相关的操作补齐数据后,对改事务进行跳过。
源库删除一行记录,而slave上找不到改行数据进行删除操作。错误代码为1032。
源库插入一行数据,而slave上表示改行数据早已存在,导致重复冲突。错误代码为1062。
源库上更新一行数据,而slave上表示找不到改行数据执行更新操作,错误代码为1032。
5.3 主从数据校验
可以使用GreatSQL社区的gt-checksum工具对主从数据进行一致性校验,或者使用官方工具checksum进行数据校验。checksum工具对数据库校验时,会给表上读锁,这一点在使用时需要注意。
gt-checksum工具详见:https://gitee.com/GreatSQL/gt-checksum
Enjoy GreatSQL :)
关于 GreatSQL
GreatSQL是适用于金融级应用的国内自主开源数据库,具备高性能、高可靠、高易用性、高安全等多个核心特性,可以作为MySQL或Percona Server的可选替换,用于线上生产环境,且完全免费并兼容MySQL或Percona Server。
相关链接: GreatSQL社区 Gitee GitHub Bilibili
GreatSQL社区:
社区有奖建议反馈: https://greatsql.cn/thread-54-1-1.html
社区博客有奖征稿详情: https://greatsql.cn/thread-100-1-1.html
(对文章有疑问或者有独到见解都可以去社区官网提出或分享哦~)
技术交流群:
微信&QQ群:
QQ群:533341697
微信群:添加GreatSQL社区助手(微信号:wanlidbc
)好友,待社区助手拉您进群。