分布式数据库OceanBase备份恢复
OceanBase 数据库是一款完全自研的企业级原生分布式数据库,在普通硬件上实现金融级高可用,首创“三地五中心”城市级故障自动无损容灾新标准,刷新 TPC-C 标准测试,单集群规模超过 1500 节点,具有云原生、强一致性、高度兼容 Oracle/MySQL 等特性。

整体架构:

面向海量事务处理的分布式数据库系统 OceanBase 数据库采用了 Zone(可用区)的概念,每个 Zone 是一个机房内的一组服务器,包含多台 OceanBase 数据库服务器(OBServer)。每台 OBServer 包含 SQL 引擎、事务引擎和存储引擎,并服务多个数据分区,其中,每个 Zone 有一台 OBServer 会同时运行总控服务(RootService),用于执行集群管理、服务器管理、自动负载均衡等操作。OBServer 上会运行 SQL 引擎、事务引擎和存储引擎,用户的 SQL 查询经过 SQL 引擎解析优化后转化为事务引擎和存储引擎的内部调用。对于跨服务器操作,OceanBase 数据库还会执行强一致的分布式事务,从而在分布式集群上实现数据库事务 ACID。
OceanBase 数据库采用 Shared-Nothing 架构,各个节点之间完全对等,每个节点都有自己的 SQL 引擎、事务引擎、存储引擎,运行在普通 PC 服务器组成的集群之上,具备可扩展、高可用、高性能、低成本等核心特性。
主旨在获得高可用,高容灾,高灵活,高扩展,低成本等高效的业务能力,进而形成全新的业务灾备体系。
备份恢复是 OceanBase 数据库高可用特性的核心组件,主要用于保障数据的安全,包括预防存储介质损坏和用户的错误操作等。如果存储介质损坏或者用户误操作而导致了数据丢失,通过恢复的方式恢复用户的数据。
目前 OceanBase 数据库支持 OSS、NFS 和 COS 三种备份介质,提供备份、恢复、管理三大功能。OceanBase 数据库支持集群级别的物理备份。物理备份由基线数据、日志归档数据两种数据组成,因此物理备份由日志归档和数据备份两个功能组合而成:
日志归档是指日志数据的自动归档功能,OBServer 会定期将日志数据归档到指定的备份路径。这个动作是全自动的,不需要外部定期触发。
数据备份指的是备份基线数据的功能,该功能分为全量备份和增量备份两种:
全量备份是指备份所有的需要基线的宏块。
增量备份是指备份上一次备份以后新增和修改过的宏块。
OceanBase 数据库支持租户级别的恢复,恢复是基于已有数据的备份重建新租户的过程。用户只需要一个alter system restore tenant命令,就可以完成整个恢复过程。恢复过程包括租户系统表和用户表的 Restore 和 Recover 过程。Restore 是将恢复需要的基线数据恢复到目标租户的 OBServer,Recover 是将基线对应的日志恢复到对应 OBServer。OceanBase 数据库目前支持手动删除指定的备份和自动过期备份的功能。
物理备份架构
OceanBase 数据库物理备份的架构如下图所示。

当用户用系统租户登录到备份集群以后,需要先用 SQL 发起日志归档,等日志归档发起完成启动阶段以后,才可以发起基线备份。
日志归档是定期备份到备份目的端的,只需要用户发起一次alter system archivelog,日志备份就会在后台持续进行。日志归档是由每个 PG(PartitionGroup)的 Leader 负责定期将该 PG 的日志归档到备份介质指定的路径,RS(RootService)负责定期统计日志归档的进度,并更新到内部表。
数据备份是需要用户触发的,比较常见的场景是周六触发一次全量备份,周二和周四触发一次增量备份。当用户发起数据备份请求时,该请求会首先被转发到 RS 所在的节点上;RS 会根据当前的租户和租户包含的 PG 生成备份数据的任务,然后把备份任务分发到 OBServer 上并行地执行备份任务;OBServer 负责备份 PG 的元信息和宏块到指定的备份目录,宏块按照 PG 为单位进行管理。
OceanBase 数据库目前支持使用 OSS、NFS 和 COS 三种文件系统作为备份的目的地
备份功能在备份目的地创建的目录结构以及每个目录下保存的文件类型
backup/ # 备份的根目录
└── ob1 # cluster_name
└── 1 # cluster_id
└── incarnation_1 #分身 ID
├── 1001 # 租户id
│ ├── clog # Clog 的根目录
│ │ ├── 1 # Clog 备份的 Round ID
│ │ │ ├── data # 日志的数据目录
│ │ │ └── index # 日志的索引目录
│ │ └── tenant_clog_backup_info # 日志备份的元信息,按照 Round ID 分段记录
│ └── data # 数据的根目录
│ ├── backup_set_1 # 全量备份的目录
│ │ ├── backup_1 # 差异备份的目录,第一个差异备份目录是全量的 Meta
│ │ ├── backup_2 # 差异备份的目录。第二个差异备份的目录,Meta 也是全量备份的。
│ │ ├── backup_set_info # 记录了 backup_set 目录内的多次差异备份的信息
│ │ └── data #宏块数据的目录,包含了所有的全量和差异的宏块
│ └── tenant_data_backup_info # 记录了租户全部的数据备份信息
├── clog_info # server启动日志备份的信息
│ └── 1_10.10.10.1_12533 # 一个 Server 一个启动日志备份信息
├── cluster_clog_backup_info # 集群级别的日志备份信息
├── cluster_data_backup_info # 集群级别的数据备份信息
├── tenant_info # 租户的信息
└── tenant_name_info #租户 Name 和 ID 的影射关系
物理恢复架构
OceanBase 数据库的物理恢复架构如下图所示。

用户可见的流程主要:在目的集群上用CREATE RESOURCE POOL命令建立恢复租户需要的资源池。通过ALTER SYSTEM RESTORE TENANT命令调度租户恢复任务。对于备份恢复来说,RESTORE TENANT命令的内部流程如下:
创建恢复用的租户。
恢复租户的系统表数据。
恢复租户的系统表日志。
调整恢复租户的元信息。
恢复租户的用户表数据。
恢复租户的用户表日志。
恢复扫尾工作。
对于单个 PG 来说,恢复的流程就是将 PG 的元信息和宏块数据拷贝到指定的 OBServer,构建出一个只有基线数据的 PG;然后再把 PG 的日志拷贝到指定的 OBServer,回放到该 PG 的 MemTable 中。这个流程中如果日志的量比较大,可能会触发转储操作。
Backup Set
OceanBase 数据库的数据备份是用 BackupSet 来管理,一次数据备份会生成一个对应的 backup_set_id,这个 ID 在集群级别是全局唯一的。
对于全量备份来说,会建立一个独立的元信息目录和一个宏块的目录;对于增量备份来说,会建立一个独立的元信息目录,但是会重用对应全量备份的宏块目录。OceanBase 数据库的增量备份包含了完整的元信息,只重用的宏块数据,不会影响恢复的性能。
备份管理的时候全量备份和其对应的若干增量备份是作为一个管理单元的,不允许单独删除一组备份里面的单个增量备份或者单个全量备份。
Archive Log Round
OceanBase 数据库的日志归档是连续备份的,不受 Backup Set 的管理。每次执行 ALTER SYSTEM ARCHIVELOG 发起日志归档的时候,会将 Archive Log Round 加 1 。 一个 Archive Log Round 表示一个完整连续的日志备份。日志归档没有单独的备份管理命令,每次删除数据备份的 Backup Set 的时候,会自动将不再需要的日志归档文件删除。如果一个 Archive Log Round 比现存的所有 Backup Set 都旧,那么整个 Archive Log Round 都会被删除。
核心指标定义与OceanBase支持能力
RTO与RPO是衡量备份策略的两大核心指标。RTO代表故障发生后系统恢复的最长可接受时间,RPO则是数据丢失的最大可容忍窗口。根据ObBackupDataStruct定义,OceanBase通过多级索引结构实现不同粒度的备份控制:
基线数据的备份
OceanBase的基线数据是合并时生成的、硬盘上的用户数据,并且仅在合并时发生变化。顾名思义,基线数据的备份指的是对这一部分数据的备份。备份恢复中执行基线备份的模块叫做AgentServer ,基线备份的架构图。
Meta DB的表中记录着基线备份任务的控制信息,AgentServer定期去任务总控表(base_data_backup)中查询,以获取基线备份任务;在获取任务之后,AgentServer会将基线备份任务按照一定规则拆分为若干子任务,将子任务插入到子任务表(base_data_backup_task)中,随后每个子任务由一个相关线程来执行,执行时随机选择延时低的从库(Follower),通过RPC调用来读取宏块数据,经过处理后异步写入到备份介质中,直到所有子任务都完成之后,任务记录被移动到历史表(base_data_backup_task_history)中。基线数据中既包含逻辑备份数据,也包含物理备份数据,其中包括建表语句等在内的DDL以逻辑的方式备份,用户数据则以物理的方式(即文件复制)备份,在备份数据中,也有记录备份进度元信息的索引文件。
基线备份通过RPC端口来拉取基线数据,而不是SQL接口,即无法通过OBProxy来进行备份,因此要求AgentServer能够直接访问到OBServer的RPC端口,建议AgentServer与OBServer部署在同一网络内。由于OceanBase架构的特点,在两次合并之间基线数据都不会发生变化。基线备份在发生合并时会被挂起,直到合并结束之后继续进行,这时候会检查已经备份的宏块,如果在新的合并中没有发生变化,则保留,否则会重新备份。为了使基线备份尽快完成,建议将基线备份的发起时间设置为合并完成之后,避免基线备份收到干扰由于合并被挂起而影响备份效率。如果一个集群的基线备份由于数据量太大而不能高效完成,可以将集群备份任务按照租户拆分为较小的任务,同时可以对备份服务进行横向扩展,即多个服务器上部署备份服务AgentServer,一起对指定集群的基线数据进行备份。
基线数据是按照合并的版本来分的,即同一个版本只需备份一次,同一版本备份多次不仅没有意义,还会对服务器负载、带宽等造成影响,因此基线备份的频率应该小于或等于合并的频率。通常来说,OceanBase在每天晚上发生一次合并,而基线数据的备份一天或者多于一天进行一次即可。具体的频率可视业务需求而定,但同时需要考虑两次基线备份之间的时间相隔越长,那么恢复到这两个版本中间某一时间点所需的时间可能就越多。
恢复概述
OceanBase的恢复过程包含了基线恢复和增量恢复。恢复的最小粒度为租户,用户可以指定恢复结束的具体时间点,恢复程序会找到这个时间点前最近的一个基线备份,基于该版本的基线备份执行基线恢复;等基线恢复完成后,再从基线数据生成的时间点开始执行增量恢复,直至用户指定的恢复结束时间完成增量恢复。等增量恢复完成后,整个恢复任务完成。基线恢复和增量恢复二者密不可分,不能只执行基线恢复而不执行增量恢复,也不能跳过基线恢复只执行增量恢复。
基线数据恢复
用户在启动恢复时,可以指定恢复具体的版本,也可以不指定版本。不指定版本时,恢复程序会去查找基线备份数据中离用户指定的恢复结束之前最近的一个成功的基线备份版本作为恢复的版本。
执行恢复的模块叫做AgentRestore,这是一个Java程序。程序启动后,会不断查询恢复任务总控表(oceanbase_restore),查找新的恢复任务。在获取新任务之后,会往基线恢复的控制表(base_data_restore)中插入一条数据,启动基线恢复。本质上来说,基线恢复是由恢复的目标集群来执行的。AgentRestore中的基线恢复线程发现基线恢复任务后,会向恢复目标集群发送一条执行基线恢复的命令:
ALTER SYSTEM RESTORE tenantFROM restoreUri
恢复的目标集群执行该命令即开始基线恢复,通过读取备份的基线数据来重构恢复租户,基线恢复的架构图所示。

基线恢复由恢复目标集群的总控服务(RootServer)执行,通过读取基线备份中的DDL来重构原租户的元数据,通过读取和复制备份中的宏块来恢复原租户的数据。基线恢复的进度、结果和历史信息记录在恢复目标集群sys租户的相关表中,如图中所示。AgentRestore不断通过相关表获取基线恢复任务的状态,一旦基线恢复任务完成,立刻向增量恢复的任务控制表(inc_data_restore)中插入一条数据,启动增量恢复。
OceanBase的增量恢复由AgentRestore来执行,增量恢复的示意图

