从最近的mysql故障谈海量数据库的备份系统设计

前言

最近炉石传说,gitlab数据库故障在业界传得沸沸扬扬,造成了无法挽回的数据丢失。对于越是大规模的数据库系统, 如何设计一个可靠的备份系统越是至关重要。本文主要以UCloud云数据库产品UDB的备份系统为例,阐述下在海量数据库情况下,备份系统该如何设计,如何确保备份的安全性,如何避免数据库从删库到跑路的窘境。

大型备份系统设计

大型备份系统设计考虑因素

如果一个DBA只需要维护一套数据库,那么备份和恢复无疑是较为简单的,通常只需要知道怎么使用备份工具进行备份和还原就搞定了。而在大公司中,数据库实例数量往往都是成百上千个,公有云服务商甚至需要维护着十万级别的实例数量,简单粗糙的备份系统就显得有些low了,设计一个完善的备份系统尤其关键。一个大体量的备份系统需要考虑好这几个方面:

l  如何根据业务类型选择备份方式,逻辑备份or物理备份?

l  如何灵活地设置备份策略,比如备份周期选择,是否在从库备份,备份上传到何处

l  如何确认备份是否成功,哪一步骤出了问题

l  如何确认成功的备份实际可有效还原

l  需要恢复时,如何做到一键恢复到指定时间点

备份系统设计架构

下面以UCloud UDB为例,讲述备份系统的一般设计架构,来解决上述问题和需求。

UDB备份系统架构设计如下图所示:


其中Access模块是业务接入模块。它处理所有来自API或控制台的备份相关请求,并将请求转发到对应的实际物理机去执行备份或者恢复操作;备份机集群实际存放备份文件,它可能是独立的物理机集群,也可能是NFS等其他存储媒介,甚至是大规模分布式存储。在DB物理机上进行的备份完成后,备份系统会通过复杂的负载均衡算法把备份上传到合适的备份节点上。以保证对应的备份相对均匀地分布在备份机集群上。

备份元数据

大型备份系统的核心之一,是”备份元数据”的结构设计。备份系统通过与“备份元数据”所在的业务数据库交互,来决定备份的各种配置。所谓“备份元数据”,即每个数据库实例的备份信息,包括该实例的信息,备份的设置策略,备份结果的详细信息等。通过这个数据库备份系统将备份元数据和常用的与备份相关的流程串通起来。

通常来说,为了能够存储上述的“备份元数据”,一个海量的备份系统大致需要这样做表设计:一张表A存放实例的备份配置信息,一张表B存放所有实例的详细备份记录。表A和表B通过该数据库实例的唯一ID从业务上关联起来。一般这两张表所要保存的一些信息如下图所示。


结合备份系统表设计和备份系统架构设计图,大多数跟备份相关的业务流程就可以更加自动化可配置化。A表使得每一个DB的实例的备份都可以配置,并且也可以通过备份系统的后台模块智能分析一批UDB实例的备份策略并记录。而B表的存在,使得每一个备份的情况都能够进行精确跟踪。备份系统后台可以定期分析B表的记录,进行后续的处理,比如给用户告警。

几个常见的备份恢复流程,都可以通过整套后台系统和核心元数据的紧密配合来完成:

定时备份和手工备份:Access模块接收到请求后,通过读取表A的备份策略,将请求转发到备份DB所在的物理机去执行,备份完成后通过负载均衡算法上传远端的NFS或是备份机集群

备份失败检测:Access模块读取表B的备份详细信息和备份失败原因,将告警发出,并且经过分析之后,可以动态修改A表该RDS的备份配置。保证RDS的备份成功率

下载备份:Access模块接受到请求后读取表B的信息,生成一个可下载的url

基于时间点的恢复:Access模块接收到请求后,通过读取表A的记录获取该实例的配置信息,生成一个相同配置的可用资源,再将请求转发到这个可用资源(即恢复DB所在的物理机),在该物理机上新建出实例。接着通过读取表B的记录获取备份信息,读取表A的信息解析所需时间段的binlog,将备份和binlog导入新实例即完成恢复

通过备份新建一个RDS实例:步骤同上,略去了获取和导入binlog

通过备份新建一个备库实例:步骤同上,略去了获取和导入binlog,改为指定复制源

备份安全性保障

常规安全性策略

业界有句话:没有校验的备份等于没有备份。我对这句话也是理解深刻,曾经碰到过多次典型的情况比如备份时没有记录相应的binlog和pos点导致pointin time恢复时不好找准确的pos点,大大增加了恢复难度;再比如mysqldump逻辑备份时没有考虑到部分表采用utf8mb4字符集,导致恢复后的数据库乱码情况等。以UDB为例,大型备份系统主要从以下几个方面确保备份安全性

◎备份失败监控,一旦备份失败就会发送告警信息

◎定期抽取历史备份记录进行恢复测试

◎备份上传前后的MD5校验

◎备份过程中的全局读锁检测,卡住时间太长则发生告警信息

◎备份周期选择上确保两次间隔的binlog未过期

◎备份数据冗余,本地存一份最新的备份,异地存多天的历史备份

◎备份机集群空间检测和负载均衡,确保空间富余

◎备份重传和重试

其中,备份的有效性测试方法大概是这样的:启几个对应版本的数据库并行测试,通过备份详细记录表获取备份后将其传送到这几个实例进行恢复测试。由于实例数量较大,不可能每天都做恢复测试,通过读取上一次校验时间和校验结果计算测试优先级。越久没有做过验证的备份优先级越高,最近校验结果为失败的次数越多的实例,优先级也越高。

备份过程的全局读锁检测,是因为备份过程中为了记录当前的pos点信息,都会执行一个flush tables with read lock的操作,但是如果当时有长SQL未执行完就会导致全局锁等待,从而阻塞后续的所有写请求。为了使备份不影响业务,在备份开始后定期会检测全局锁等待,检测到以后kill长连接或者kill当前备份线程。

这些备份的常规检测都会在备份系统后台定期全自动执行,保证了备份的成功率。

除了上述的考虑,有些特殊情况下光有备份还不够。假设一个单实例UDB宕机,由于硬件故障物理机无法启动,物理机恢复时间茫茫无知。这种情况下由于无法获取最实时的binlog,仅仅通过历史备份只能恢复到备份时间点,而无法恢复到故障前的时间点。所以仅从安全性角度考虑,必要的备库都是不可忽略的存在。UDB出于客户配置灵活,经济成本的考虑,既支持只创建单实例RDS,也可以创建高可用RDS和跨机房的master-slave RDS,对于重要的业务,一般都建议至少配置有备库。

智能宕机恢复方法选择

数据库宕机有时也会造成数据损坏导致无法正常启动的情况,以MySQL innodb为例,一般这时需要做一次force recovery,但是这时的业务恢复时间评估也显得尤为重要。UDB可以智能选择在这种情况下采用何种恢复方式,即:

◎基于重新初始化DB+备份+binlog的恢复

◎force recovery启动+重新mysqldump+mv损坏数据+重新导入

主要基于以下几点考虑:

◎原数据量大小评估,数据量越大,重新mysqldump的时间会越长,但同样的历史备份传输+解压时间也会越长,需要找到一个平衡点

◎备份时间点到故障时间点的写入量评估,写入量越大,使用历史备份+binlog花的时间也会越长

◎恢复过程中适当提到当前RDS的IOPS上限

总结

有效的备份和恢复策略对于数据库安全的重要性不言而喻,而海量数据库的备份系统设计和实现却不是那么简单的一件事,需要考虑的细节还有很多。希望本文对于有意研发海量DB备份系统的童鞋有一些帮助。没有意向自研的童鞋,不妨考虑将数据库迁移上云,让强大的云数据库为您的数据保驾护航

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章