下厨房基于 JuiceFS 的 MySQL 备份实践

2018-04-18
苏锐

今天这篇文章来自 ushuz (GitHub),他 2014 年加入下厨房,目前负责下厨房平台和基础设施方面的工作。

下厨房是 JuiceFS 第一批客户之一,从最早用 JuiceFS 做 Nginx 日志和 MySQL 备份,到现在将 JuiceFS 用作 Hadoop / Hive / HBase 的主力存储,使用场景已经非常广泛。今天的这篇文章的实践性非常强,最早听说他们利用 JuiceFS Snapshot 特性做 MySQL 备份验证的时候,我们自己也觉得这个脑洞特别赞!

下面请看文章:

下厨房 是国内最大的专注于家庭美食领域的社区,以菜谱和作品分享为核心,业务涉及电商、付费内容、短视频等,目前拥有超过 2000 万注册用户,全平台日活接近 300 万,用户上传了超过 100 万道菜谱、接近 4000 万个作品,赞和收藏量均接近 10 亿。

下厨房从创立已经走过 7 年时间,公司和业务都经历了很多的变化和成长,到现在仍然保持着一个精简的团队,用最务实的方案来支撑业务增长,借此机会和大家分享一下小团队如何高效构建数据库冗余、备份及备份验证机制。

数据库架构概览

下厨房使用的是最常见的主备架构。

在迁移至云服务之前,我们使用 2 台物理机专门作为数据库机器,每台物理机上只运行 1 个 MySQL 实例,按业务划分 database,2 个实例互为主备。迁移至云服务后,对 database 按负载和业务可用性要求进行了分组。每组包括至少 2 个 MySQL 实例(每台云主机只运行 1 个实例)互为主备,分别放在不同的可用区,以减少因出现火灾、雷击等致命事故导致数据丢失的可能性。对于负载较大的分组,增设了若干只读实例,通过部分读写分离分担负载。

随着业务增长,我们的数据库规模也在不断增加,目前大概有 10 个主备对,总数据量近 2T,最大表有近 10 亿行,峰值数据库总 QPS 接近 15 万。

目前使用数据库的主要姿势有:

冗余和备份

冗余不做,日子甭过;备份不做,十恶不赦! – Xin Li (@delphij) 2010年1月6日

下厨房对此深有体会。在 2013 年 6 月误删数据库文件后,由于冗余和备份中断长达 2 个月,不得不花费了大量时间精力从硬盘恢复数据。

冗余和备份是数据库管理工作的重中之重:一方面天有不测风云,从手抖删库到自然灾害,都可能导致数据库数据损失;另一方面数据是一家互联网公司最重要的资产,留得青山在,不怕没柴烧,但如果青山没了,恐怕就得散伙了。

有惨痛教训在前,我们对冗余和备份格外重视,以最大限度避免数据丢失。在实践中,Percona XtraBackupJuiceFS 两款工具为我们带来了极大便利。

冗余方面,跨可用区主备架构提供了第一层保障,即使主节点数据库文件被误删、宿主机灭失、甚至所在可用区灭失,备节点也仍保有几乎全部数据。Percona XtraBackup 支持热备份,主节点无需停机,简单几步操作即可创建一个 slave,让架设主备变得得心应手。

# on master, take a backup to /backup/mysql
 root@master $ juicefs mount <volume> /backup
 root@master $ innobackupex --no-timestamp --slave-info /backup/mysql/

 # on slave, copy back the backup
 # before copying, shutdown mysql on slave
 root@slave $ juicefs mount <volume> /backup
 root@slave $ rsync -avP /backup/mysql/ /path/to/mysql/datadir

 # prepare the backup
 # it’s faster to apply logs on SSD than on a network filesystem (in our case JuiceFS)
 root@slave $ innobackupex --apply-log --use-memory=16G /path/to/mysql/datadir

 # make sure datadir ownership
 # then start mysql on slave, setup replication based on /backup/mysql/xtrabackup_binlog_info
 # if the backup was taken from a slave, use /backup/mysql/xtrabackup_slave_info
 root@slave $ chown -R mysql:mysql /path/to/mysql/datadir

备份方面,每天定时备份整库及 binlog 提供了第二层保障,在第一层保障失效,如手抖误 DROP 时,仍能恢复全部数据。但是,整库备份体积较大,每日定时备份产生的数据量相当可观。JuiceFS 依托对象存储提供了近乎无限的存储空间,十分适合备份的场景,这样一来可以按需长期保留备份而不必过多担心空间占用。我们目前的策略是保留 7 天内的 Percona XtraBackup 整库备份、3 年内的 binlog 以及 1 年内的周级 mysqldump。

相比于自建 NFS,JuiceFS 是与对象存储同等级的高可用服务,它还可以实现跨云的数据冗余,便于跨地域甚至跨云传输。跨地域或跨云访问 JuiceFS 时是通过对象存储的公网出口来传输数据,带宽十分充足,我们曾在 AWS 北京从基于 UCloud UFile 的 JuiceFS 中拷贝数据,传输速度能达到 800+Mbps。

备份验证

终于明白为啥我们公司的数据库备份有检查大小的 alert 了 – 荡师傅 (@inntran) 2016年8月13日

定时备份不意味着高枕无忧,因为备份也可能出问题,因此验证备份是十分必要的。检查备份的大小是一种最简单的方法,但对于数据库备份而言,备份是否真的可用,只有用备份进行一次恢复才知道。一次恢复成功的标志是:MySQL 进程启动且 replication 正常。

常规恢复流程中需要拷贝备份,对于体积较大的备份,拷贝过程会耗时很久,用于运行测试的目标机器也要有足够的硬盘空间,这样的话备份测试的时间成本相当高。

好在 JuiceFS 提供了快照(snapshot)功能,可以为 JuiceFS 上某个路径快速创建一份快照,对快照进行的修改不会影响原路径下的文件。利用 JuiceFS 的快照功能,可以节约大量时间成本。

下厨房基于 Docker 和 JuiceFS 快照构建了一套简单易用的备份测试方案:主要包括在云主机上运行的,执行创建快照、启动容器及清理快照的 verify-backup.sh

#!/usr/bin/env bash
juicefs snapshot /backup/mysql /backup/snapshot

# run a percona:5.7 container to verify the backup
# make sure container can reach master with --add-host or --network
docker run --rm \
    -v /path/to/my-cnf:/root/.my.cnf:ro \
    -v /path/to/report.sh:/report.sh:ro \
    -v /backup/snapshot:/var/lib/mysql \
    percona:5.7 \
    /report.sh

juicefs snapshot -d /backup/snapshot

以及在容器中运行的,执行准备备份、配置 replication 并报告 replication 状态的 report.sh

#!/usr/bin/env bash
set -e

# switch to domestic mirrors
sed -i 's|deb.debian.org/debian|ftp.cn.debian.org/debian|g' /etc/apt/sources.list
sed -i 's|security.debian.org|ftp.cn.debian.org/debian-security|g' /etc/apt/sources.list

# install percona-xtrabackup-24
apt-get update && apt-get install -y percona-xtrabackup-24

# prepare backup
innobackupex --apply-log --use-memory=16G /var/lib/mysql > /dev/null
chown -R mysql:mysql /var/lib/mysql

# start mysql
mysqld \
  --default-storage-engine=InnoDB \
  --character-set-server=utf8mb4 \
  --collation-server=utf8mb4_unicode_ci \
  --server-id=1234 \
  --gtid-mode=on \
  --enforce-gtid-consistency=on \
  --read-only=on \
  --transaction-isolation=READ-COMMITTED \
  --binlog-format=ROW \
  --master-info-repository=TABLE \
  --relay-log-info-repository=TABLE \
  --log-bin=/var/log/mysql/mysql-bin \
  --relay-log=mysqld-relay-bin \
  --log-error=/var/log/mysql/error.log \
  --innodb-lru-scan-depth=256 \
  --disable-partition-engine-check &

# check mysql ready, timeout 300s
# it seems that commands inside loop aren't affected by "set -e"
n=0
until [ $n -ge 100 ]; do
  mysqladmin ping && break
  n=$[$n+1]
  sleep 3
done

# setup gtid, connect to mysql server using /root/.my.cnf to avoid password input
mysql < <(printf "RESET SLAVE;\nRESET MASTER;\n$(cat /var/lib/mysql/xtrabackup_slave_info);\nSTART SLAVE;\n")

# check replication status
#   Slave_IO_Running / Slave_SQL_Running should be Yes
#   Seconds_Behind_Master should be reasonable
sleep 10
mysql -e 'SHOW SLAVE STATUS\G' | grep -E '(Running:|Seconds)'

# report and exit
exit $?

在备份完成后,通过定时任务,或其他方式触发 verify-backup.sh 运行。如果备份验证成功,脚本应打印出 MySQL replication 线程的状态及落后于 master 的时间,replication 线程应都处于 Running 状态,且落后于 master 的时间应在合理范围内。

Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
        Seconds_Behind_Master: 3625

另外鉴于备份及备份验证任务的特殊性和重要性,需要额外关注下通知策略。如果只在报错时通知,那当没有收到通知时可能是任务运行成功,也可能是任务根本没有运行。对于备份及备份验证任务而言,没有运行也算任务失败。

总结

下厨房技术团队规模不大,技术资源较为有限。但是借助 Percona 全家桶及 JuiceFS,他们快速地实现了数据库冗余和备份,以及一套有效易用的备份验证方案,从而使他们可以更加专注且有信心地推进业务开发,助力业务增长。

这篇原文发布在下厨房技术团队博客,也欢迎其他 MySQL DBA 和对基础设施感兴趣的工程师一起探讨,可以通过网站右下角的聊天窗口联络我们,或者发邮件到 [email protected]

相关博客

JuiceFS 用户必备的 6 个技巧

2023-11-22
随着大数据、AI 技术的发展,越来越多的企业、团队和个人开始使用 JuiceFS,本文整理了 6 个超实用的 JuiceFS 技巧,帮助大家提升 JuiceFS 的管理效率。

40+ 倍提升,详解 JuiceFS 元数据备份恢复性能优化方法

2022-07-13 执剑
JuiceFS 是一款云原生分布式文件系统。对元数据进行迁移和备份是使用中的一个关键场景,随着用户元数据的数据量越来越大,有些重度用户的元数据甚至可达到亿级水平,元数据的迁移和备份的性能备受挑战。在…

JuiceFS v1.0 RC1 发布,大幅优化 dump/load 命令性能, 深度用户不容错过

2022-06-16 Juicedata
JuiceFS v1.0 RC1 今天正式发布了!这个版本中,最值得关注的是对元数据迁移备份工具 dump/load 的优化。