本文首发于2018年。
作者:夏鹏举,OceanBase高级技术支持专家
前言
数据库产品作为信息系统的重要组成部分,除了要高效处理用户请求,还需要保证在各种异常情况下故障业务7*24的持续可用和数据的零丢失,本文的主要目的是总结和回顾一下传统数据库的常见故障,并介绍一下OceanBase作为分布式数据库在应对常见故障时的应对措施。
常见数据库故障分类
系统故障
系统故障指数据库在运行过程中,由于硬件故障、数据库软件或操作系统的漏洞、突然停电等情况,导致系统异常,所有故障机器上正在运行的事务以非正常方式终止。
不同于传统数据库的单点故障所导致的系统可用性问题,OceanBase作为分布式数据库为保证可用性提供了多种级别的容灾部署方案。针对机房级别容灾能力提供了同城三机房或两地三中心的部署方案,同时针对城市级别容灾能力提供了三地五中心部署方案。
2018年的云栖大会演示的网商银行三地五中心方案,模拟杭州城市级故障,26秒即完成容灾切换即体现了OceanBase针对城市级别故障的自愈能力。同时针对极小概率的多数派故障,OceanBase和传统数据库一样提供全量+增量的备份恢复机制,当出现数据库异常故障时可以使用离线备份恢复到全量备份到当前时间区间的任一时间点。为保障已提交事务数据不丢失,OceanBase采用和传统数据库一样的WAL(Write-Ahead Logging)方案,通过多数派先持久化事务日志的方式,保证提交事务不丢失。
介质故障
介质故障也称为硬故障,主要指数据库在运行过程中,由于磁盘损坏、强磁干扰、天灾人祸等情况,使得数据库中的数据部分错误或数据丢失的一类故障。
应对方案:针对介质故障导致的数据不一致问题,OceanBase会做两方面的校验。由于OceanBase是一个分布式数据库,同一份数据会有多个副本,大版本全量合并时会做多副本的一致性校验,每个SSTable内部会拆分成2MB大小宏块,每个宏块会计算Checksum。
当发现不一致时自动进行异常副本的替换。另一方面主表和索引表的数据同样会做一致性校验,保障数据在主表和索引表之间的一致性。出于对数据正确性的敬畏,数据校验是OceanBase内部对自己的一道防火墙。
用户误操作
常见错误包括误连线上库后drop业务表, delete操作由于where条件缺失导致误删业务数据。
应对方案:OceanBase通过多副本机制保证单点故障不影响业务,但仅有以上机制无法防范用户的误操作。常见的误删Database和Table方式,可以通过OceanBase的回收站机制实现恢复操作,而对于delete等dml方式的误操作,OceanBase可以通过数据库Flashback Query方式获取删除前快照实现误删数据恢复。下面会专门介绍传统数据和OceanBase应对用户误操作所做的工作。
主流数据库如何应对用户误操作
SQL Server 之 Database Snapshot
SQL Server通过DatabaseSnapshot机制实现快照查询,原理是首先在Database级别创建Snapshot,存储粒度为data-page级别,当Database快照对应的data-page第一次出现修改时,会将对应的前镜像存储于独立的sparse file中,当快照查询涉及到没有修改的data-page直接复用原始page即可。
MySQL 时间点恢复
MySQL没有单独机制存储数据块的前镜像,没有实现类似SQL Server的快照功能。 针对用户误操作只能通过数据库备份加上Binlog重放恢复到单一时间点,因此消耗的时间会比较久。有一些第三方工具比如binlog2sql , 可以通过设置Mysql数据库的binlog_format值为row,并且binlog_row_image参数设置为full,可以通过解析误Delete时间点的Binlog日志生成回滚SQL,在生产库中回放回滚SQL来实现数据库误操作恢复。
还有一种做法是通过备库延迟应用Binlog日志实现,这其实就是变相的使用离线备份加Binlog重放恢复,但可以节省全量备份恢复的时间。归根到底在数据库原生不存储数据前镜像的条件下只能通过备份加Binlog重放恢复用户数据。
Oracle Flashback 机制
Oracle作为功能最为完善的商业关系型数据库产品,提供了多种粒度的数据库闪回功能。
Flashback Query,FlashbackTable,Flashback Transaction Query和Flashback Versions Query都是通过直接从UNDO中读取数据前镜像构造历史快照,因为UNDO段是循环使用的,只要事务提交,之前的UNDO信息就可能被覆盖,从而导致闪回出现快照过旧的报错。Flashback Drop是通过内部将原始Database或者Table重命名,并没有物理删除。Flashback Database和FlashbackData Archive通过专有的数据前镜像来实现回滚操作。
整个Flashback家族中比较常用的功能为Flashback Query,Flashback Table和 Flashback Drop 。其中Flashback Table和Flashback Query 用于误Delete数据恢复, Flashback Drop 主要用于误Drop表的恢复。Flashback Database可能影响业务和丢失数据,一般只会在业务备库上开启,线上库很少使用。
OceanBase Flashback 功能介绍
OceanBase闪回功能和语法上整体上保持与Oracle兼容,但提供传统数据缺失的分布式容灾及多活能力。OceanBase 1.4版本已实现Table和Database级别的FlashbackDrop功能,在2.0版本实现Flashback Query功能,额外实现了Oracle缺失的Truncate Table的闪回功能。因为OceanBase和Oracle在设计思想方面的不同,故实现方式上有本质的区别,下文具体介绍OceanBase的Flashback原理。
Flashback Query
OceanBase的闪回依赖于大版本合并的基线数据,和每次多版本的转储数据及内存MemTable中的所有事务修改记录,可闪回时间范围类似Oracle通过设置undo_retention最早可恢复时间,OceanBase会强制保留最早恢复时间点前一个大版本基线数据及后续所有的多版本转储SSTable。先简单介绍使用OceanBase Flashback Query的使用用例。
mysql> create table flash_query_table (id int); Query OK, 0 rows affected (0.17 sec) mysql> insert into flash_query_table(id) values (1),(2),(3); Query OK, 3 rows affected (0.05 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> select now(),* from flash_query_table; +---------------------+------+ | now() | id | +---------------------+------+ | 2018-10-09 17:01:39 | 1 | | 2018-10-09 17:01:39 | 2 | | 2018-10-09 17:01:39 | 3 | +---------------------+------+ 3 rows in set (0.03 sec) mysql> delete from flash_query_table where id<2; Query OK, 1 row affected (0.04 sec) mysql> select now(),* from flash_query_table; +---------------------+------+ | now() | id | +---------------------+------+ | 2018-10-09 17:01:56 | 2 | | 2018-10-09 17:01:56 | 3 | +---------------------+------+ 2 rows in set (0.02 sec) mysql> select now(),* from flash_query_table as of timestamp to_timestamp('2018-10-09 17:01:40', 'yyyy-mm-dd hh24:mi:ss'); +---------------------+------+ | now() | id | +---------------------+------+ | 2018-10-09 17:02:01 | 1 | | 2018-10-09 17:02:01 | 2 | | 2018-10-09 17:02:01 | 3 | +---------------------+------+ 3 rows in set (0.03 sec)
Flashback Query实现原理
OceanBase采用基线+增量的存储方式,内存中保存最新的行数据修改历史及对应的事务时间戳。同时当内存写满时会触发转储或者合并,转储的数据会保留上一次基线数据到当前转储时间点和每个数据行的所有版本事务修改。MemTable同样会保留最新的事务多版本修改。
抽象下MemTable中存储逻辑结构如下:
说明:Han--> 4:s:2000000 表示4号事务修改行主键Han对应行的字段s值为2000000
MemTable在内存中维护了转储之后到最新时点的事务历史,将历史事务针对该行的操作按照事务提交时间组织成行操作链,新事务提交时会往行操作链尾部追加新的行操作。如果行操作链保存的历史事务过多,将影响读取性能,此时需要触发Compaction操作,融合这些历史事务并生成新的行操作链。但不会删除老的行操作链。因此Flashback Query只需要顺着内存中的反向指针往前回溯即可读取任一时点历史快照,根据Flashback Query闪回的位点分两种情况分析:
闪回到转储之后的位点
需要合并基线+转储多版本+MemTable闪回位点对应快照
闪回到基线之后转储之前的位点
需要合并基线+多版本转储对应闪回位点快照
回收站机制——flashback drop table / database,truncate table
OceanBase实现了回收站机制,从而防止用户误 drop table/database 的时候能快速恢复表数据。通过全局系统变量recyclebin 变量控制回收站功能的打开和关闭,on为打开(默认), off 为关闭。
索引单独drop进回收站比较特殊,当用户删除唯一索引后,对主表继续插入数据时,由于缺少唯一性约束而导致当索引从回收站Flashback时,会出现唯一性冲突。针对此场景OceanBase替代方案为使用alter index invisible/visible, 设置索引在SQL层对用户不可见,存储层不受影响,唯一索引仍然做数据的唯一性检查。不支持drop索引进回收站。索引数据依赖主表,单独drop索引后通过重建恢复更方便。
OceanBase针对truncatetable特殊设计为truncate table=drop table+ create table,当开启回收站的情况下和drop table机制类似,但Flashback时需要采用rename to子句保证表名不冲突。而Oracle执行truncate table是不会保存undo信息,也不会挪进回收站,只能通过数据库备份恢复,此为OceanBase的一个设计优化点。
//还原回收站中的DATABASE FLASHBACK DATABASE db_name TO BEFORE DROP [RENAME TO db_name]; //还原回收站中的TABLE, 注意这里db_name 可以将表flash到一个新的库下 FLASHBACK table_name TO BEFORE DROP [RENAME TO db_name.table_name]; //租户级别打开回收站机制 set global recyclebin=on MySQL [test]> create table tb_drop(id int primary key,name varchar(5)); Query OK, 0 rows affected (0.07 sec) MySQL [test]> insert into tb_drop values(1,'a'),(3,'c'); Query OK, 2 rows affected (0.01 sec) Records: 2 Duplicates: 0 Warnings: 0 MySQL [test]> drop table tb_drop; Query OK, 0 rows affected (0.02 sec) MySQL [test]> select * from tb_drop; +----+------+ | id | name | +----+------+ | 1 | a | | 3 | c | +----+------+ 2 rows in set (0.00 sec) MySQL [test]> select gmt_create, tenant_id,object_name,original_name from oceanbase.__all_recyclebin where ORIGINAL_NAME='tb_drop'; +-----------------+-----------+------------------------+---------------+ | gmt_create | tenant_id | object_name | original_name | +-----------------+-----------+------------------------+---------------+ | 2018-10-16 14:31:01.102840 | 1 | __recycle_$_10023_1_1539671461102752 | tb_drop | +-----------------+-----------+------------------------+---------------+ 1 row in set (0.03 sec) MySQL [test]> flashback table __recycle_$_10023_1_1539671461102752 to before drop; Query OK, 0 rows affected (0.02 sec) MySQL [test]> select * from tb_drop ; +----+------+ | id | name | +----+------+ | 1 | a | | 3 | c | +----+------+ 2 rows in set (0.00 sec)
实现原理如下:
在开启回收站功能后,在原始的“DROP操作”的基础上会加一层包装。DROP涉及的对象会进入回收站,主要操作为修改对象的schema信息及在__all_recyclebin新增相关记录。
(1)被drop的表是系统表,那么直接删除,不进入回收站,否则进入步骤2;
(2)把被drop对象的schema信息中的OBJECT_NAME 改成__recycle$_gen_id ,其中gen_id的生成规则如下:
GEN_ID = cluster_id + tenant_id + schema_version 的方式组合
例如:__recycle_$_10023_1004_1539604687557424
(3)在__all_recyclebin表中插入被drop对象的信息。
还原操作涉及到两点,分别是对象的schema信息修改和删除内部表 __all_recyclebin 中的记录,具体步骤如下:
(1)检查用户是否具备相应的权限;
(2)从schema信息中OBJECT_NAME里面提取出原有对象的相关信息,修改对象的schema信息,主要是修改对象的名称,如果使用了RENAME TO语句指定了NEW_OBJECT_NAME,那么把名称改成NEW_OBJECT_NAME,否则改成ORIGINAL_NAME;
(3)如果发现当前TABLE所在的DATABASE已经被DROP掉,则FLASHBACK失败。如果成功则删除__all_recyclebin中相关的行记录。
总结
OceanBase通过分布式多副本机制实现单点及少数派故障业务无感知,对于介质故障OceanBase通过多层Checksum机制保证数据的一致性,并自动实现数据修正。对用户疏忽导致的误操作,OceanBase可以通过Flashback的轻量级方式实现数据快速恢复,或者通过数据库离线备份实现恢复到故障发生之前的位点。拥有OceanBase,线上误删数据库再也不需要跑路了。