通常,我们对数据库进行误操作后,需要把数据库Rollback到之前的版本。一个常用的方法就是,使用日志来进行数据库恢复。这个方法虽然强大有效,但是花费时间等成本较高。而数据库表快照(Snapshot)功能,可以在某些时间点为数据表创建快照,保护快照时间点的数据不被修改,并可根据需要快速恢复快照点数据,从而达到高效、可靠的数据备份恢复能力。
Snapshot是一个数据库或表的只读副本,它是数据库表所有数据的映射,由快照被执行的时间点来决定它的内容。由于快照是只读的,所以查询快照数据是无锁查询,会对查询速度有一定的提升。数据快照在报表方面非常有价值,快照设计最开始的目的是为了报表服务。比如,要得到2021的资产负债表,需要数据保持在2021年12月31日零点时的状态,利用快照即可实现。快照也可使用在灾难恢复中,用于将现有的数据恢复到现有的快照时间点,或者在有害数据操作声明的事件中存储个别必要的表和数据。
Part 1 - 对比备份恢复方案的优缺点
(1)数据库快照创建时非常快,恢复也比较高效。
(2)数据库快照可解决用户误操作造成的数据丢失问题,借助多副本的存储策略,也可应对单机系统故障的数据丢失问题。
(3)无法解决整个集群故障或大面积故障造成的丢失数据问题。
(4)除了数据安全保障,“备份/恢复”方案还可以实现数据的跨集群迁移,这是数据库快照方案无法实现的。
Part 2 - 快照使用限制
(1)为了减少占用存储,一个数据库或一张表限制最多只能有3个快照。
(2)为防止使用快照后出现数据不一致,当表有外键等外部约束时,无法创建快照。当表有约束:主键、UNIQUE、CHECK时可创建快照。当快照有序列时可创建快照。库创建快照:当该库的表与其他库的表有外键等外部约束时,无法创建快照。
(3)在数据库有快照时,无法增加表、删除表,ALTER表、TRUNCATE表、删除库。当表有快照时,无法删除表,ALTER表、TRUNCATE表。
(4)在恢复快照期间,该数据库或表无法有其他事务。
(5)在恢复快照时,数据表也无法有外键等引用。
(6)快照是关联到数据库、表的,因此在删除数据库、表之前,需把关联的快照删除。
图1
快照的实现主要利用了MVCC-多版本并发控制来存储KV数据,Key使用时间戳作为数据的版本号,同一Key的多个版本按照时间戳倒序排序,如图1所示;时间戳t=0的数据表示写数据的事务还未提交,数据在WriteIntent中,值是Metadata;对于多版本的数据,云溪数据库会定时进行清理,当快照开启时,会保护快照时间点对应的数据版本不被清理或修改,并可根据需要快速恢复快照点数据,从而实现数据快速“备份”和恢复。
2.1 快照恢复
当需要恢复快照点数据时,首先需检查要恢复的表或库是否存在外部约束,表级快照恢复只有当没有外部约束的情况下才能实现快照恢复;库级快照恢复时,如果库内的表有外部约束,需检查相关联的表是否在该数据库内,如不在同一数据库内,则无法恢复。
图2
快照恢复时,首先查找建立快照的时间点,然后需要得到要恢复表的元数据Descriptor,根据Descriptor找出所有要恢复的数据Range,构建Span,最后将Range回退到建立快照的时间点对应的版本。
恢复Range数据到快照时间点时,需要遍历Range内所有Key版本,利用MVCC多版本机制,根据同一Key的多个版本的时间戳与快照时间点进行比较,将数据恢复到快照时间点,主要流程为:
(1)快照点后未变化的数据,不处理
(2)快照点后新增加的数据,使用DELETE标记覆盖
(3)快照点后修改或删除的数据,使用快照前的值覆盖
恢复快照期间遇到写意图时,需清理已提交事务的写意图,未清理的需返回给上游检查写意图状态,如此事务未提交,则无法进行恢复。
2.2 快照与GC
云溪数据库的GC策略是比较数据表的过期时间与多余MVCCKey的时间戳,选出时间戳小于过期时间的Key进行清理;当建立快照时,需要在时间戳比较时,增加对快照时间的比较:
(1)第一个快照点的delete数据可删除
(2)没有快照的delete数据可删除
(3)临近快照点的delete数据不能GC,否则会访问到更旧的数据
例如,现有如下数据:
增加2个快照s1和s2,那么在GC筛选垃圾数据时就会有3个时间戳的比较,则数据表的GC示例如下:
在原有的GC逻辑中,如果最新的数据已delete并过期,那么该数据的所有Version都会被清理。但有了快照之后则不能如此操作。比如上面例子中的Key d,如果把d@ts25h_1删除,那么在查询快照2的数据时会得到d@ts2day数据,而实际上在ts25h_1时,数据已经被标记删除,客户端应该查不到数据才正确。