如何在 Ubuntu 16.04 上使用 Percona XtraBackup 配置 MySQL 备份

介绍

数据库通常会存储您基础设施中最有价值的信息,因此,在发生事故或硬件故障时,要有可靠的备份,以防止数据丢失。

Percona XtraBackup 备份工具提供了在系统运行时执行 MySQL 数据的备份的方法,他们通过在文件系统级别复制数据文件,然后执行故障恢复来实现数据集内部的一致性。

在本指南中,我们将创建一个系统,在Ubuntu 16.04服务器上自动备份MySQL数据,我们将使用Cron和Percona工具在一组脚本中创建定期的安全备份,我们可以在出现问题时使用恢复。

前提条件

要完成此指南,您将需要一个 Ubuntu 16.04 服务器,具有非根 sudo 用户配置用于管理任务. 您可以遵循我们的 [)指南,在您的服务器上设置具有这些权限的用户。

一旦你有一个sudo用户可用,你将需要安装MySQL。可以使用这些指南,取决于你想要使用的软件包。第一个指南是适当的,如果你想坚持官方的Ubuntu存储库,而第二个指南是更适合的,如果你需要更多的最新功能:

一旦安装了MySQL,请登录您的服务器作为您的sudo用户继续。

安装 Percona Xtrabackup 工具

我们需要做的第一件事是安装实际的Percona备份实用程序,该项目保留了自己的存储库,我们可以将其添加到我们的MySQL服务器,以获取对包的访问。

要開始,請前往 Percona 發行頁面為 Ubuntu 尋找最新的 .deb 包來安裝儲存庫. 因為我們在 Ubuntu 16.04 上,這個代號為「Xenial Xerus」,我們應該選擇「xenial」包。 右下按一下相應的連結並複製地址。

<$>[注] 注: 您可以随时通过键入下列方式对您的服务器的发行代码名称进行双检查:

1lsb_release -c
1[secondary_label Output]
2Codename:   xenial

美元

一旦您复制了链接,转到/tmp目录,然后下载curl的仓库配置包:

1cd /tmp
2curl -LO https://repo.percona.com/apt/percona-release_0.1-4.xenial_all.deb

接下来,使用dpkg来安装下载的包,该包将在系统上配置Perconaapt存储库:

1sudo dpkg -i percona*

随着新存储库的配置,我们将更新本地包索引以下载有关新可用的包的信息,然后我们将从存储库安装XtraBackup工具和qpress压缩工具:

1sudo apt-get update
2sudo apt-get install percona-xtrabackup-24 qpress

其他包装的实用程序中,xtrabackupxbstreamqpress的命令现在将可用,我们的脚本将使用这些命令进行备份和恢复数据。

配置 MySQL 备份用户并添加测试数据

首先,开始与MySQL root用户进行交互式MySQL会话:

1mysql -u root -p

您将被提示在MySQL安装过程中选择的管理密码,一旦您输入了密码,您将被放入MySQL会话。

创建具有适当权限的MySQL用户

我们需要做的第一件事是创建一个新的MySQL用户配置来处理备份任务,我们只会给这个用户在系统运行时安全地复制数据所需的权限。

为了明确帐户的用途,我们将将新用户称为备份。我们将将用户的身份证放置在一个安全的文件中,所以请自由选择一个复杂的密码:

1CREATE USER 'backup'@'localhost' IDENTIFIED BY 'password';

接下来,我们需要授予新的备份用户执行数据库系统上的所有备份操作所需的权限。

1GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT, CREATE TABLESPACE, PROCESS, SUPER, CREATE, INSERT, SELECT ON *.* TO 'backup'@'localhost';
2FLUSH PRIVILEGES;

我们的MySQL备份用户已配置并具有所需的访问权限。

为备份创建测试数据

接下来,我们将创建一些测试数据. 运行以下命令以创建一个游乐场数据库与一个设备表. 我们将通过插入代表蓝色幻灯片的单个记录开始:

1CREATE DATABASE playground;
2CREATE TABLE playground.equipment ( id INT NOT NULL AUTO_INCREMENT, type VARCHAR(50), quant INT, color VARCHAR(25), PRIMARY KEY(id));
3INSERT INTO playground.equipment (type, quant, color) VALUES ("slide", 2, "blue");

在本指南中,我们将使用和修改这些数据来测试我们创建完整和增量备份的能力。

在结束MySQL会话之前,我们将检查datadir变量的值,我们需要知道这个值,以确保我们的系统级备份用户可以访问MySQL数据文件。

通过键入datadir变量的值显示:

1SELECT @@datadir;
1[secondary_label Output]
2+-----------------+
3| @@datadir       |
4+-----------------+
5| /var/lib/mysql/ |
6+-----------------+
71 row in set (0.01 sec)

记下你找到的位置。

这是我们目前在MySQL中所需要做的所有事情,通过键入输出壳:

1exit

接下来,我们可以看看一些系统级配置。

配置系统备份用户并分配权限

现在我们有一个MySQL用户来执行备份,我们将确保一个相应的Linux用户存在,具有类似的有限特权。

在Ubuntu 16.04上,一个备份用户和相应的备份组已经可用,请通过以下命令检查/etc/passwd/etc/group文件来确认此情况:

1grep backup /etc/passwd /etc/group
1[secondary_label Output]
2/etc/passwd:backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
3/etc/group:backup:x:34:

来自 /etc/passwd 文件的第一行描述了 备份’ 用户,而来自 /etc/group 文件的第二行定义了 备份` 组。

我们可以将备份用户添加到mysql组,以安全地允许访问数据库文件和目录,我们还应该将我们的sudo用户添加到备份组,以便我们可以访问我们要备份的文件。

输入以下命令,将备份用户添加到mysql组和sudo用户添加到备份组:

1sudo usermod -aG mysql backup
2sudo usermod -aG backup ${USER}

如果我们再次检查/etc/group文件,你会看到你的当前用户被添加到备份组中,而备份用户被添加到mysql组中:

1grep backup /etc/group
1[secondary_label Output]
2backup:x:34:sammy
3mysql:x:116:backup

新组在我们当前的会话中不自动可用. 要重新评估我们的sudo用户可用的组,请退出并重新登录,或键入:

1exec su - ${USER}

您将被要求继续使用您的sudo用户密码. 通过再次检查我们的用户组,确认您当前的会话现在可以访问备份组:

1id -nG
1[secondary_label Output]
2sammy sudo backup

我们的sudo用户现在将能够利用其在备份组中的会员资格。

接下来,我们需要通过添加组执行权限来使 /var/lib/mysql' 目录及其子目录可访问 mysql' 组。

注意:如果在先前检查MySQL内部时 datadir的值不是 `/var/lib/mysql,则在随后的命令中替换您发现的目录。

若要向「mysql」组提供访问 MySQL 数据目录,请键入:

1sudo find /var/lib/mysql -type d -exec chmod 750 {} \;

我们的备份用户现在可以访问所需的MySQL目录。

创建备份资产

现在MySQL和系统备份用户可用,我们可以开始设置配置文件,加密密密钥和其他资源,我们需要成功创建和保护我们的备份。

创建一个MySQL配置文件与备份参数

首先,创建一个最小的MySQL配置文件,备份脚本将使用它,这将包含MySQL用户的MySQL凭证。

在您的文本编辑器中打开一个文件在 /etc/mysql/backup.cnf:

1sudo nano /etc/mysql/backup.cnf

在内部,启动一个[客户端]部分,并设置您在MySQL中定义的MySQL备份用户和密码用户:

1[label /etc/mysql/backup.cnf]
2[client]
3user=backup
4password=password

保存并关闭文件,当你完成。

将文件的所有权授予备份用户,然后限制权限,以便其他用户无法访问该文件:

1sudo chown backup /etc/mysql/backup.cnf
2sudo chmod 600 /etc/mysql/backup.cnf

备份用户将能够访问此文件以获取适当的凭证,但其他用户将受到限制。

创建一个备份根目录

接下来,为备份内容创建一个目录. 我们将使用 /backups/mysql作为我们的备份的基本目录:

1sudo mkdir -p /backups/mysql

接下来,将/backups/mysql目录的所有权分配给backup用户,并将组所有权分配给mysql组:

1sudo chown backup:mysql /backups/mysql

备份用户现在应该能够将备份数据写到这个位置。

创建加密密钥来保护备份文件

由于备份包含来自数据库系统本身的所有数据,所以要正确保护它们很重要。‘extrabackup’实用程序可以加密每个文件,因为它们是备份和存档的。

我们可以使用openssl命令在备份根目录中创建加密密钥:

1printf '%s' "$(openssl rand -base64 24)" | sudo tee /backups/mysql/encryption_key && echo

再次,将所有权分配给备份用户,并拒绝所有其他用户访问:

1sudo chown backup:backup /backups/mysql/encryption_key
2sudo chmod 600 /backups/mysql/encryption_key

此密钥将在备份过程中使用,以及您需要从备份中恢复的任何时间。

创建备份和恢复脚本

我们现在有我们需要执行运行MySQL实例的安全备份的一切。

为了使我们的备份和恢复步骤可重复,我们将脚本整个过程,我们将创建以下脚本:

  • `备份-mysql.sh': 此脚本支持 MySQL 数据库, 加密和压缩过程中的文件 。 它创建了完整和递增的备份,并每天自动组织内容. 默认情况下,剧本维持了价值3天的备份.
  • `摘录-mysql.sh': 此脚本解压缩并解密备份文件以创建带有备份内容的目录.
  • `准备-mysql.sh': 此脚本"准备"后置目录,通过处理文件并应用日志. 任何增量备份都应用到全备份上. 一旦准备的脚本完成,文件就可以被移动回数据目录了. .

您可以随时查看 GitHub 上本教程的脚本(https://github.com/do-community/ubuntu-1604-mysql-backup) 如果您不想复制和粘贴下面的内容,您可以通过键入直接从 GitHub 下载它们:

1cd /tmp
2curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/backup-mysql.sh
3curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/extract-mysql.sh
4curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/prepare-mysql.sh

请确保在下载后检查脚本,以确保它们被成功检索,并您批准他们将执行的操作. 如果您满意,请将脚本标记为可执行,然后通过键入将其移动到 /usr/local/bin 目录:

1chmod +x /tmp/{backup,extract,prepare}-mysql.sh
2sudo mv /tmp/{backup,extract,prepare}-mysql.sh /usr/local/bin

接下来,我们将设置这些脚本,并详细讨论它们。

创建备份-mysql.sh 脚本

如果您没有从 GitHub 下载备份-mysql.sh脚本,请在/usr/local/bin目录中创建一个名为备份-mysql.sh的新文件:

1sudo nano /usr/local/bin/backup-mysql.sh

将脚本内容复制并粘贴到文件中:

 1[label /usr/local/bin/backup-mysql.sh]
 2#!/bin/bash
 3
 4export LC_ALL=C
 5
 6days_of_backups=3  # Must be less than 7
 7backup_owner="backup"
 8parent_dir="/backups/mysql"
 9defaults_file="/etc/mysql/backup.cnf"
10todays_dir="${parent_dir}/$(date +%a)"
11log_file="${todays_dir}/backup-progress.log"
12encryption_key_file="${parent_dir}/encryption_key"
13now="$(date +%m-%d-%Y_%H-%M-%S)"
14processors="$(nproc --all)"
15
16# Use this to echo to standard error
17error () {
18    printf "%s: %s\n" "$(basename "${BASH_SOURCE}")" "${1}" >&2
19    exit 1
20}
21
22trap 'error "An unexpected error occurred."' ERR
23
24sanity_check () {
25    # Check user running the script
26    if [ "$(id --user --name)" != "$backup_owner" ]; then
27        error "Script can only be run as the \"$backup_owner\" user"
28    fi
29
30    # Check whether the encryption key file is available
31    if [ ! -r "${encryption_key_file}" ]; then
32        error "Cannot read encryption key at ${encryption_key_file}"
33    fi
34}
35
36set_options () {
37    # List the xtrabackup arguments
38    xtrabackup_args=(
39        "--defaults-file=${defaults_file}"
40        "--backup"
41        "--extra-lsndir=${todays_dir}"
42        "--compress"
43        "--stream=xbstream"
44        "--encrypt=AES256"
45        "--encrypt-key-file=${encryption_key_file}"
46        "--parallel=${processors}"
47        "--compress-threads=${processors}"
48        "--encrypt-threads=${processors}"
49        "--slave-info"
50    )
51
52    backup_type="full"
53
54    # Add option to read LSN (log sequence number) if a full backup has been
55    # taken today.
56    if grep -q -s "to_lsn" "${todays_dir}/xtrabackup_checkpoints"; then
57        backup_type="incremental"
58        lsn=$(awk '/to_lsn/ {print $3;}' "${todays_dir}/xtrabackup_checkpoints")
59        xtrabackup_args+=( "--incremental-lsn=${lsn}" )
60    fi
61}
62
63rotate_old () {
64    # Remove the oldest backup in rotation
65    day_dir_to_remove="${parent_dir}/$(date --date="${days_of_backups} days ago" +%a)"
66
67    if [ -d "${day_dir_to_remove}" ]; then
68        rm -rf "${day_dir_to_remove}"
69    fi
70}
71
72take_backup () {
73    # Make sure today's backup directory is available and take the actual backup
74    mkdir -p "${todays_dir}"
75    find "${todays_dir}" -type f -name "*.incomplete" -delete
76    xtrabackup "${xtrabackup_args[@]}" --target-dir="${todays_dir}" > "${todays_dir}/${backup_type}-${now}.xbstream.incomplete" 2> "${log_file}"
77
78    mv "${todays_dir}/${backup_type}-${now}.xbstream.incomplete" "${todays_dir}/${backup_type}-${now}.xbstream"
79}
80
81sanity_check && set_options && rotate_old && take_backup
82
83# Check success and print message
84if tail -1 "${log_file}" | grep -q "completed OK"; then
85    printf "Backup successful!\n"
86    printf "Backup created at %s/%s-%s.xbstream\n" "${todays_dir}" "${backup_type}" "${now}"
87else
88    error "Backup failure! Check ${log_file} for more information"
89fi

脚本具有以下功能:

  • 创建加密、压缩的完整备份,每次运行一次。 * 基于当在同一天再次调用时的每日完整备份生成加密、压缩的增量备份。 * 保留按日安排的备份。

当脚本运行时,将创建一个每日目录,在那里将写出代表个别备份的时刻标记文件。第一个时刻标记的文件将是一个完整的备份,前缀为full-

备份将在日常目录中生成名为backup-progress.log的文件,该文件来自最新的备份操作的输出。 还将在那里创建名为xtrabackup_checkpoints的文件,其中包含最新的备份元数据。 此文件需要生成未来的增量备份,所以重要的是不要删除它。

完成后,保存并关闭文件。

接下来,如果您还没有这样做,请通过键入执行脚本:

1sudo chmod +x /usr/local/bin/backup-mysql.sh

我们现在有一个单一的命令,将启动MySQL备份。

创建 extract-mysql.sh 脚本

接下来,我们将创建extract-mysql.sh脚本,用于从单个备份文件中提取MySQL数据目录结构。

如果您没有从存储库下载脚本,请在 /usr/local/bin 目录中创建并打开名为 `extract-mysql.sh 的文件:

1sudo nano /usr/local/bin/extract-mysql.sh

在内部,插入以下脚本:

 1[label /usr/local/bin/extract-mysql.sh]
 2#!/bin/bash
 3
 4export LC_ALL=C
 5
 6backup_owner="backup"
 7encryption_key_file="/backups/mysql/encryption_key"
 8log_file="extract-progress.log"
 9number_of_args="${#}"
10processors="$(nproc --all)"
11
12# Use this to echo to standard error
13error () {
14    printf "%s: %s\n" "$(basename "${BASH_SOURCE}")" "${1}" >&2
15    exit 1
16}
17
18trap 'error "An unexpected error occurred. Try checking the \"${log_file}\" file for more information."' ERR
19
20sanity_check () {
21    # Check user running the script
22    if [ "${USER}" != "${backup_owner}" ]; then
23        error "Script can only be run as the \"${backup_owner}\" user"
24    fi
25
26    # Check whether the qpress binary is installed
27    if ! command -v qpress >/dev/null 2>&1; then
28        error "Could not find the \"qpress\" command. Please install it and try again."
29    fi
30
31    # Check whether any arguments were passed
32    if [ "${number_of_args}" -lt 1 ]; then
33        error "Script requires at least one \".xbstream\" file as an argument."
34    fi
35
36    # Check whether the encryption key file is available
37    if [ ! -r "${encryption_key_file}" ]; then
38        error "Cannot read encryption key at ${encryption_key_file}"
39    fi
40}
41
42do_extraction () {
43    for file in "${@}"; do
44        base_filename="$(basename "${file%.xbstream}")"
45        restore_dir="./restore/${base_filename}"
46
47        printf "\n\nExtracting file %s\n\n" "${file}"
48
49        # Extract the directory structure from the backup file
50        mkdir --verbose -p "${restore_dir}"
51        xbstream -x -C "${restore_dir}" < "${file}"
52
53        xtrabackup_args=(
54            "--parallel=${processors}"
55            "--decrypt=AES256"
56            "--encrypt-key-file=${encryption_key_file}"
57            "--decompress"
58        )
59
60        xtrabackup "${xtrabackup_args[@]}" --target-dir="${restore_dir}"
61        find "${restore_dir}" -name "*.xbcrypt" -exec rm {} \;
62        find "${restore_dir}" -name "*.qp" -exec rm {} \;
63
64        printf "\n\nFinished work on %s\n\n" "${file}"
65
66    done > "${log_file}" 2>&1
67}
68
69sanity_check && do_extraction "$@"
70
71ok_count="$(grep -c 'completed OK' "${log_file}")"
72
73# Check the number of reported completions. For each file, there is an
74# informational "completed OK". If the processing was successful, an
75# additional "completed OK" is printed. Together, this means there should be 2
76# notices per backup file if the process was successful.
77if (( $ok_count !=  $# )); then
78    error "It looks like something went wrong. Please check the \"${log_file}\" file for additional information"
79else
80    printf "Extraction complete! Backup directories have been extracted to the \"restore\" directory.\n"
81fi

backup-mysql.sh脚本不同,该脚本旨在自动化,此脚本旨在故意使用,当您计划从备份中恢复。

该脚本在当前目录中创建一个恢复目录,然后为每个被传输的备份创建内部的个别目录,作为参数,它将通过从档案中提取目录结构来处理所提供的.xbstream文件,解密内部的个别文件,然后解压解密的文件。

完成此过程后,恢复目录应该包含每个提供的备份的目录,这允许您检查目录,检查备份的内容,并决定要准备和恢复哪些备份。

完成后保存并关闭文件,然后通过键入确保脚本可执行:

1sudo chmod +x /usr/local/bin/extract-mysql.sh

此脚本将允许我们将单个备份文件扩展到恢复所需的目录结构。

创建 prepare-mysql.sh 脚本

最后,在/usr/local/bin目录中下载或创建prepare-mysql.sh脚本. 此脚本将将日志应用于每个备份,以创建一致的数据库截图。

在文本编辑器中创建脚本文件,如果您之前没有下载它:

1sudo nano /usr/local/bin/prepare-mysql.sh

内部,粘贴下列内容:

 1[label /usr/local/bin/prepare-mysql.sh]
 2#!/bin/bash
 3
 4export LC_ALL=C
 5
 6shopt -s nullglob
 7incremental_dirs=( ./incremental-*/ )
 8full_dirs=( ./full-*/ )
 9shopt -u nullglob
10
11backup_owner="backup"
12log_file="prepare-progress.log"
13full_backup_dir="${full_dirs[0]}"
14
15# Use this to echo to standard error
16error() {
17    printf "%s: %s\n" "$(basename "${BASH_SOURCE}")" "${1}" >&2
18    exit 1
19}
20
21trap 'error "An unexpected error occurred. Try checking the \"${log_file}\" file for more information."' ERR
22
23sanity_check () {
24    # Check user running the script
25    if [ "${USER}" != "${backup_owner}" ]; then
26        error "Script can only be run as the \"${backup_owner}\" user."
27    fi
28
29    # Check whether a single full backup directory are available
30    if (( ${#full_dirs[@]} != 1 )); then
31        error "Exactly one full backup directory is required."
32    fi
33}
34
35do_backup () {
36    # Apply the logs to each of the backups
37    printf "Initial prep of full backup %s\n" "${full_backup_dir}"
38    xtrabackup --prepare --apply-log-only --target-dir="${full_backup_dir}"
39
40    for increment in "${incremental_dirs[@]}"; do
41        printf "Applying incremental backup %s to %s\n" "${increment}" "${full_backup_dir}"
42        xtrabackup --prepare --apply-log-only --incremental-dir="${increment}" --target-dir="${full_backup_dir}"
43    done
44
45    printf "Applying final logs to full backup %s\n" "${full_backup_dir}"
46    xtrabackup --prepare --target-dir="${full_backup_dir}"
47}
48
49sanity_check && do_backup > "${log_file}" 2>&1
50
51# Check the number of reported completions. Each time a backup is processed,
52# an informational "completed OK" and a real version is printed. At the end of
53# the process, a final full apply is performed, generating another 2 messages.
54ok_count="$(grep -c 'completed OK' "${log_file}")"
55
56if (( ${ok_count} == ${#full_dirs[@]} + ${#incremental_dirs[@]} + 1 )); then
57    cat << EOF
58Backup looks to be fully prepared. Please check the "prepare-progress.log" file
59to verify before continuing.
60
61If everything looks correct, you can apply the restored files.
62
63First, stop MySQL and move or remove the contents of the MySQL data directory:
64
65        sudo systemctl stop mysql
66        sudo mv /var/lib/mysql/ /tmp/
67
68Then, recreate the data directory and copy the backup files:
69
70        sudo mkdir /var/lib/mysql
71        sudo xtrabackup --copy-back --target-dir=${PWD}/$(basename "${full_backup_dir}")
72
73Afterward the files are copied, adjust the permissions and restart the service:
74
75        sudo chown -R mysql:mysql /var/lib/mysql
76        sudo find /var/lib/mysql -type d -exec chmod 750 {} \\;
77        sudo systemctl start mysql
78EOF
79else
80    error "It looks like something went wrong. Check the \"${log_file}\" file for more information."
81fi

它使用MySQL日志将承诺的交易应用于完整的备份后,它将任何增量备份应用于完整的备份以更新数据以最新信息,再次应用承诺的交易。

一旦所有备份合并,未承诺的交易将被滚动回来,此时,完整备份将代表一组可移动到MySQL数据目录的数据。

为了最大限度地减少数据丢失的可能性,脚本不会再将文件复制到数据目录,这样用户就可以手动验证此过程中创建的备份内容和日志文件,并决定如何处理MySQL数据目录的当前内容。

完成后保存并关闭文件. 如果您以前没有这样做,请通过键入标记文件为可执行:

1sudo chmod +x /usr/local/bin/prepare-mysql.sh

此脚本是我们在将备份文件移动到MySQL的数据目录之前运行的最终脚本。

测试MySQL备份和恢复脚本

现在备份和恢复脚本已经在服务器上,我们应该测试它们。

完成完整的备份

开始用备份用户调用backup-mysql.sh脚本:

1sudo -u backup backup-mysql.sh
1[secondary_label Output]
2Backup successful!
3Backup created at /backups/mysql/Thu/full-04-20-2017_14-55-17.xbstream

如果一切按计划进行,脚本将正确执行,表示成功,并输出新备份文件的位置.正如上面的输出所示,每天目录()已经创建,以容纳当天的备份。

让我们进入每日备份目录并查看内容:

1cd /backups/mysql/"$(date +%a)"
2ls
1[secondary_label Output]
2backup-progress.log full-04-20-2017_14-55-17.xbstream xtrabackup_checkpoints xtrabackup_info

在这里,我们看到实际的备份文件(‘full-04-20-2017_14-55-17.xbstream’),备份事件的日志(‘backup-progress.log’),包含有关备份内容的元数据的‘xtrabackup_checkpoints’文件,以及包含其他元数据的‘xtrabackup_info’文件。

如果我们排列backup-progress.log,我们可以确认备份已成功完成。

1tail backup-progress.log
 1[secondary_label Output]
 2170420 14:55:19 All tables unlocked
 3170420 14:55:19 [00] Compressing, encrypting and streaming ib_buffer_pool to <STDOUT>
 4170420 14:55:19 [00]        ...done
 5170420 14:55:19 Backup created in directory '/backups/mysql/Thu/'
 6170420 14:55:19 [00] Compressing, encrypting and streaming backup-my.cnf
 7170420 14:55:19 [00]        ...done
 8170420 14:55:19 [00] Compressing, encrypting and streaming xtrabackup_info
 9170420 14:55:19 [00]        ...done
10xtrabackup: Transaction log of lsn (2549956) to (2549965) was copied.
11170420 14:55:19 completed OK!

如果我们看‘xtrabackup_checkpoints’文件,我们可以查看有关备份的信息. 虽然这个文件为管理员提供了一些有用的信息,但它主要用于随后的备份工作,以便他们知道哪些数据已经被处理。

虽然此副本在每个备份中被重写,以代表最新信息,但每个原始文件仍然可在备份档案中使用。

1cat xtrabackup_checkpoints
1[secondary_label Output]
2backup_type = full-backuped
3from_lsn = 0
4to_lsn = 2549956
5last_lsn = 2549965
6compact = 0
7recover_binlog_info = 0

上面的示例告诉我们,完全备份已经完成,并且备份覆盖了日志序列号(LSN)0到日志序列号2549956。

执行增量备份

现在我们有一个完整的备份,我们可以采取额外的增量备份. 增量备份记录自上次备份执行以来所做的更改. 第一次增量备份是基于完整的备份,随后的增量备份是基于以前的增量备份。

我们应该在进行另一个备份之前将一些数据添加到我们的数据库中,这样我们就可以知道哪些备份已经应用。

将另一个记录插入我们的游乐场数据库的设备表中,代表10个黄色旋转。

1mysql -u root -p -e 'INSERT INTO playground.equipment (type, quant, color) VALUES ("swing", 10, "yellow");'

现在有比我们最近的备份更多的当前数据,我们可以采取增量备份来捕捉这些更改. 如果有一个完整的备份在同一天存在,则backup-mysql.sh脚本将采取增量备份:

1sudo -u backup backup-mysql.sh
1[secondary_label Output]
2Backup successful!
3Backup created at /backups/mysql/Thu/incremental-04-20-2017_17-15-03.xbstream

再次检查每日备份目录以找到增量备份档案:

1cd /backups/mysql/"$(date +%a)"
2ls
1[secondary_label Output]
2backup-progress.log incremental-04-20-2017_17-15-03.xbstream xtrabackup_info
3full-04-20-2017_14-55-17.xbstream xtrabackup_checkpoints

xtrabackup_checkpoints文件的内容现在是指最新的增量备份:

1cat xtrabackup_checkpoints
1[secondary_label Output]
2backup_type = incremental
3from_lsn = 2549956
4to_lsn = 2550159
5last_lsn = 2550168
6compact = 0
7recover_binlog_info = 0

备份类型列为增量,而不是从 LSN 0 开始,就像我们的完整备份一样,它从我们最后的备份结束的地方开始。

提取备份

接下来,让我们提取备份文件来创建备份目录. 由于空间和安全考虑,这通常只能在您准备恢复数据时完成。

我们可以通过将.xbstream备份文件传输到extract-mysql.sh脚本中提取备份,再一次,这必须由备份用户运行:

1sudo -u backup extract-mysql.sh *.xbstream
1[secondary_label Output]
2Extraction complete! Backup directories have been extracted to the "restore" directory.

上面的输出表明该过程已成功完成. 如果我们再次检查每日备份目录的内容,将创建一个extract-progress.log文件和一个restore目录。

如果我们尾巴提取日志,我们可以确认最新备份已成功提取。

1tail extract-progress.log
1[secondary_label Output]
2170420 17:23:32 [01] decrypting and decompressing ./performance_schema/socket_instances.frm.qp.xbcrypt
3170420 17:23:32 [01] decrypting and decompressing ./performance_schema/events_waits_summary_by_user_by_event_name.frm.qp.xbcrypt
4170420 17:23:32 [01] decrypting and decompressing ./performance_schema/status_by_user.frm.qp.xbcrypt
5170420 17:23:32 [01] decrypting and decompressing ./performance_schema/replication_group_members.frm.qp.xbcrypt
6170420 17:23:32 [01] decrypting and decompressing ./xtrabackup_logfile.qp.xbcrypt
7170420 17:23:33 completed OK!
8
9Finished work on incremental-04-20-2017_17-15-03.xbstream

如果我们进入恢复目录,与我们提取的备份文件相应的目录现在可用:

1cd restore
2ls -F
1[secondary_label Output]
2full-04-20-2017_14-55-17/  incremental-04-20-2017_17-15-03/

备份目录包含原始备份文件,但它们尚未处于MySQL可以使用的状态。

准备最后的备份

接下来,我们将准备备份文件. 要做到这一点,您必须在包含完整和任何增量备份的恢复目录中。 脚本将将任何增量目录的更改应用到完整备份目录中。

如果出于任何原因,您不希望恢复某些更改,现在是您从)。

当你准备好时,请拨打prepare-mysql.sh脚本. 再次,请确保你在恢复目录中,你的个人备份目录位于:

1sudo -u backup prepare-mysql.sh
 1[secondary_label Output]
 2Backup looks to be fully prepared. Please check the "prepare-progress.log" file
 3to verify before continuing.
 4
 5If everything looks correct, you can apply the restored files.
 6
 7First, stop MySQL and move or remove the contents of the MySQL data directory:
 8
 9        sudo systemctl stop mysql
10        sudo mv /var/lib/mysql/ /tmp/
11
12Then, recreate the data directory and copy the backup files:
13
14        sudo mkdir /var/lib/mysql
15        sudo xtrabackup --copy-back --target-dir=/backups/mysql/Thu/restore/full-04-20-2017_14-55-17
16
17Afterward the files are copied, adjust the permissions and restart the service:
18
19        sudo chown -R mysql:mysql /var/lib/mysql
20        sudo find /var/lib/mysql -type d -exec chmod 750 {} \;
21        sudo systemctl start mysql

上面的输出表明,脚本认为备份已完全准备好,并且完整备份现在代表了一个完全一致的数据集。

脚本不会真正将文件复制到MySQL的数据目录,以便您可以验证一切看起来是正确的。

将备份数据恢复到MySQL数据目录

如果您在审查日志后确信一切顺利,您可以遵循在prepare-mysql.sh输出中描述的指示。

首先,停止运行 MySQL 过程:

1sudo systemctl stop mysql

由于备份数据可能与MySQL数据目录的当前内容相冲突,我们应该删除或移动 /var/lib/mysql目录. 如果您在文件系统上有空间,最好的选择是将当前内容移动到 /tmp目录或其他地方,如果有什么不对劲:

1sudo mv /var/lib/mysql/ /tmp

重新创建一个空的 /var/lib/mysql 目录. 我们需要在一瞬间修复权限,所以我们还不必担心:

1sudo mkdir /var/lib/mysql

现在,我们可以使用xtrabackup实用程序将完整的备份复制到MySQL数据目录,然后在下面的命令中替代您准备的完整备份的路径:

1sudo xtrabackup --copy-back --target-dir=/backups/mysql/Thu/restore/full-04-20-2017_14-55-17

一旦文件在位,我们需要重新修复所有权和权限,以便MySQL用户和组拥有并可以访问恢复的结构:

1sudo chown -R mysql:mysql /var/lib/mysql
2sudo find /var/lib/mysql -type d -exec chmod 750 {} \;

我们的恢复文件现在在MySQL数据目录中。

重新启动 MySQL 以完成该过程:

1sudo systemctl start mysql

检查数据是否已被恢复,通过查看playground.equipment表的内容。

1mysql -u root -p -e 'SELECT * FROM playground.equipment;'
1[secondary_label Output]
2+----+-------+-------+--------+
3| id | type  | quant | color  |
4+----+-------+-------+--------+
5|  1 | slide |     2 | blue   |
6|  2 | swing |    10 | yellow |
7+----+-------+-------+--------+
82 rows in set (0.02 sec)

我们的数据已成功恢复。

恢复数据后,重要的是返回并删除恢复目录. 未来的增量备份无法应用到完整备份后,所以我们应该删除它。

1cd ~
2sudo rm -rf /backups/mysql/"$(date +%a)"/restore

下次我们需要清洁副本的备份目录,我们可以从备份文件中再次提取它们。

创建一个Cron工作以运行备份小时

现在我们已经验证了备份和恢复过程的顺利运行,我们应该设置一个cron任务,自动进行定期备份。

我们将创建一个小脚本在/etc/cron.hourly目录中,自动运行我们的备份脚本并记录结果。

1sudo nano /etc/cron.hourly/backup-mysql

内部,我们将使用systemd-cat实用程序调用备份脚本,以便输出在日志中可用,我们将用backup-mysql标记它们,以便我们可以轻松过滤日志:

1[label /etc/cron.hourly/backup-mysql]
2#!/bin/bash
3sudo -u backup systemd-cat --identifier=backup-mysql /usr/local/bin/backup-mysql.sh

完成后保存并关闭文件. 通过键入,使脚本可执行:

1sudo chmod +x /etc/cron.hourly/backup-mysql

备份脚本现在将每小时运行,脚本本身将负责清理超过三天前的备份。

我们可以通过手动运行它来测试cron脚本:

1sudo /etc/cron.hourly/backup-mysql

完成后,检查日志以获取日志消息,键入:

1sudo journalctl -t backup-mysql
1[secondary_label Output]
2-- Logs begin at Wed 2017-04-19 18:59:23 UTC, end at Thu 2017-04-20 18:54:49 UTC. --
3Apr 20 18:35:07 myserver backup-mysql[2302]: Backup successful!
4Apr 20 18:35:07 myserver backup-mysql[2302]: Backup created at /backups/mysql/Thu/incremental-04-20-2017_18-35-05.xbstream

检查几小时后,以确保正在进行额外的备份。

结论

在本指南中,我们安装了 Percona Xtrabackup 工具,以帮助定期创建 MySQL 数据的实时截图,我们配置了 MySQL 和系统备份用户,设置了一个加密密钥来保护我们的备份文件,然后设置脚本来自动化备份和恢复程序的部分。

备份脚本在每一天开始时生成完整的备份,随后每小时进行增量备份,随时保留三天的备份。加密文件和加密密密钥可与其他备份技术一起使用,将数据转移到现场进行保留。

Published At
Categories with 技术
comments powered by Disqus