如何在Ubuntu16.04上使用PerconaXtraBackup配置MySQL备份
介绍
数据库通常会在您的基础架构中存储一些最有价值的信息。 因此,拥有可靠的备份以防止在发生事故或硬件故障时丢失数据非常重要。
Percona XtraBackup 备份工具提供了一种在系统运行时对 MySQL 数据执行“热”备份的方法。 他们通过在文件系统级别复制数据文件然后执行崩溃恢复来实现数据集内的一致性来做到这一点。
在本指南中,我们将创建一个系统来自动备份 Ubuntu 16.04 服务器上的 MySQL 数据。 我们将在一组脚本中使用 cron 和 Percona 工具来创建定期、安全的备份,以便在出现问题时用于恢复。
先决条件
要完成本指南,您需要一个 Ubuntu 16.04 服务器,并为管理任务配置非 root sudo
用户。 您可以按照我们的 “使用 Ubuntu 16.04 进行初始服务器设置” 指南在您的服务器上设置具有这些权限的用户。
一旦您有 sudo
用户可用,您将需要安装 MySQL。 可以使用这些指南中的任何一个,具体取决于您要使用的包。 如果您想坚持使用官方 Ubuntu 存储库,则第一个指南是合适的,而如果您需要更多最新功能,则第二个指南更适合:
- 如何在 Ubuntu 16.04 上安装 MySQL(使用 Ubuntu 存储库中的默认包)
- 如何在 Ubuntu 16.04 上安装最新的 MySQL(使用 MySQL 项目提供的更新包)
安装 MySQL 后,以 sudo
用户身份登录您的服务器以继续。
安装 Percona Xtrabackup 工具
我们需要做的第一件事是安装实际的 Percona 备份实用程序。 该项目维护自己的存储库,我们可以将其添加到 MySQL 服务器以访问包。
首先,转到 Ubuntu 的 Percona 发布页面,找到用于安装存储库的最新 .deb
软件包。 由于我们使用的是代号为“Xenial Xerus”的Ubuntu 16.04,我们应该选择“xenial”包。 右键单击相应的链接并复制地址。
注意: 您可以随时通过键入以下命令仔细检查服务器的发布代号:
lsb_release -c
OutputCodename: xenial
复制链接后,移动到 /tmp
目录,然后使用 curl
下载存储库配置包:
cd /tmp curl -LO https://repo.percona.com/apt/percona-release_0.1-4.xenial_all.deb
接下来,使用 dpkg
安装下载的包,这将在系统上配置 Percona apt
存储库:
sudo dpkg -i percona*
配置新的存储库后,我们将更新本地包索引以获取有关新可用包的信息。 然后,我们将从存储库中安装 XtraBackup 工具和 qpress
压缩实用程序:
sudo apt-get update sudo apt-get install percona-xtrabackup-24 qpress
在其他捆绑的实用程序中,xtrabackup
、xbstream
和 qpress
命令现在将可用。 我们的脚本将使用其中的每一个来执行备份和恢复数据。
配置 MySQL 备份用户并添加测试数据
首先,使用 MySQL root 用户启动一个交互式 MySQL 会话:
mysql -u root -p
系统将提示您输入在 MySQL 安装期间选择的管理密码。 输入密码后,您将进入 MySQL 会话。
创建具有适当权限的 MySQL 用户
我们需要做的第一件事是创建一个配置为处理备份任务的新 MySQL 用户。 我们只会给予该用户在系统运行时安全复制数据所需的权限。
为了明确帐户的用途,我们将调用新用户 backup
。 我们将把用户的凭据放在一个安全的文件中,所以请随意选择一个复杂的密码:
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'password';
接下来,我们需要授予新的 backup
用户在数据库系统上执行所有备份操作所需的权限。 通过键入以下内容授予所需的权限并将它们应用于当前会话:
GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT, CREATE TABLESPACE, PROCESS, SUPER, CREATE, INSERT, SELECT ON *.* TO 'backup'@'localhost'; FLUSH PRIVILEGES;
我们的 MySQL 备份用户已配置并具有所需的访问权限。
为备份创建测试数据
接下来,我们将创建一些测试数据。 运行以下命令以创建带有 equipment
表的 playground
数据库。 我们将从插入代表蓝色幻灯片的单个记录开始:
CREATE DATABASE playground; CREATE TABLE playground.equipment ( id INT NOT NULL AUTO_INCREMENT, type VARCHAR(50), quant INT, color VARCHAR(25), PRIMARY KEY(id)); INSERT INTO playground.equipment (type, quant, color) VALUES ("slide", 2, "blue");
在本指南的后面部分,我们将使用和更改这些数据来测试我们创建完整备份和增量备份的能力。
在结束 MySQL 会话之前,我们将检查 datadir
变量的值。 我们需要知道这个值,以确保我们的系统级 backup
用户可以访问 MySQL 数据文件。
通过键入以下内容显示 datadir
变量的值:
SELECT @@datadir;
Output+-----------------+ | @@datadir | +-----------------+ | /var/lib/mysql/ | +-----------------+ 1 row in set (0.01 sec)
记下您找到的位置。
这就是我们目前在 MySQL 中需要做的所有事情。 通过键入以下内容退出到 shell:
exit
接下来,我们可以看看一些系统级别的配置。
配置系统备份用户并分配权限
现在我们有一个 MySQL 用户来执行备份,我们将确保相应的 Linux 用户存在,具有类似的有限权限。
在 Ubuntu 16.04 上,backup
用户和相应的 backup
组已经可用。 通过使用以下命令检查 /etc/passwd
和 /etc/group
文件来确认这一点:
grep backup /etc/passwd /etc/group
Output/etc/passwd:backup:x:34:34:backup:/var/backups:/usr/sbin/nologin /etc/group:backup:x:34:
/etc/passwd
文件的第一行描述了 backup
用户,而 /etc/group
文件的第二行定义了 backup
组。
保存 MySQL 数据的 /var/lib/mysql
目录归 mysql
用户和组所有。 我们可以将 backup
用户添加到 mysql
组,以安全地允许访问数据库文件和目录。 我们还应该将我们的 sudo
用户添加到 backup
组,以便我们可以访问我们将备份的文件。
键入以下命令将 backup
用户添加到 mysql
组,并将您的 sudo
用户添加到 backup
组:
sudo usermod -aG mysql backup sudo usermod -aG backup ${USER}
如果我们再次检查 /etc/group
文件,您会看到您的当前用户已添加到 backup
组,并且 backup
用户已添加到 [X155X ] 团体:
grep backup /etc/group
Outputbackup:x:34:sammy mysql:x:116:backup
新组在我们当前的会话中自动不可用。 要重新评估我们的 sudo
用户可用的组,请注销并重新登录,或键入:
exec su - ${USER}
系统将提示您输入 sudo
用户密码以继续。 通过再次检查我们的用户组,确认您当前的会话现在可以访问 backup
组:
id -nG
Outputsammy sudo backup
我们的 sudo
用户现在可以利用其在 backup
组中的成员资格。
接下来,我们需要通过添加组执行权限,使 mysql
组可以访问 /var/lib/mysql
目录及其子目录。 否则,backup
用户将无法进入这些目录,即使它是 mysql
组的成员。
注意: 如果您之前在 MySQL 内部检查时 datadir
的值不是 /var/lib/mysql
,请替换您在以下命令中发现的目录。
要授予 mysql
组访问 MySQL 数据目录的权限,请键入:
sudo find /var/lib/mysql -type d -exec chmod 750 {} \;
我们的 backup
用户现在可以访问 MySQL 目录。
创建备份资产
现在 MySQL 和系统备份用户可用,我们可以开始设置成功创建和保护备份所需的配置文件、加密密钥和其他资产。
使用备份参数创建 MySQL 配置文件
首先创建备份脚本将使用的最小 MySQL 配置文件。 这将包含 MySQL 用户的 MySQL 凭据。
在文本编辑器中打开位于 /etc/mysql/backup.cnf
的文件:
sudo nano /etc/mysql/backup.cnf
在里面,开始一个 [client]
部分并设置您在 MySQL 中定义的 MySQL 备份用户和密码用户:
/etc/mysql/backup.cnf
[client] user=backup password=password
完成后保存并关闭文件。
将文件的所有权授予 backup
用户,然后限制权限,使其他用户无法访问该文件:
sudo chown backup /etc/mysql/backup.cnf sudo chmod 600 /etc/mysql/backup.cnf
备份用户将能够访问此文件以获取正确的凭据,但其他用户将受到限制。
创建备份根目录
接下来,为备份内容创建一个目录。 我们将使用 /backups/mysql
作为备份的基本目录:
sudo mkdir -p /backups/mysql
接下来,将 /backups/mysql
目录的所有权分配给 backup
用户,并将组所有权分配给 mysql
组:
sudo chown backup:mysql /backups/mysql
backup
用户现在应该能够将备份数据写入此位置。
创建加密密钥以保护备份文件
因为备份包含来自数据库系统本身的所有数据,所以正确保护它们很重要。 xtrabackup
实用程序能够在备份和存档每个文件时对其进行加密。 我们只需要为它提供一个加密密钥。
我们可以使用 openssl
命令在备份根目录中创建加密密钥:
printf '%s' "$(openssl rand -base64 24)" | sudo tee /backups/mysql/encryption_key && echo
限制对该文件的访问也非常重要。 同样,将所有权分配给 backup
用户并拒绝所有其他用户的访问:
sudo chown backup:backup /backups/mysql/encryption_key sudo chmod 600 /backups/mysql/encryption_key
此密钥将在备份过程中以及您需要从备份中恢复的任何时间使用。
创建备份和恢复脚本
我们现在拥有对正在运行的 MySQL 实例执行安全备份所需的一切。
为了使我们的备份和恢复步骤可重复,我们将编写整个过程的脚本。 我们将创建以下脚本:
backup-mysql.sh
:此脚本备份 MySQL 数据库,在此过程中对文件进行加密和压缩。 它创建完整和增量备份,并按天自动组织内容。 默认情况下,该脚本会维护 3 天的备份。extract-mysql.sh
:此脚本对备份文件进行解压缩和解密,以创建包含备份内容的目录。prepare-mysql.sh
:此脚本通过处理文件和应用日志来“准备”备份目录。 任何增量备份都将应用于完整备份。 准备脚本完成后,文件就可以移回数据目录了。
您可以随时在 GitHub 上查看本教程的 存储库中的脚本。 如果您不想复制和粘贴以下内容,可以直接从 GitHub 下载,输入:
cd /tmp curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/backup-mysql.sh curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/extract-mysql.sh curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/prepare-mysql.sh
请务必在下载后检查脚本,以确保它们已成功检索并且您批准它们将执行的操作。 如果您满意,请将脚本标记为可执行,然后通过键入以下命令将它们移动到 /usr/local/bin
目录中:
chmod +x /tmp/{backup,extract,prepare}-mysql.sh sudo mv /tmp/{backup,extract,prepare}-mysql.sh /usr/local/bin
接下来,我们将设置每个脚本并更详细地讨论它们。
创建 backup-mysql.sh 脚本
如果您没有从 GitHub 下载 backup-mysql.sh
脚本,请在 /usr/local/bin
目录中创建一个名为 backup-mysql.sh
的新文件:
sudo nano /usr/local/bin/backup-mysql.sh
将脚本内容复制并粘贴到文件中:
/usr/local/bin/backup-mysql.sh
#!/bin/bash export LC_ALL=C days_of_backups=3 # Must be less than 7 backup_owner="backup" parent_dir="/backups/mysql" defaults_file="/etc/mysql/backup.cnf" todays_dir="${parent_dir}/$(date +%a)" log_file="${todays_dir}/backup-progress.log" encryption_key_file="${parent_dir}/encryption_key" now="$(date +%m-%d-%Y_%H-%M-%S)" processors="$(nproc --all)" # Use this to echo to standard error error () { printf "%s: %s\n" "$(basename "${BASH_SOURCE}")" "${1}" >&2 exit 1 } trap 'error "An unexpected error occurred."' ERR sanity_check () { # Check user running the script if [ "$(id --user --name)" != "$backup_owner" ]; then error "Script can only be run as the \"$backup_owner\" user" fi # Check whether the encryption key file is available if [ ! -r "${encryption_key_file}" ]; then error "Cannot read encryption key at ${encryption_key_file}" fi } set_options () { # List the xtrabackup arguments xtrabackup_args=( "--defaults-file=${defaults_file}" "--backup" "--extra-lsndir=${todays_dir}" "--compress" "--stream=xbstream" "--encrypt=AES256" "--encrypt-key-file=${encryption_key_file}" "--parallel=${processors}" "--compress-threads=${processors}" "--encrypt-threads=${processors}" "--slave-info" ) backup_type="full" # Add option to read LSN (log sequence number) if a full backup has been # taken today. if grep -q -s "to_lsn" "${todays_dir}/xtrabackup_checkpoints"; then backup_type="incremental" lsn=$(awk '/to_lsn/ {print $3;}' "${todays_dir}/xtrabackup_checkpoints") xtrabackup_args+=( "--incremental-lsn=${lsn}" ) fi } rotate_old () { # Remove the oldest backup in rotation day_dir_to_remove="${parent_dir}/$(date --date="${days_of_backups} days ago" +%a)" if [ -d "${day_dir_to_remove}" ]; then rm -rf "${day_dir_to_remove}" fi } take_backup () { # Make sure today's backup directory is available and take the actual backup mkdir -p "${todays_dir}" find "${todays_dir}" -type f -name "*.incomplete" -delete xtrabackup "${xtrabackup_args[@]}" --target-dir="${todays_dir}" > "${todays_dir}/${backup_type}-${now}.xbstream.incomplete" 2> "${log_file}" mv "${todays_dir}/${backup_type}-${now}.xbstream.incomplete" "${todays_dir}/${backup_type}-${now}.xbstream" } sanity_check && set_options && rotate_old && take_backup # Check success and print message if tail -1 "${log_file}" | grep -q "completed OK"; then printf "Backup successful!\n" printf "Backup created at %s/%s-%s.xbstream\n" "${todays_dir}" "${backup_type}" "${now}" else error "Backup failure! Check ${log_file} for more information" fi
该脚本具有以下功能:
- 每天第一次运行时创建一个加密、压缩的完整备份。
- 在同一天再次调用时,根据每日完整备份生成加密的压缩增量备份。
- 维护按天组织的备份。 默认情况下,会保留三天的备份。 这可以通过调整脚本中的
days_of_backups
参数来更改。
运行脚本时,将创建一个每日目录,其中将写入代表单个备份的时间戳文件。 第一个时间戳文件将是完整备份,前缀为 full-
。 当天的后续备份将是增量备份,由 incremental-
前缀表示,表示自上次完整或增量备份以来的更改。
备份将在 daily 目录中生成一个名为 backup-progress.log
的文件,其中包含最近备份操作的输出。 还将在那里创建一个名为 xtrabackup_checkpoints
的文件,其中包含最新的备份元数据。 该文件是生成未来增量备份所必需的,因此不要删除它很重要。 还会生成一个名为 xtrabackup_info
的文件,其中包含其他元数据,但脚本不引用此文件。
完成后,保存并关闭文件。
接下来,如果您还没有这样做,请键入以下命令使脚本可执行:
sudo chmod +x /usr/local/bin/backup-mysql.sh
我们现在有一个可用的命令来启动 MySQL 备份。
创建 extract-mysql.sh 脚本
接下来,我们将创建 extract-mysql.sh
脚本。 这将用于从单个备份文件中提取 MySQL 数据目录结构。
如果您没有从存储库下载脚本,请在 /usr/local/bin
目录中创建并打开一个名为 extract-mysql.sh
的文件:
sudo nano /usr/local/bin/extract-mysql.sh
在里面,粘贴以下脚本:
/usr/local/bin/extract-mysql.sh
#!/bin/bash export LC_ALL=C backup_owner="backup" encryption_key_file="/backups/mysql/encryption_key" log_file="extract-progress.log" number_of_args="${#}" processors="$(nproc --all)" # Use this to echo to standard error error () { printf "%s: %s\n" "$(basename "${BASH_SOURCE}")" "${1}" >&2 exit 1 } trap 'error "An unexpected error occurred. Try checking the \"${log_file}\" file for more information."' ERR sanity_check () { # Check user running the script if [ "${USER}" != "${backup_owner}" ]; then error "Script can only be run as the \"${backup_owner}\" user" fi # Check whether the qpress binary is installed if ! command -v qpress >/dev/null 2>&1; then error "Could not find the \"qpress\" command. Please install it and try again." fi # Check whether any arguments were passed if [ "${number_of_args}" -lt 1 ]; then error "Script requires at least one \".xbstream\" file as an argument." fi # Check whether the encryption key file is available if [ ! -r "${encryption_key_file}" ]; then error "Cannot read encryption key at ${encryption_key_file}" fi } do_extraction () { for file in "${@}"; do base_filename="$(basename "${file%.xbstream}")" restore_dir="./restore/${base_filename}" printf "\n\nExtracting file %s\n\n" "${file}" # Extract the directory structure from the backup file mkdir --verbose -p "${restore_dir}" xbstream -x -C "${restore_dir}" < "${file}" xtrabackup_args=( "--parallel=${processors}" "--decrypt=AES256" "--encrypt-key-file=${encryption_key_file}" "--decompress" ) xtrabackup "${xtrabackup_args[@]}" --target-dir="${restore_dir}" find "${restore_dir}" -name "*.xbcrypt" -exec rm {} \; find "${restore_dir}" -name "*.qp" -exec rm {} \; printf "\n\nFinished work on %s\n\n" "${file}" done > "${log_file}" 2>&1 } sanity_check && do_extraction "$@" ok_count="$(grep -c 'completed OK' "${log_file}")" # Check the number of reported completions. For each file, there is an # informational "completed OK". If the processing was successful, an # additional "completed OK" is printed. Together, this means there should be 2 # notices per backup file if the process was successful. if (( $ok_count != $# )); then error "It looks like something went wrong. Please check the \"${log_file}\" file for additional information" else printf "Extraction complete! Backup directories have been extracted to the \"restore\" directory.\n" fi
与设计为自动化的 backup-mysql.sh
脚本不同,此脚本旨在在您计划从备份恢复时有意使用。 因此,该脚本希望您传递您希望提取的 .xbstream
文件。
该脚本在当前目录中创建一个 restore
目录,然后为作为参数传入的每个备份创建单独的目录。 它将通过从存档中提取目录结构,解密其中的各个文件,然后解压缩解密文件来处理提供的 .xbstream
文件。
此过程完成后,restore
目录应包含每个提供的备份的目录。 这允许您检查目录、检查备份的内容,并决定您希望准备和恢复哪些备份。
完成后保存并关闭文件。 之后,通过键入以下命令确保脚本是可执行的:
sudo chmod +x /usr/local/bin/extract-mysql.sh
该脚本将允许我们将单个备份文件展开到恢复所需的目录结构中。
创建 prepare-mysql.sh 脚本
最后,在 /usr/local/bin
目录下下载或创建 prepare-mysql.sh
脚本。 此脚本会将日志应用到每个备份以创建一致的数据库快照。 它将任何增量备份应用于完整备份以合并以后的更改。
如果您之前没有下载脚本文件,请在文本编辑器中创建它:
sudo nano /usr/local/bin/prepare-mysql.sh
在里面,粘贴以下内容:
/usr/local/bin/prepare-mysql.sh
#!/bin/bash export LC_ALL=C shopt -s nullglob incremental_dirs=( ./incremental-*/ ) full_dirs=( ./full-*/ ) shopt -u nullglob backup_owner="backup" log_file="prepare-progress.log" full_backup_dir="${full_dirs[0]}" # Use this to echo to standard error error() { printf "%s: %s\n" "$(basename "${BASH_SOURCE}")" "${1}" >&2 exit 1 } trap 'error "An unexpected error occurred. Try checking the \"${log_file}\" file for more information."' ERR sanity_check () { # Check user running the script if [ "${USER}" != "${backup_owner}" ]; then error "Script can only be run as the \"${backup_owner}\" user." fi # Check whether a single full backup directory are available if (( ${#full_dirs[@]} != 1 )); then error "Exactly one full backup directory is required." fi } do_backup () { # Apply the logs to each of the backups printf "Initial prep of full backup %s\n" "${full_backup_dir}" xtrabackup --prepare --apply-log-only --target-dir="${full_backup_dir}" for increment in "${incremental_dirs[@]}"; do printf "Applying incremental backup %s to %s\n" "${increment}" "${full_backup_dir}" xtrabackup --prepare --apply-log-only --incremental-dir="${increment}" --target-dir="${full_backup_dir}" done printf "Applying final logs to full backup %s\n" "${full_backup_dir}" xtrabackup --prepare --target-dir="${full_backup_dir}" } sanity_check && do_backup > "${log_file}" 2>&1 # Check the number of reported completions. Each time a backup is processed, # an informational "completed OK" and a real version is printed. At the end of # the process, a final full apply is performed, generating another 2 messages. ok_count="$(grep -c 'completed OK' "${log_file}")" if (( ${ok_count} == ${#full_dirs[@]} + ${#incremental_dirs[@]} + 1 )); then cat << EOF Backup looks to be fully prepared. Please check the "prepare-progress.log" file to verify before continuing. If everything looks correct, you can apply the restored files. First, stop MySQL and move or remove the contents of the MySQL data directory: sudo systemctl stop mysql sudo mv /var/lib/mysql/ /tmp/ Then, recreate the data directory and copy the backup files: sudo mkdir /var/lib/mysql sudo xtrabackup --copy-back --target-dir=${PWD}/$(basename "${full_backup_dir}") Afterward the files are copied, adjust the permissions and restart the service: sudo chown -R mysql:mysql /var/lib/mysql sudo find /var/lib/mysql -type d -exec chmod 750 {} \\; sudo systemctl start mysql EOF else error "It looks like something went wrong. Check the \"${log_file}\" file for more information." fi
该脚本在当前目录中查找以 full-
或 incremental-
开头的目录。 它使用 MySQL 日志将提交的事务应用于完整备份。 之后,它将任何增量备份应用于完整备份,以使用更新的信息更新数据,再次应用提交的事务。
合并所有备份后,将回滚未提交的事务。 此时,full-
备份将代表一组一致的数据,可以移动到 MySQL 的数据目录中。
为了最大程度地减少数据丢失的可能性,该脚本不会将文件复制到数据目录中。 这样,用户可以手动验证备份内容和在此过程中创建的日志文件,并决定如何处理 MySQL 数据目录的当前内容。 命令退出时会显示完全恢复文件所需的命令。
完成后保存并关闭文件。 如果您之前没有这样做,请通过键入以下命令将文件标记为可执行文件:
sudo chmod +x /usr/local/bin/prepare-mysql.sh
这个脚本是我们在将备份文件移动到 MySQL 的数据目录之前运行的最后一个脚本。
测试 MySQL 备份和恢复脚本
现在备份和恢复脚本在服务器上,我们应该测试它们。
执行完整备份
首先使用 backup
用户调用 backup-mysql.sh
脚本:
sudo -u backup backup-mysql.sh
OutputBackup successful! Backup created at /backups/mysql/Thu/full-04-20-2017_14-55-17.xbstream
如果一切按计划进行,脚本将正确执行,指示成功,并输出新备份文件的位置。 如上面的输出所示,已经创建了一个每日目录(在本例中为“Thu”)来存放当天的备份。 备份文件本身以 full-
开头,表示这是一个完整备份。
让我们进入每日备份目录并查看内容:
cd /backups/mysql/"$(date +%a)" ls
Outputbackup-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
文件,其中包含有关备份的元数据up 内容,以及包含附加元数据的 xtrabackup_info
文件。
如果我们跟踪 backup-progress.log
,我们可以确认备份成功完成。
tail backup-progress.log
Output170420 14:55:19 All tables unlocked 170420 14:55:19 [00] Compressing, encrypting and streaming ib_buffer_pool to <STDOUT> 170420 14:55:19 [00] ...done 170420 14:55:19 Backup created in directory '/backups/mysql/Thu/' 170420 14:55:19 [00] Compressing, encrypting and streaming backup-my.cnf 170420 14:55:19 [00] ...done 170420 14:55:19 [00] Compressing, encrypting and streaming xtrabackup_info 170420 14:55:19 [00] ...done xtrabackup: Transaction log of lsn (2549956) to (2549965) was copied. 170420 14:55:19 completed OK!
如果我们查看 xtrabackup_checkpoints
文件,我们可以查看有关备份的信息。 虽然此文件提供了一些对管理员有用的信息,但它主要用于后续备份作业,以便他们知道哪些数据已被处理。
这是包含在每个存档中的文件的副本。 即使此副本被每个备份覆盖以表示最新信息,但每个原始文件仍将在备份存档中可用。
cat xtrabackup_checkpoints
Outputbackup_type = full-backuped from_lsn = 0 to_lsn = 2549956 last_lsn = 2549965 compact = 0 recover_binlog_info = 0
上面的示例告诉我们进行了完整备份,并且备份涵盖了日志序列号 (LSN) 0 到日志序列号 2549956。 last_lsn
编号表示备份过程中发生了一些操作。
执行增量备份
现在我们有了完整备份,我们可以进行额外的增量备份。 增量备份记录自上次执行备份以来所做的更改。 第一次增量备份基于完整备份,后续增量备份基于之前的增量备份。
在进行另一个备份之前,我们应该将一些数据添加到我们的数据库中,以便我们可以知道哪些备份已被应用。
在我们的 playground
数据库的 equipment
表中插入另一条记录,代表 10 个黄色波动。 在此过程中,系统将提示您输入 MySQL 管理密码:
mysql -u root -p -e 'INSERT INTO playground.equipment (type, quant, color) VALUES ("swing", 10, "yellow");'
既然当前数据比我们最近的备份多,我们可以进行增量备份来捕获更改。 如果存在同一天的完整备份,则 backup-mysql.sh
脚本将进行增量备份:
sudo -u backup backup-mysql.sh
OutputBackup successful! Backup created at /backups/mysql/Thu/incremental-04-20-2017_17-15-03.xbstream
再次检查每日备份目录以找到增量备份存档:
cd /backups/mysql/"$(date +%a)" ls
Outputbackup-progress.log incremental-04-20-2017_17-15-03.xbstream xtrabackup_info full-04-20-2017_14-55-17.xbstream xtrabackup_checkpoints
xtrabackup_checkpoints
文件的内容现在指的是最近的增量备份:
cat xtrabackup_checkpoints
Outputbackup_type = incremental from_lsn = 2549956 to_lsn = 2550159 last_lsn = 2550168 compact = 0 recover_binlog_info = 0
备份类型被列为“增量”,而不是像我们的完整备份那样从 LSN 0 开始,它从我们上次备份结束的 LSN 开始。
提取备份
接下来,让我们提取备份文件以创建备份目录。 出于空间和安全方面的考虑,这通常只应在您准备好恢复数据时进行。
我们可以通过将 .xbstream
备份文件传递给 extract-mysql.sh
脚本来提取备份。 同样,这必须由 backup
用户运行:
sudo -u backup extract-mysql.sh *.xbstream
OutputExtraction complete! Backup directories have been extracted to the "restore" directory.
以上输出表明该过程已成功完成。 如果我们再次查看每日备份目录的内容,则会创建一个extract-progress.log
文件和一个restore
目录。
如果我们跟踪提取日志,我们可以确认最新备份已成功提取。 其他备份成功消息显示在文件的前面。
tail extract-progress.log
Output170420 17:23:32 [01] decrypting and decompressing ./performance_schema/socket_instances.frm.qp.xbcrypt 170420 17:23:32 [01] decrypting and decompressing ./performance_schema/events_waits_summary_by_user_by_event_name.frm.qp.xbcrypt 170420 17:23:32 [01] decrypting and decompressing ./performance_schema/status_by_user.frm.qp.xbcrypt 170420 17:23:32 [01] decrypting and decompressing ./performance_schema/replication_group_members.frm.qp.xbcrypt 170420 17:23:32 [01] decrypting and decompressing ./xtrabackup_logfile.qp.xbcrypt 170420 17:23:33 completed OK! Finished work on incremental-04-20-2017_17-15-03.xbstream
如果我们移动到 restore
目录,与我们提取的备份文件对应的目录现在可用:
cd restore ls -F
Outputfull-04-20-2017_14-55-17/ incremental-04-20-2017_17-15-03/
备份目录包含原始备份文件,但它们尚未处于 MySQL 可以使用的状态。 为了解决这个问题,我们需要准备文件。
准备最终备份
接下来,我们将准备备份文件。 为此,您必须位于包含 full-
和任何 incremental-
备份的 restore
目录中。 该脚本会将任何 incremental-
目录中的更改应用到 full-
备份目录。 之后,它将应用日志来创建 MySQL 可以使用的一致数据集。
如果出于任何原因您不想恢复某些更改,现在是您从 restore
目录中删除这些增量备份目录的最后机会(增量备份文件仍将在父目录中可用) . 当前目录中剩余的 incremental-
目录将应用于 full-
备份目录。
准备好后,调用 prepare-mysql.sh
脚本。 同样,确保您位于各个备份目录所在的 restore
目录中:
sudo -u backup prepare-mysql.sh
OutputBackup looks to be fully prepared. Please check the "prepare-progress.log" file to verify before continuing. If everything looks correct, you can apply the restored files. First, stop MySQL and move or remove the contents of the MySQL data directory: sudo systemctl stop mysql sudo mv /var/lib/mysql/ /tmp/ Then, recreate the data directory and copy the backup files: sudo mkdir /var/lib/mysql sudo xtrabackup --copy-back --target-dir=/backups/mysql/Thu/restore/full-04-20-2017_14-55-17 Afterward the files are copied, adjust the permissions and restart the service: sudo chown -R mysql:mysql /var/lib/mysql sudo find /var/lib/mysql -type d -exec chmod 750 {} \; sudo systemctl start mysql
上面的输出表明脚本认为备份已完全准备好,并且 full-
备份现在表示完全一致的数据集。 如输出所示,您应该检查 prepare-progress.log
文件以确认在此过程中没有报告错误。
该脚本实际上没有将文件复制到 MySQL 的数据目录中,以便您可以验证一切看起来是否正确。
将备份数据恢复到 MySQL 数据目录
如果您在查看日志后对一切正常感到满意,则可以按照 prepare-mysql.sh
输出中列出的说明进行操作。
首先,停止正在运行的 MySQL 进程:
sudo systemctl stop mysql
由于备份的数据可能与 MySQL 数据目录的当前内容冲突,我们应该删除或移动 /var/lib/mysql
目录。 如果文件系统上有空间,最好的选择是将当前内容移动到 /tmp
目录或其他地方,以防出现问题:
sudo mv /var/lib/mysql/ /tmp
重新创建一个空的 /var/lib/mysql
目录。 我们需要马上修复权限,所以我们还不需要担心:
sudo mkdir /var/lib/mysql
现在,我们可以使用 xtrabackup
实用程序将完整备份复制到 MySQL 数据目录。 在以下命令中替换您准备好的完整备份的路径:
sudo xtrabackup --copy-back --target-dir=/backups/mysql/Thu/restore/full-04-20-2017_14-55-17
正在复制的文件的运行日志将在整个过程中显示。 一旦文件到位,我们需要再次修复所有权和权限,以便 MySQL 用户和组拥有并可以访问恢复的结构:
sudo chown -R mysql:mysql /var/lib/mysql sudo find /var/lib/mysql -type d -exec chmod 750 {} \;
我们恢复的文件现在位于 MySQL 数据目录中。
再次启动 MySQL 以完成该过程:
sudo systemctl start mysql
通过查看playground.equipment
表的内容来检查数据是否已经恢复。 同样,系统将提示您输入 MySQL root
密码以继续:
mysql -u root -p -e 'SELECT * FROM playground.equipment;'
Output+----+-------+-------+--------+ | id | type | quant | color | +----+-------+-------+--------+ | 1 | slide | 2 | blue | | 2 | swing | 10 | yellow | +----+-------+-------+--------+ 2 rows in set (0.02 sec)
我们的数据已成功恢复。
恢复数据后,请务必返回并删除 restore
目录。 未来的增量备份一旦准备好就无法应用于完整备份,因此我们应该将其删除。 此外,出于安全原因,备份目录不应在磁盘上未加密:
cd ~ sudo rm -rf /backups/mysql/"$(date +%a)"/restore
下次我们需要备份目录的干净副本时,我们可以再次从备份文件中提取它们。
创建 Cron 作业以每小时运行一次备份
现在我们已经验证了备份和恢复过程运行顺利,我们应该设置一个 cron
作业来自动进行定期备份。
我们将在 /etc/cron.hourly
目录中创建一个小脚本来自动运行我们的备份脚本并记录结果。 cron
进程将每小时自动运行一次:
sudo nano /etc/cron.hourly/backup-mysql
在内部,我们将使用 systemd-cat
实用程序调用备份脚本,以便输出在日志中可用。 我们将用 backup-mysql
标识符标记它们,以便我们可以轻松过滤日志:
/etc/cron.hourly/backup-mysql
#!/bin/bash sudo -u backup systemd-cat --identifier=backup-mysql /usr/local/bin/backup-mysql.sh
完成后保存并关闭文件。 通过键入以下命令使脚本可执行:
sudo chmod +x /etc/cron.hourly/backup-mysql
备份脚本现在将每小时运行一次。 该脚本本身将负责清理三天前的备份。
我们可以通过手动运行来测试 cron
脚本:
sudo /etc/cron.hourly/backup-mysql
完成后,通过键入以下内容检查日志中的日志消息:
sudo journalctl -t backup-mysql
Output-- Logs begin at Wed 2017-04-19 18:59:23 UTC, end at Thu 2017-04-20 18:54:49 UTC. -- Apr 20 18:35:07 myserver backup-mysql[2302]: Backup successful! Apr 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 和系统备份用户,设置了加密密钥来保护我们的备份文件,然后设置脚本来自动化部分备份和恢复过程。
备份脚本在每天开始时生成完整备份,之后每小时生成增量备份,随时保留三天的备份。 加密文件和加密密钥可与其他备份技术结合使用,将数据传输到异地以进行安全保存。 提取和准备脚本让我们将当天的备份组合成一组一致的数据,可用于恢复系统。