OceanBase调研

介绍

OceanBase 在支付宝其实已经服务多年,是一款非常有实力的国产分布式数据库,主打高性能、高可用、低成本和线性扩展。尤其在金融领域有丰富的经验。现在也已经开源。本文基于一些公开资料的学习给自己做个总结

基本架构


上图有些组件没有显示全,下图作为一个补充

整体是一个 share-nothing 架构,具备良好的并行和扩展性。下面介绍下其核心的组件:

  • 集群:一个集群下可以有属于多个可用区的节点共同组成
  • region: 物理地域,可以是一个城市或者地域
  • 可用区: 一个 region 由多个 zone 组成,一个可用区下可以有多个节点
  • ob server 节点:一个 ob server 实例相当于一个节点,一个节点归属于一个可用区。一个 ob server 是一台物理机。
  • 租户:租户可以理解为是隔离的数据库,类似 Oracle 中一个独立的库。observer 会父子创建租户的独立资源池,隔离 CPU 和内存资源使用。这个对于一些优先级划分是很有帮助的。
  • obproxy: 无状态的数据库代理,负责分发 SQL 请求、连接管理、数据路由、兼容 MySQL 和 Oracle 协议
  • 分区:图上的 P 就是一个分区。ob 内部分区的类型有全能型(F)、日志型(L)、加密投票型(E)和只读副本型®。其中只读副本不构成 paxos 成员组。不同副本能力上存在差异。这个有点类似 oracle 不同的文件类型,数据文件、日志文件,可以参照这么理解。
  • 副本:数据是多份存储在分区内,有主副本和从副本
  • RootService: 主要负责资源管理、分区负载均衡管理、schema 管理、集群自举、分区 leader 选举等,运行在集群的 zone 内部的某一台 ob server 上

一般部署模式

存储层


整体上是基于 LSM 设计的存储结构,不过 oceanbase 有一些自己的优化,达到了总体上低成本、易使用、高性能、高可靠的效果。OB 内存里面同时有 hash 表和 B+树 应对点查和范围查询的场景。虽然基于 LSM,但是 oceanbase 本质还是一个行存数据库,基本数据块中存储的是 row 的信息。

优化点

宏块与微块(性能)

oceanbase 引入宏块,将数据文件拆分成较粗粒度,没有变更的宏块在 compaction 的时候不进行完整读取,降低 compaction 的代价。一个 SSTable 实际上就是多个宏块的集合。微块是文件读 IO 最小单位,包含若干数据行,多个微块又构成了一个宏块。微块构建分 flat 和 encoding 两种偶见模式。内存里面的是增量数据,磁盘上的是基线数据。encoding 只针对基线数据。压缩编码在微块粒度上进行。经历填充行->编码->通用压缩->加密。OB 支持的加密算法为 zlib、snappy、lz4 和 zstd。

多级缓存(性能)

LSM 设计一般都有块缓存,ob 自己设计了一套多级缓存的框架,主要包含:

  • 行缓存,减少了读放大,对 TP 事物支持更好。
  • log cache:缓存 redo log
  • location cache:缓存数据副本所在的位置
  • schema cache:缓存 schema 信息

分层转储(性能)

实际上理解也比较简单,就是在内存层面支持多个 frozen memtable;在 disk L0 层支持多个 mini sstable。避免形成一个过大的 memtable

行列混合编码(成本)

OB 通过自研编码压缩算法,数据存储压缩率比传统数据库有 10+倍提升。原理上定长部分用按列编码,存储在微块的列存区,变长部分按行存储,存储在变长区。读取微块一行时,可以只对一行数据进行解码。3.2 版本后,encoding 支持了 avx2 的向量化执行。

轮转合并(可靠性)

把正常服务和合并时间错开,避免合并影响用户正常请求

数据校验(可靠性)

ob 具备全链路的数据校验,在合并时也会比较多副本以及主表索引表比对来保证数据准确性

范围删除标记(性能)

LSM 对于大量删除不友好,因为本质也是对所有行做删除标记,无法避免合并时的完整扫描。ob 提供了一个范围删除标记,通过特殊的转储合并提前回收这些删除行来加速查询性能。

隐藏主键

无主键表有隐藏主键,所有数据表都是索引聚簇表。插入会判断 PK 冲突,PK 冲突频率高的会异步构建 bloom filter

非全列更新信息

更新操作只记录 PK 和更新列的新值,加速 compaction 效率

partition group (PG)

这个概念是为了优化性能而产生的。OB 提供语法将相同分区键、分区规则的表定义成一个 PG,OB 保证听一个 PG 中的 partition 都是在一起的,那么一个 PG 下的事务会降级成单机事务,效率会比较高。

数据合并

OceanBase 支持多种合并方式,比较灵活

  • 全量合并(需手动开启):类似 marjor compaction,静态数据和内存数据全量合并会消耗大量 IO 和空间
  • 增量合并(默认):宏块和微块没修改都可以重用,在微块级增量及合并,减少合并代价
  • 渐进合并:主要是为了 DDL 变更服务,DDL 变更造成的数据合并分散到多次每日合并去做
  • 并行合并:分区内、分区间并行合并
  • 轮转合并:低峰时间进行合并

计算层

基本架构流程

计算层主要是 SQL 引擎的实现,下图演示了 SQL 引擎在整个体系中的位置以及一个 SQL 执行的过程。

下图 SQL 执行的过程有一些信息没有透露完整,即如果执行器没法做本地化执行,会生成远程执行计划通过 RPC 到远程执行。

SQL 语句并行执行流程

用户连接的 observer 称之为 QC(query coordinator),执行步骤如下:

  1. QC 预约足够的线程资源
  2. QC 将需要并行的计划拆成多个子计划,即 DFO(data flow operation)。每个 DFO 包含若干个执串行执行的算子。例如,一个 DFO 里包含了扫描分区、聚集和发送算子的任务,另外一个 DFO 包含了收集、聚集算子等任务
  3. QC 按照一定的逻辑顺序将 DFO 调度到合适的 OBServer 上执行,OBServer 上会临时启动一个辅助协调者 SQC(sub query coordinator),SQC 负责在所在 OBServer 上位各个 DFO 申请执行资源、构造执行上下文等环境等,然后启动 DFO 在各个 OBServer 上进行并行执行
  4. 当各个 DFO 都执行完毕,QC 会串行执行剩余部分的计算。例如一个并行的 COUNT 算法最终需要 QC 将各个机器上的计算结果做一个 SUM 运算
  5. QC 所在线程将结果返回给哭护短

功能特性

兼容模式

ob 支持 MySQL 和 Oracle 的兼容模式,让原 oracle 和 mysql 的用户迁移到 ob 的过程变得十分容易和自然,这对 ob 在国内的推广具有重要意义。

表组

这个概念是为了保证分布式环境下做 join 时的 data locality 优化。相同分区方式的表放在一个机器上,这样做 join 的时候可以避免跨机分布式事务,有更好的性能。

全局索引

ob 中全局索引有如下特点:

  • 索引表分区的方式是独立的
  • 全局分区索引存在索引键映射到不同分区的情况

分布式数据中实现全局索引存在一些挑战,主要是:

  • 保证主表数据和索引数据的跨机器同步更新(一致性):这个可以基于 paxos 协议和 2PC 去完成
  • 索引数据和主表数据分跨机器访问时的效率问题(性能): ob 通过改进的 2PC 来解决
  • 分布式数据库中全局(跨多台机器)的读写一致性问题:ob 提供全局一致性快照可以解。

全局一致性快照与全局时间戳

全局一致性快照实现的关键是全局一致性版本号的使用。一般是利用精确的物理时钟或者软件的全局时间戳服务(GTS)。oceanbase 采用的 GTS 服务架构如下,设计的特点在于 GTS 也是和租户去对应的,这样 GTS 本身的扩展性也是会比较好,由于 GTS 引发的故障 scope 也只会限定在租户级。此外 GTS 也支持高可用,默认三副本。

数据闪回

本质上是有数据前镜像,ob 支持在 oracle、mysql 模式上采用闪回 sql 访问被误删的数据。例如:

1
2
3
4
5
6
### oracle模式,通过 TIMESTAMP 指定的历史时间并闪回查询一张单表在该历史时间中的状态的数据
obclient> SELECT * FROM tbl1 as of timestamp TO_TIMESTAMP('2020-08-13 16:20:00','yyyy-mm-dd hh24:mi:ss');

##mysql 模式 通过 AS OF SNAPSHOT 指定历史时间并闪回查询单表在该历史时间点的状态的数据的示例如下:
obclient> SELECT * FROM table1 AS OF SNAPSHOT 1582807800000000;

自动负载均衡

均衡组维度均衡

ob 定义了几种均衡组。不同均衡组定义了特定的一些分区。在均衡组维度对分区进行均衡从而保证各个均衡组内的分区均衡。均衡的维度是在整个 zone 内。

Primary zone 内的 leader 分区均衡

存放 leader 主分区的 zone 称之为 primary zone,这个可以由 ob 去定义,定义好了 primary zone 的个数,ob 会把 leader 分区均衡到这些 zone 的上。下面是只设置一个 primary zone 的例子

自动死锁检测

OB 实现的是一种基于优先级的多出度分布式死锁检测方案(LCL-Lock chain length),这种检测方式属于边跟踪算法的模式。优先级最低的事务指的是冲突事务中开启最晚的事务。一个事务同时等待多个事务的单向依赖的有向边称为多出度。传统采用路径推动算法(path-pushing algorithm)的死锁检测有多杀及误杀事务的问题。LCL 引入一个令牌保证只有最大令牌值的节点去探测到死锁并进行 kill,避免误杀和多杀。

tips: 死锁检测是个关注蛮多的领域,EDGAR KNAPP 把分布式死锁检测算法分为了四类。有兴趣可以了解下:

  • Path-Pushing Algorithms(路径推动算法)
  • Edge-Chasing Algorithms(边追踪算法)
  • Diffusing Computations(扩散计算)
  • Global State Detection(全局状态检测)

周边生态

应用场景

  • OB 比较关注严苛的 TP 场景,重视 locality,比如和 TIDB 比较的话,每个 observer 包含完整的 SQL ENGINE、memory table、sstable 等核心组件,避免跨网络交互。
  • 事务提交不需要所有节点参与,有只读副本。事务提交保证不丢即可,避免过多节点参与多数派选举导致的延迟问题。

感悟:企业规模不同阶段用不同数据库即可。数据量小,用单机数据库就最好。

参考资料