环境介绍

Server OS: CentOS 8.2 x64

MariaDB: 10.5.x (主 、从均运行在 Docker / Podman 容器里)

操作目标: 配置一台全新的从库

假设主库IP是 dbserver1

从库IP是 dbserver2

使用 MariaBackup 做全量或增量同步,更加方便。

安装MariaBackup

首先我们需要在 主 / 从 服务器上分别安装 MariaBackup.

这里比较简单,参考 官方文档就好了。

# CentOS 8 / Stream
sudo dnf install MariaDB-backup

# Debian / Ubuntu
sudo apt-get install mariadb-backup

准备全量同步备份文件

这里假设,master 机已经开启 binlog 和 master 模式。我们需要给整个库做一个备份。

由于我们是针对整个 master 服务器备份,因此这里没有指定 --databases=数据名 参数。

mariabackup --verbose --rsync --backup \
   --target-dir=/home/user001/backup/mysql/ \
   --host=dbserver1 --port=3307 --protocol=tcp \
   --user=root --password=root密码在这

这里一定会出错。 因为我们的 MariaDB 跑在容器里,host 机并不存在 /var/lib/mysql 目录。

解决办法是:

Create a symlink on your host so that Mariabackup thinks/var/lib/mysql exists. Then delete the symlink once you’re done.

ln -s /home/user007/data/mysql /var/lib/mysql

然后再运行命令,就OK了。如果备份成功,最后一部分的日志输出大概如下:

[00] 2021-03-04 05:54:58 Writing xtrabackup_binlog_info
[00] 2021-03-04 05:54:58         ...done
[00] 2021-03-04 05:54:58 Executing FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS...
[00] 2021-03-04 05:54:58 mariabackup: The latest check point (for incremental): '2611384153422'
mariabackup: Stopping log copying thread.[00] 2021-03-04 05:54:58 >> log scanned up to (2611437102980)

[00] 2021-03-04 05:54:58 >> log scanned up to (2611437103220)
[00] 2021-03-04 05:54:58 Executing BACKUP STAGE END
[00] 2021-03-04 05:54:58 All tables unlocked
[00] 2021-03-04 05:54:58 Backup created in directory '/home/user007/backup/mysql/'
[00] 2021-03-04 05:54:58 MySQL binlog position: filename 'master16-bin.003097', position '15736398', GTID of the last change '0-101-9088409195'
[00] 2021-03-04 05:54:58 Writing backup-my.cnf
[00] 2021-03-04 05:54:58         ...done
[00] 2021-03-04 05:54:58 Writing xtrabackup_info
[00] 2021-03-04 05:54:58         ...done
[00] 2021-03-04 05:54:58 Redo log (from LSN 2611376635865 to 2611437103220) was copied.
[00] 2021-03-04 05:54:59 completed OK!

这样还没完,因为现在copy出来的备份文件,不能直接用于 MariaDB 恢复备份。

原因在 这里有解释:

Prepares an existing backup to restore to the MariaDB Server.

Files that Mariabackup generates during --backup operations in the target directory are not ready for use on the Server. Before you can restore the data to MariaDB, you first need to prepare the backup.

In the case of full backups, the files are not point in time consistent, since they were taken at different times. If you try to restore the database without first preparing the data, InnoDB rejects the new data as corrupt. Running Mariabackup with the --prepare command readies the data so you can restore it to MariaDB Server. When working with incremental backups, you need to use the --prepare command and the --incremental-dir option to update the base backup with the deltas from an incremental backup.

因此,对于增量备份的 prepare 操作,其实还需要加 --incremental-dir 选项。

mariabackup --prepare --verbose --rsync --target-dir=/home/user007/backup/mysql/
......
2021-03-04  6:02:53 0 [Note] InnoDB: apply 2611435179079: [page id: space=129, page number=2125]
2021-03-04  6:02:53 0 [Note] InnoDB: apply 2611436322402: [page id: space=129, page number=2177]
2021-03-04  6:02:53 0 [Note] InnoDB: apply 2611436322402: [page id: space=129, page number=2177]
2021-03-04  6:02:53 0 [Note] InnoDB: apply 2611436391525: [page id: space=129, page number=2177]
2021-03-04  6:02:53 0 [Note] InnoDB: apply 2611436391525: [page id: space=129, page number=2177]
2021-03-04  6:02:53 0 [Note] InnoDB: apply 2611435179079: [page id: space=129, page number=2125]
2021-03-04  6:02:53 0 [Note] InnoDB: apply 2611435368189: [page id: space=129, page number=2125]
2021-03-04  6:02:53 0 [Note] InnoDB: apply 2611435368189: [page id: space=129, page number=2125]
[00] 2021-03-04 06:02:53 Last binlog file , position 0
[00] 2021-03-04 06:02:53 completed OK!

复制备份文件到从库所在主机

# 首先 ssh 到从库机器, 准备好存放备份的目录
mkdir -p /home/user007/backup/mysql

# 其实我这里的机器并不是22端口
# 没错,当然是走内网地址
rsync -e 'ssh -p 2222' -avP /home/user007/backup/mysql [email protected]:/home/user007/backup/

准备好从库同步需要的账号

CREATE USER 'replication_user'@'dbserver2' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.*  TO 'replication_user'@'dbserver2';

在新的从库上恢复备份

首先 systemctl stop ctr-site007-mariadb 停止掉 MariaDB 服务器, 准备恢复备份。

为什么不是 docker stop 或 podman stop ? 因为这个容器是用 podman generate 生成了 systemd unit 启动服务的,如果从 podman 停止, systemd 又会自动重启容器。所以,下面看到 systemctl 操作 ctr 开头的 service 时,请自行替换成你自己的命令。

恢复其实就是 copy 准备好的备份文件到 MariaDB datadir (也就是/var/lib/mysql)。

由于从为这边我们也是使用容器跑的 MariaDB, 因此同样要给做软链:

ln -s /home/user007/data/mysql /var/lib/mysql

开始恢复备份文件:

mariabackup --copy-back --verbose --rsync \
   --target-dir=/home/user007/backup/mysql/
   
   
# 相关日志输出
[01] 2021-03-04 06:27:14 Copying ./xtrabackup_info to /var/lib/mysql/xtrabackup_info
[01] 2021-03-04 06:27:14         ...done
[00] 2021-03-04 06:27:14 completed OK!

# 检查下恢复的文件
[[email protected] mysql]# ls /var/lib/mysql/
aria_log.00000001  aria_log_control  ib_buffer_pool  ibdata1  mysql  performance_schema  site007_prod  xtrabackup_info

注意,执行这个操作的前提是 /var/lib/mysql 目录是空的。也就是如果是我们新跑的服务器,里面没数据的,我们也要清空这个目录, 不然 MariaBackup 不会执行恢复备份的操作 (会有错误: Original data directory /var/lib/mysql is not empty!)。

根据情况的不同,可能还要恢复一下文件权限,比如 chown -R mysql:mysql /var/lib/mysql/

查看一下 xtrabackup_binlog_info 这个文件的内容,这里第三个值 0-101-9090487761 就是 GTID, copy出来先找个地方暂存一下,等会要用:

[[email protected] mysql]# cat xtrabackup_binlog_info
master16-bin.003097     775477086       0-101-9090487761

开启从库同步

启动从服务器: systemctl start ctr-site007-mariadb

登录从库mysql -u root -h dbserver2 -P 3307 -p

STOP SLAVE;
RESET SLAVE;

SET GLOBAL gtid_slave_pos = "0-101-9090487761";


CHANGE MASTER TO
  MASTER_HOST='dbserver1',
  MASTER_USER='replication_user',
  MASTER_PASSWORD='主库同步密码',
  MASTER_PORT=3307,
  MASTER_CONNECT_RETRY=10,
  MASTER_USE_GTID=slave_pos;
  
START SLAVE;

查看一下同步状态是否正常:

比较需要关注的几个选项:

Slave_IO_State: Queueing master event to the relay log

Master_Log_File: master16-bin.003097 Read_Master_Log_Pos: 842451034 Relay_Log_File: master94-relay-bin.000002 Relay_Log_Pos: 649307

Slave_IO_Running: Yes Slave_SQL_Running: Yes

Using_Gtid: Slave_Pos Gtid_IO_Pos: 0-101-9090669366

Seconds_Behind_Master: 301

Seconds_Behind_Master 是最重要的一个参数。300多秒的延时对于一般的同步来说算是问题比较大了,但是这里由于我们是刚刚启动复制,才开始第一次同步,因此这个情况是正常的。过一会这个值就会正常了(正常来说应该是接近0)。

MariaDB [(none)]> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
                Slave_IO_State: Queueing master event to the relay log
                   Master_Host: dbserver1
                   Master_User: replication_user
                   Master_Port: 3307
                 Connect_Retry: 10
               Master_Log_File: master16-bin.003097
           Read_Master_Log_Pos: 842451034
                Relay_Log_File: master94-relay-bin.000002
                 Relay_Log_Pos: 649307
         Relay_Master_Log_File: master16-bin.003097
              Slave_IO_Running: Yes
             Slave_SQL_Running: Yes
               Replicate_Do_DB: site007_prod
           Replicate_Ignore_DB:
            Replicate_Do_Table:
        Replicate_Ignore_Table:
       Replicate_Wild_Do_Table:
   Replicate_Wild_Ignore_Table:
                    Last_Errno: 0
                    Last_Error:
                  Skip_Counter: 0
           Exec_Master_Log_Pos: 776125657
               Relay_Log_Space: 66974795
               Until_Condition: None
                Until_Log_File:
                 Until_Log_Pos: 0
            Master_SSL_Allowed: No
            Master_SSL_CA_File:
            Master_SSL_CA_Path:
               Master_SSL_Cert:
             Master_SSL_Cipher:
                Master_SSL_Key:
         Seconds_Behind_Master: 301
 Master_SSL_Verify_Server_Cert: No
                 Last_IO_Errno: 0
                 Last_IO_Error:
                Last_SQL_Errno: 0
                Last_SQL_Error:
   Replicate_Ignore_Server_Ids:
              Master_Server_Id: 101
                Master_SSL_Crl:
            Master_SSL_Crlpath:
                    Using_Gtid: Slave_Pos
                   Gtid_IO_Pos: 0-101-9090669366
       Replicate_Do_Domain_Ids:
   Replicate_Ignore_Domain_Ids:
                 Parallel_Mode: optimistic
                     SQL_Delay: 0
           SQL_Remaining_Delay: NULL
       Slave_SQL_Running_State: init for update
              Slave_DDL_Groups: 0
Slave_Non_Transactional_Groups: 0
    Slave_Transactional_Groups: 1765
1 row in set (0.000 sec)

Misc

ERROR 1198

ERROR 1198 (HY000): This operation cannot be performed as you have a running slave ‘'; run STOP SLAVE '’ first

这个错误一般出现在 CHANGE MASTER TO … 配置slave时。原因是前面的 slave 配置还在,STOP SLAVE 然后 RESET SLAVE 再执行 CHANGE MASTER TO … 就好了。

ERROR 1932

#1932 - Table ‘site007_prod.users’ doesn’t exist in engine

Copy the ib_logfileXX and ibdata file from old mysql/data folder to the new mysql data folder and it will fix the issue

这个主要是没有使用 MariaBackup 恢复备份,而是采用 rsync 手动 copy 文件到 MairaDB datadir 下的。

并且,少复制了 ib_logfileXX 和 ibdata 等文件。所以,还是用 MariaBackup 恢复的好,除非你非常了解 MariaBackup 的工作原理。

如何安全地清理 binlog

这里主要是讲主库的binlog (从库一般不再作为别人的主,我们不开)

命令格式:

PURGE { BINARY | MASTER } LOGS { TO 'log_name' | BEFORE datetime_expr }

The PURGE BINARY LOGS statement deletes all the binary log files listed in the log index file prior to the specified log file name or date. BINARY and MASTER are synonyms.

Deleted log files also are removed from the list recorded in the index file, so that the given log file becomes the first in the list. The datetime expression is in the format ‘YYYY-MM-DD hh:mm:ss’.

注意这里是 prior to (在…之前), 首先我们要检查从库最慢的那个节点(假设有多个从库的话)的同步情况,比如Slave status中的 Master_Log_File = master16-bin.003103

Relay_Log_File master94-relay-bin.000015

Relay_Master_Log_File master16-bin.003103

表示当前 slave 正在 replicate master 那边的 master16-bin.003103, 因此, master 那边可以清空 在 master16-bin.003103 之前的所有 binlog 文件:

PURGE BINARY LOGS TO 'master16-bin.003103'

如何使用 podman 生成 systemd unit 文件

cd /etc/systemd/system/

podman generate systemd --new -t 10 \
--container-prefix ctr -f -n site007-mariadb

# 启用并启动服务
systemctl enable --now ctr-site007-mariadb

refs

本文主要参考: https://mariadb.com/kb/en/setting-up-a-replication-slave-with-mariabackup/

expire_logs_days 配置: https://mariadb.com/kb/en/replication-and-binary-log-system-variables/#expire_logs_days

显示从服务器: https://mariadb.com/kb/en/show-replica-hosts/

关于使用 GTID: https://mariadb.com/kb/en/gtid/#using-global-transaction-ids

MySQL 复制常见误区: https://severalnines.com/blog/top-mistakes-avoid-mysql-replication

https://mariadb.com/kb/en/setting-up-replication/

https://alibaba-cloud.medium.com/how-to-setup-mariadb-master-and-slave-replication-on-ubuntu-16-04-850c155c5481

MariaBackup 相关文档:

安装: https://mariadb.com/kb/en/mariabackup-overview/#installing-with-a-package-manager

MariaBackup 选项: https://mariadb.com/kb/en/mariabackup-options/

MariaBackup 概览: https://mariadb.com/kb/en/mariabackup-overview/

MariaBackup 工作原理之初始化: https://mariadb.com/kb/en/how-mariabackup-works/#initialization-phase

解决 MariaBackup 针对 MariaDB 容器备份时遇到的问题 https://dba.stackexchange.com/questions/234586/mariabackup-of-database-running-in-docker

如何安全地清理binlog:

https://minervadb.com/index.php/2018/05/14/purging-binary-logs-from-mysql-master-safely/

https://mariadb.com/kb/en/purge-binary-logs/

innodb 如何只恢复特定的表:

首先使用 MariaBackup 在 prepare 的时候 增加 --export 参数:

https://mariadb.com/kb/en/restoring-individual-tables-and-partitions-with-mariabackup/

然后 Importing Transportable Tablespaces for Non-partitioned Tables https://mariadb.com/kb/en/innodb-file-per-table-tablespaces/#importing-transportable-tablespaces-for-non-partitioned-tables

部分备份:

https://mariadb.com/kb/en/partial-backup-and-restore-with-mariabackup/

进一步了解Mariabackup:

Mariabackup备份的文件: https://mariadb.com/kb/en/files-backed-up-by-mariabackup/

Mariabackup创建的文件: https://mariadb.com/kb/en/files-created-by-mariabackup/