阿里巴巴开源数据库 --OceanBase 从使用聊到架构剖析
OceanBase 概述:
OceanBase 是由蚂蚁金服、阿里巴巴完全自主研发的金融级分布式关系数据库,始创于 2010 年。OceanBase 具有数据强一致、高可用、高性能、在线扩展、高度兼容 SQL 标准和主流关系数据库、低成本等特点。OceanBase 至今已成功应用于支付宝全部核心业务:交易、支付、会员、账务等系统以及阿里巴巴淘宝(天猫)收藏夹、P4P 广告报表等业务。除在蚂蚁金服和阿里巴巴业务系统中获广泛应用外,从 2017 年开始,OceanBase 开始服务外部客户,客户包括南京银行、西安银行、印度 Paytm、人保健康险等。
2021 年 6 月:OceanBase 3.0 版本正式发布,此次推出的 OceanBase 3.0 版本产品同时具备在事务处理和数据分析两类任务的高性能能力,升级为一款支持 HTAP 混合负载的企业级分布式数据库。同时,OceanBase 宣布正式开源,并成立 OceanBase 开源社区,社区官网同步上线,300 万行核心代码向社区开放。
OceanBase 源码地址:https://github.com/alibaba/oceanbase
OceanBase 开源社区:https://developer.aliyun.com/group/oceanbase
OceanBase 资料整理:https://github.com/0voice/newsql_nosql_library
2. OceanBase 特点:
高可用 单服务器故障能够自愈,支持跨城多机房容灾,数据零丢失,可满足金融行业 6 级容灾标准(RPO=0,RTO<=30 秒)。
线性扩展 透明扩展,自动负载均衡,应用透明的水平扩展,集群规模可超过 1500 节点,数据量可达 PB 级,单表记录万亿行。
MySQL 高度兼容 兼容 MySQL 协议、语法和使用习惯,MySQL 客户端工具可以直接访问 OceanBase 数据库。
高性能 准内存级数据变更操作、独创的编码压缩技术,结合线性水平扩展,TPC-C 测试达到 7.07 亿 tpmC。
低成本 使用 PC 服务器和低端 SSD,高存储压缩率降低存储成本,高性能降低计算成本,多租户混部充分利用系统资源。
多租户 原生支持多租户构架,同一套数据库集群可以为多个独立业务提供服务,租户间数据隔离,降低部署和运维成本。
OceanBase 数据库支持支付宝的全部核心业务,以及银行、保险、证券、运营商等多个行业的数百个客户的核心业务系统。
3. OceanBase 上手:
3.1 快速上手
查看 快速使用指南 开始试用 OceanBase 数据库。
3.2 文档
英文(English)(Coming soon)
3.3 客户端
3.4 许可证
OceanBase 数据库使用 MulanPubL - 2.0 许可证。您可以免费复制及使用源代码。当您修改或分发源代码时,请遵守木兰协议。
3.5 兼容性列表
4. OceanBase 构建
4.1 前提条件
在构建前您需要确认您的机器已经安装必要的软件:
4.2 Fedora 系列 (包括 CentOS,Fedora,OpenAnolis,RedHat 等)
4.3 Debian 系列 (包括 Debian,ubuntu 等)
4.4 SUSE 系列 (包括 SUSE,openSUSE 等)
4.5 debug 模式
4.6 release 模式
4.7 构建 RPM 软件包
5. OceanBase 其他概念
5.1 OceanBase 云平台
OceanBase 云平台(OceanBase Cloud Platform,OCP)伴随 OceanBase 数据库而生,OceanBase 云平台(OCP)是一款以 OceanBase 为核心的企业级数据库管理平台,不仅提供对 OceanBase 集群和租户等组件的全生命周期管理服务,同时也对 OceanBase 相关的资源(主机、网络和软件包等)提供管理服务,让您能够更加高效地管理 OceanBase 集群,降低企业的 IT 运维成本。
5.2 OceanBase 开发者中心
OceanBase 开发者中心(OceanBase Developer Center,ODC)是为 OceanBase 数据库量身打造的企业级数据库开发平台。ODC 支持连接 OceanBase 中 MySQL 和 Oracle 模式下的数据库,同时为数据库开发者提供了数据库日常开发操作、WebSQL、SQL 诊断、会话管理和数据导入导出等功能。
5.3 OceanBase 迁移服务
OceanBase 迁移服务(OceanBaseMigrationService,OMS)是 OceanBase 提供的一种支持同构或异构 RDBMS 与 OceanBase 之间进行数据交互的服务,它提供了数据的在线迁移和实时增量同步的数据复制能力。
5.4 OceanBase 公有云
构建基础设施上的公有云数据库服务,基于完全自主研发的原生分布式能力,提供 7.07 亿 tpmC 的卓越性能、PB 级存储容量、万亿级单表记录、主流数据库兼容、极致弹性、异地容灾等金融级核心能力。支持一站式部署、扩容、监控运维管理、开发工具、数据迁移、备份恢复等端到端数据库服务化解决方案。
5.5 OceanBase 数据库一体机
OceanBase 数据库一体机是基于蚂蚁金融级分布式数据库和自研可信硬件打造的软硬一体化产品,针对数据库业务软硬件深度性能调优,提供高可靠性、高安全性、高性价比、智能管控和一站式快速交付部署能力。它极大的降低复杂性,开箱即用,让您更快享受到 OceanBase 分布式数据库高效之旅。
5.6 OceanBase 数据库架构
6. OceanBase 系统架构
OceanBase 整体架构图
6.1 OceanBase 由如下几个部分组成:
客户端:用户使用 OccanBase 的方式和 MySQL 数据库完全相同,支持 JDBC、C 客户端访问,等等。基于 MySQL 数据库开发的应用程序、工具能够直接迁移到 OceanBase。
RootServer: 管理集群中的所有服务器,子表(tablet)数据分布以及副本管理。RootServer 一般为一主一备,主备之间数据强同步。
UpdateServer: 存储 OccanBase 系统的增量更新数据。UpdateServer 一般为一主一备,主备之间可以配置不同的同步模式。部署时,UpdateServer 进程和 RootServer 进程往往共用物理服务器。
ChunkServer: 存储 OccanBase 系统的基线数据。基线数据一般存储两份或者三份,可配置。
MergeServer: 接收并解析用户的 sQL 请求,经过词法分析、语法分析、查询优化等一系列操作后转发给相应的 ChunkServer 或者 UpdateServer。如果请求的数据分布在多台 ChunkServer 上,MergeServer 还需要对多台 ChunkServer 返回的结果进行合并。客户端和 MergeScrver 之间采用原生的 MySQL 通信协议,MySQL 客户端可以直接访问 MergeServer。
OceanBase 支持部署多个机房,每个机房部署一个包含 RootServer、MergeServer、ChunkServer 以及 UpdateServer 的完整 OceanBase 集群,每个集群由各自的 RootServer 负责数据划分、负载均衡、集群服务器管理等操作,集群之间数据同步通过主集群的主 UpdateServer 往备集群同步增量更新操作日志实现。 客户端配置了多个集群的 RootServer 地址列表,使用者可以设置每个集群的流量分配比例,客户端根据这个比例将读写操作发往不同的集群。
6.2 客户端
OceanBase 客户端与 MergeServer 通信,目前主要支持如下几种客户端:
Mysql 客户端:MergeServer 兼容 Mysql 协议,Mysql 客户端及相关工具(如 Java 数据库访问方式 JDBC)只需要将服务器的地址设置为任意一台 MergeServer 的地址就可以直接使用。
Java 客户端:OceanBase 内部部署了多台 MergeServer,Java 客户端提供对 Mysql 标准 JDBC Driver 的封装,并提供流量分配、负载均衡、MergeServer 异常处理等功能。简单来讲,Java 客户端首先按照一定的策略定位到某台 MergeServer,接着调用 Mysql JDBC Driver 往这台 MergeServer 发送读写请求。Java 客户端实现符合 JDBC 标准,能够支持 Spring、iBatis 等 Java 编程框架。
C 客户端:OceanBase C 客户端的功能和 Java 客户端类似。它首先按照一定的策略定位到某台 MergeServer,接着调用 Mysql 标准 C 客户端往这台 MergeServer 发送读写请求。C 客户端的接口和 Mysql 标准 C 客户端接口完全相同,因此,能够通过 LD_PRELOAD 的方式将应用程序依赖的 Mysql 标准 C 客户端替换为 OceanBase C 客户端,而无需修改应用程序的代码。
OceanBase 集群有多台 MergeServer,这些 MergeServer 的服务器地址存储在 OceanBase 服务器端的系统表(与 Oracle 的系统表类似,存储 OceanBase 系统的元数据)内。OceanBase Java/C 客户端首先请求服务器端获取 MergeServer 地址列表,接着按照一定的策略将读写请求发送给某台 MergeServer,并负责对出现故障的 MergeServer 进行容错处理。
Java/C 客户端访问 OceanBase 的流程大致如下:
6.2.1 请求 RootServer 获取集群中 MergeServer 的地址列表。
6.2.2 按照一定的策略选择某台 MergeServer 发送读写请求。客户端与 MergeServer 之间的通信协议兼容原生的 Mysql 协议,因此,只需要调用 Mysql JDBC Driver 或者 Mysql C 客户端这样的标准库即可。客户端支持的策略主要有两种:随机以及一致性哈希。一致性哈希的主要目的是将相同的 SQL 请求发送到同一台 MergeServer,方便 MergeServer 对查询结果进行缓存。
6.2.3 如果请求 MergeServer 失败,则从 MergeServer 列表中重新选择一台 MergeServer 重试;如果请求某台 MergeServer 失败超过一定的次数,将这台 MergeServer 加入黑名单并从 MergeServer 列表中删除。另外,客户端会定期请求 RootServer 更新 MergeServer 地址列表。
如果 OceanBase 部署多个集群,客户端还需要处理多个集群的流量分配问题。使用者可以设置多个集群之间的流量分配比例,客户端获取到流量分配比例后,按照这个比例将请求发送到不同的集群。
OceanBase 程序升级版本时,往往先将备集群的读取流量调整为 0,这时所有的读写请求都只发往主集群,接着升级备集群的程序版本。备集群升级完成后将流量逐步切换到备集群观察一段时间,如果没有出现异常,则将所有的流量切到备集群,并将备集群切换为主集群提供写服务。原来的主集群变为新的备集群,升级新的备集群的程序版本后重新分配主备集群的流量比例。
6.3 RootServer
RootServer 的功能主要包括:集群管理、数据分布以及副本管理。
RootServer 管理集群中的所有 MergeServer、ChunkServer 以及 UpdateServer。每个集群内部同一时刻只允许一个 UpdateServer 提供写服务,这个 UpdateServer 成为主 UpdateServer。这种方式通过牺牲一定的可用性获取了强一致性。RootServer 通过租约(Lease)机制选择唯一的主 UpdateServer,当原先的主 UpdateServer 发生故障后,RootServer 能够在原先的租约失效后选择一台新的 UpdateServer 作为主 UpdateServer。另外,RootServer 与 MergeServer&ChunkServer 之间保持心跳(heartbeat),从而能够感知到在线和已经下线的 MergeServer&ChunkServer 机器列表。
OceanBase 内部使用主键对表格中的数据进行排序和存储,主键由若干列组成并且具有唯一性。在 OceanBase 内部,基准数据按照主键排序并且划分为数据量大致相等的数据范围,称为 tablet。 每个 tablet 的缺省大小是 256MB(可配置)。OceanBase 的数据分布方式与 Bigtable 一样采用顺序分布,不同的是,OceanBase 没有采用 RootTable + MetaTable 两级索引结构,而是采用 RootTable 一级索引结构。
如上图所示,主键值在[1, 100]之间的表格被划分为四个 tablet:1~ 25,26~ 50,51~ 80 以及 81~100。RootServer 中的 RootTable 记录了每个 tablet 所在的 ChunkServer 位置信息,每个 tablet 包含多个副本(一般为三个副本,可配置),分布在多台 ChunkServer 中。当其中某台 ChunkServer 发生故障时,RootServer 能够检测到,并且触发对这台 ChunkServer 上的 tablet 增加副本的操作;另外,RootServer 也会定期执行负载均衡,选择某些 tablet 从负载较高的机器迁移到负载较低的机器。
RootServer 采用一主一备的结构,主备之间数据强同步,并通过 Linux HA(http://www.linux-ha.org)软件实现高可用性。 主备 RootServer 之间共享 VIP,当主 RootServer 发生故障后,VIP 能够自动漂移到备 RootServer 所在的机器,备 RootServer 检测到以后切换为主 RootServer 提供服务。
6.3.1 MergeServer
MergeServer 的功能主要包括:协议解析、SQL 解析、请求转发、结果合并、多表操作等。
OceanBase 客户端与 MergeServer 之间的协议为 Mysql 协议。MergeServer 首先解析 Mysql 协议,从中提取出用户发送的 SQL 语句,接着进行词法分析和语法分析,生成 SQL 语句的逻辑查询计划和物理查询计划,最后根据物理查询计划调用 OceanBase 内部的各种操作符。
MergeServer 缓存了 tablet 分布信息,根据请求涉及的 tablet 将请求转发给该 tablet 所在的 ChunkServer。如果是写操作,还会转发给 UpdateServer。某些请求需要跨多个 tablet,此时 MergeServer 会将请求拆分后发送给多台 ChunkServer,并合并这些 ChunkServer 返回的结果。如果请求涉及到多个表格,MergeServer 需要首先从 ChunkServer 获取每个表格的数据,接着再执行多表关联或者嵌套查询等操作。
MergeServer 支持并发请求多台 ChunkServer,即将多个请求发给多台 ChunkServer,再一次性等待所有请求的应答。另外,在 SQL 执行过程中,如果某个 tablet 所在的 ChunkServer 出现故障,MergeServer 会将请求转发给该 tablet 的其他副本所在的 ChunkServer。这样,ChunkServer 故障是不会影响用户查询的。
MergeServer 本身是没有状态的,因此,MergeServer 宕机不会对使用者产生影响,客户端会自动将发生故障的 MergeServer 屏蔽掉。
6.3.2 ChunkServer
ChunkServer 的功能包括:存储多个 tablet、提供读取服务、执行定期合并以及数据分发。
OceanBase 将大表划分为大小约为 256MB 的 tablet,每个 tablet 由一个或者多个 SSTable 组成(一般为一个),每个 SSTable 由多个块(Block,大小为 4KB ~ 64KB 之间,可配置)组成,数据在 SSTable 中按照主键有序存储。查找某一行数据时,需要首先定位这一行所属的 tablet,接着在相应的 SSTable 中执行二分查找。 SSTable 支持两种缓存模式,Block Cache 以及 Row Cache。Block Cache 以 Block 为单位缓存最近读取的数据,Row Cache 以行为单位缓存最近读取的数据。
MergeServer 将每个 tablet 的读取请求发送到 tablet 所在的 ChunkServer,ChunkServer 首先读取 SSTable 中包含的基准数据,接着请求 UpdateServer 获取相应的增量更新数据,并将基准数据与增量更新融合后得到最终结果。
由于每次读取都需要从 UpdateServer 中获取最新的增量更新,为了保证读取性能,需要限制 UpdateServer 中增量更新的数据量,最好能够全部存放在内存中。 OceanBase 内部会定期触发合并或者数据分发操作,在这个过程中,ChunkServer 将从 UpdateServer 获取一段时间之前的更新操作。通常情况下,OceanBase 集群会在每天的服务低峰期(凌晨 1:00 开始,可配置)执行一次合并操作。这个合并操作往往也称为每日合并。
6.3.3 UpdateServer
UpdateServer 是集群中唯一能够接受写入的模块,每个集群中只有一个主 UpdateServer。UpdateServer 中的更新操作首先写入到内存表,当内存表的数据量超过一定值时,可以生成快照文件并转储到 SSD 中。 快照文件的组织方式与 ChunkServer 中的 SSTable 类似,因此,这些快照文件也称为 SSTable。另外,由于数据行的某些列被更新,某些列没被更新,SSTable 中存储的数据行是稀疏的,称为稀疏型 SSTable。
为了保证可靠性,主 UpdateServer 更新内存表之前需要首先写操作日志,并同步到备 UpdateServer。当主 UpdateServer 发生故障时,RootServer 上维护的租约将失效,此时,RootServer 将从备 UpdateServer 列表中选择一台最新的备 UpdateServer 切换为主 UpdateServer 继续提供写服务。 UpdateServer 宕机重启后需要首先加载转储的快照文件(SSTable 文件),接着回放快照点之后的操作日志。
由于集群中只有一台主 UpdateServer 提供写服务,因此,OceanBase 很容易地实现了跨行跨表事务,而不需要采用传统的两阶段提交协议。当然,这样也带来了一系列的问题。由于整个集群所有的读写操作都必须经过 UpdateServer,UpdateServer 的性能至关重要。OceanBase 集群通过定期合并和数据分发这两种机制将 UpdateServer 一段时间之前的增量更新源源不断地分散到 ChunkServer,而 UpdateServer 只需要服务最新一小段时间新增的数据,这些数据往往可以全部存放在内存中。 另外,系统实现时也需要对 UpdateServer 的内存操作、网络框架、磁盘操作做大量的优化。
6.3.4 定期合并 &数据分发
定期合并和数据分发都是将 UpdateServer 中的增量更新分发到 ChunkServer 中的手段,二者的整体流程比较类似:
6.3.4.1 UpdateServer 冻结当前的活跃内存表(Active MemTable),生成冻结内存表,并开启新的活跃内存表,后续的更新操作都写入新的活跃内存表。
6.3.4.2 UpdateServer 通知 RootServer 数据版本发生了变化,之后 RootServer 通过心跳消息通知 ChunkServer。
6.3.4.3 每台 ChunkServer 启动定期合并或者数据分发操作,从 UpdateServer 获取每个 tablet 对应的增量更新数据。
定期合并与数据分发两者之间的不同点在于,数据分发过程中 ChunkServer 只是将 UpdateServer 中冻结内存表中的增量更新数据缓存到本地,而定期合并过程中 ChunkServer 需要将本地 SSTable 中的基准数据与冻结内存表的增量更新数据执行一次多路归并,融合后生成新的基准数据并存放到新的 SSTable 中。定期合并对系统服务能力影响很大,往往安排在每天服务低峰期执行(例如凌晨 1 点开始),而数据分发可以不受限制。
定期合并不停读服务
如上图所示,活跃内存表冻结后生成冻结内存表,后续的写操作进入新的活跃内存表。定期合并过程中 ChunkServer 需要读取 UpdateServer 中冻结内存表的数据、融合后生成新的 Tablet,即:
虽然定期合并过程中各个 ChunkServer 的各个 Tablet 合并时间和完成时间可能都不相同,但并不影响读取服务。如果 tablet 没有合并完成,那么使用旧 Tablet,并且读取 UpdateServer 中的冻结内存表以及新的活跃内存表;否则,使用新 Tablet,只读取新的活跃内存表,即:
7. 架构剖析
7.1 一致性选择
Eric Brewer 教授的 CAP 理论指出,在满足分区可容忍性的前提下,一致性和可用性不可兼得。
虽然目前大量的互联网项目选择了弱一致性,但我们认为这是底层存储系统,比如 Mysql 数据库,在大数据量和高并发需求压力之下的无奈选择。弱一致性给应用带来了很多麻烦,比如数据不一致时需要人工订正数据。如果存储系统既能够满足大数据量和高并发的需求,又能够提供强一致性,且硬件成本相差不大,用户将毫不犹豫地选择它。强一致性将大大简化数据库的管理,应用程序也会因此而简化。因此,OceanBase 选择支持强一致性和跨行跨表事务。
OceanBase UpdateServer 为主备高可用架构,更新操作流程如下:
将更新操作发送到备机;
将更新操作的 redo 日志写入主机硬盘;
将 redo 日志应用到主机的内存表格中;
返回客户端写入成功。
OceanBase 要求将 redo 日志同步到主备的情况下才能够返回客户端写入成功,即使主机出现故障,备机自动切换为主机,也能够保证新的主机拥有以前所有的更新操作,严格保证数据不丢失。另外,为了提高可用性,OceanBase 还增加了一种机制,如果主机往备机同步 redo 日志失败,比如备机故障或者主备之间网络故障,主机可以将备机从同步列表中剔除,本地更新成功后就返回客户端写入成功。主机将备机剔除前需要通知 RootServer,后续如果主机故障,RootServer 能够避免将不同步的备机切换为主机。
OceanBase 的高可用机制保证主机、备机以及主备之间网络三者之中的任何一个出现故障都不会对用户产生影响,然而,如果三者之中的两个同时出现故障,系统可用性将受到影响,但仍然保证数据不丢失。如果应用对可用性要求特别高,可以增加备机数量,从而容忍多台机器同时出现故障的情况。
OceanBase 主备同步也允许配置为异步模式,支持最终一致性。这种模式一般用来支持异地容灾。例如,用户请求通过杭州主站的机房提供服务,主站的 UpdateServer 内部有一个同步线程不停地将用户更新操作发送到青岛机房。如果杭州机房整体出现不可恢复的故障,比如地震,还能够通过青岛机房恢复数据并继续提供服务。
另外,OceanBase 所有写事务最终都落到 UpdateServer,而 UpdateServer 逻辑上是一个单点,支持跨行跨表事务,实现上借鉴了传统关系数据库的做法。
7.2 数据结构
OceanBase 数据分为基准数据和增量数据两个部分,基准数据分布在多台 ChunkServer 上,增量数据全部存放在一台 UpdateServer 上。 如下图所示,系统中有 5 个 tablet,每个 tablet 有 3 个副本,所有的 tablet 分布到 4 台 ChunkServer 上。RootServer 中维护了每个 tablet 所在的 ChunkServer 的位置信息,UpdateServer 存储了这 5 个 tablet 的增量更新。
OceanBase 数据结构
不考虑数据复制,基准数据的数据结构如下:
每个表格按照主键组成一颗分布式 B+树,主键由若干列组成;
每个叶子节点包含表格一个前开后闭的主键范围(rk1,rk2]内的数据;
每个叶子节点称为一个子表(tablet),包含一个或者多个 SSTable;
每个 SSTable 内部按主键范围有序划分为多个块(block)并内建块索引(block index);每个 SSTable 内部按主键范围有序划分为多个块(block)并内建块索引(block index);
每个块的大小通常在 4KB ~ 64KB 之间并内建块内的行索引;每个块的大小通常在 4KB ~ 64KB 之间并内建块内的行索引;
数据压缩以块为单位,压缩算法由用户并可随时变更;数据压缩以块为单位,压缩算法由用户并可随时变更;
叶子节点可能合并或者分裂;叶子节点可能合并或者分裂;
所有叶子节点基本上是均匀的,随机地分布在多台 ChunkServer 机器上;
通常情况下每个叶子节点有 2~ 3 个副本;通常情况下每个叶子节点有 2~3 个副本;
叶子节点时负载平衡和任务调度的基本单元; 叶子节点时负载平衡和任务调度的基本单元;
支持 bloom filter 过滤;
增量数据的数据结构如下:
增量数据按照时间从旧到新划分为多个版本;
最新版本的数据为一颗内存中的 B+树,称为 Active Memtable;
用户的更新操作写入 Active Memtable,到达一定大小后,原有的 Active Memtable 将被冻结,并开启新的 Active Memtable 接受更新操作;
冻结的 Memtable 将以 SSTable 的形式转储到 SSD 中持久化;
每个 SSTable 内部按主键范围有序划分为多个块并内建块索引,每个块的大小通常为 4KB ~ 8KB 并内建块内行索引,一般不压缩;
UpdateServer 支持主备,增量数据通常为 2 个副本,每个副本支持 RAID1 存储;
7.3 可靠性与可用性
分布式系统需要处理各种故障,例如软件故障,服务器故障,网络故障,数据中心故障,地震,火灾,等。与其它分布式存储系统一样,OceanBase 通过冗余的方式保障了高可靠性和高可用性。
OceanBase 在 ChunkServer 中保存了基准数据的多个副本。单集群部署时一般会配置 3 个副本,主备集群部署时一般会配置每个集群 2 个副本,总共 4 个副本。
OceanBase 在 UpdateServer 中保存了增量数据的多个副本。UpdateServer 主备模式下主备两台机器各保存一个副本,另外,每台机器都通过软件的方式实现了 RAID1,将数据自动复制到多块磁盘,进一步增强了可靠性。
ChunkServer 的多个副本可以同时提供服务。Bigtable 以及 HBase 这样的系统服务节点不冗余,如果服务器出现故障,需要等待其它节点恢复成功才能提供服务,而 OceanBase 多个 ChunkServer 的 tablet 副本数据完全一致,可以同时提供服务。
UpdateServer 主备之间为热备,同一时刻只有一台机器为主 UpdateServer 提供写服务。如果主 UpdateServer 发生故障,OceanBase 能够在几秒中之内(一般为 3~5 秒)检测到并将服务切换到备机,备机几乎没有预热时间。
OceanBase 存储多个副本并没有带来太多的成本。当前的主流服务器的磁盘容量通常是富余的,例如 300GB×12 或 600GB×12 的服务器有 3TB 或 6TB 左右的磁盘总容量,但存储系统单机通常只能服务少得多的数据量。、
7.4 读写事务
在 OceanBase 系统中,用户的读写请求,即读写事务,都发给 MergeServer。MergeServer 解析这些读写事务的内容,例如词法和语法分析、schema 检查等。对于只读事务,由 MergeServer 发给相应的 ChunkServer 分别执行后再合并每个 ChunkServer 的执行结果;对于读写事务,由 MergeServer 进行预处理后,发送给 UpdateServer 执行。
只读事务执行流程如下:
MergeServer 解析 SQL 语句,词法分析、语法分析、预处理(schema 合法性检查、权限检查、数据类型检查等),最后生成逻辑执行计划和物理执行计划。
如果 SQL 请求只涉及单张表格,MergeServer 将请求拆分后同时发给多台 ChunkServer 并发执行,每台 ChunkServer 将读取的部分结果返回 MergeServer,由 MergeServer 来执行结果合并。如果 SQL 请求只涉及单张表格,MergeServer 将请求拆分后同时发给多台 ChunkServer 并发执行,每台 ChunkServer 将读取的部分结果返回 MergeServer,由 MergeServer 来执行结果合并。
如果 SQL 请求涉及多张表格,MergeServer 还需要执行联表、嵌套查询等操作。如果 SQL 请求涉及多张表格,MergeServer 还需要执行联表、嵌套查询等操作。
MergeServer 将最终结果返回给客户端。MergeServer 将最终结果返回给客户端。
读写事务执行流程如下:
与只读事务相同,MergeServer 首先解析 SQL 请求,得到物理执行计划。
MergeServer 请求 ChunkServer 获取需要读取的基线数据,并将物理执行计划和基线数据一起传给 UpdateServer。MergeServer 请求 ChunkServer 获取需要读取的基线数据,并将物理执行计划和基线数据一起传给 UpdateServer。
UpdateServer 根据物理执行计划执行读写事务,执行过程中需要使用 MergeServer 传入的基线数据。UpdateServer 根据物理执行计划执行读写事务,执行过程中需要使用 MergeServer 传入的基线数据。
UpdateServer 返回 MergeServer 操作成功或者失败,MergeServer 接着会把操作结果返回客户端。UpdateServer 返回 MergeServer 操作成功或者失败,MergeServer 接着会把操作结果返回客户端。
例如,假设某 SQL 语句为:“update t1 set c1 = c1 + 1 where rowkey=1”,即将表格 t1 中主键为 1 的 c1 列加 1,这一行数据存储在 ChunkServer 中,c1 列的值原来为 2012。那么,MergeServer 执行 SQL 时首先从 ChunkServer 读取主键为 1 的数据行的 c1 列,接着将读取结果(c1=2012)以及 SQL 语句的物理执行计划一起发送给 UpdateServer。UpdateServer 根据物理执行计划将 c1 加 1,即将 c1 变为 2013 并记录到 MemTable 中。当然,更新 MemTable 之前需要记录操作日志。
7.5 单点性能
OceanBase 架构的优势在于既支持跨行跨表事务,又支持存储服务器线性扩展。当然,这个架构也有一个明显的缺陷:UpdateServer 单点,这个问题限制了 OceanBase 集群的整体读写性能。
下面从内存容量、网络、磁盘等几个方面分析 UpdateServer 的读写性能。其实大部分数据库每天的修改次数相当有限,只有少数修改比较频繁的数据库才有每天几亿次的修改次数。另外,数据库平均每次修改涉及的数据量很少,很多时候只有几十个字节到几百个字节。假设数据库每天更新 1 亿次,平均每次需要消耗 100 字节,每天插入 1000 万次,平均每次需要消耗 1000 字节,那么,一天的修改量为:1 亿 * 100 + 1000 万 * 1000 = 20GB,如果内存数据结构膨胀 2 倍,占用内存只有 40GB。而当前主流的服务器都可以配置 96GB 内存,一些高档的服务器甚至可以配置 192GB,384GB 乃至更多内存。
从上面的分析可以看出,UpdateServer 的内存容量一般不会成为瓶颈。然而,服务器的内存毕竟有限,实际应用中仍然可能出现修改量超出内存的情况。例如,淘宝双 11 网购节数据库修改量暴涨,某些特殊应用每天的修改次数特别多或者每次修改的数据量特别大,DBA 数据订正时一次性写入大量数据。为此,UpdateServer 设计实现了几种方式解决内存容量问题,UpdateServer 的内存表达到一定大小时,可自动或者手工冻结并转储到 SSD 中,另外,OceanBase 支持通过定期合并或者数据分发的方式将 UpdateServer 的数据分散到集群中所有的 ChunkServer 机器中,这样不仅避免了 UpdateServer 单机数据容量问题,还能够使得读取操作往往只需要访问 UpdateServer 内存中的数据,避免访问 SSD 磁盘,提高了读取性能。
从网络角度看,假设每秒的读取次数为 20 万次,每次需要从 UpdateServer 中获取 100 字节,那么,读取操作占用的 UpdateServer 出口带宽为:20 万 * 100 = 20MB,远远没有达到千兆网卡带宽上限。另外,UpdateServer 还可以配置多块千兆网卡或者万兆网卡,例如,OceanBase 线上集群一般给 UpdateServer 配置 4 块千兆网卡。当然,如果软件层面没有做好,硬件特性将得不到充分发挥。针对 UpdateServer 全内存、收发的网络包一般比较小的特点,开发团队对 UpdateServer 的网络框架做了专门的优化,大大提高了每秒收发网络包的个数,使得网络不会成为瓶颈。
从磁盘的角度看,数据库事务需要首先将操作日志写入磁盘。如果每次写入都需要将数据刷入磁盘,而一块 SAS 磁盘每秒支持的 IOPS 很难超过 300,磁盘将很快成为瓶颈。为了解决这个问题,UpdateServer 在硬件上会配置一块带有缓存模块的 RAID 卡,UpdateServer 写操作日志只需要写入到 RAID 卡的缓存模块即可,延时可以控制在 1 毫秒之内。RAID 卡带电池,如果 UpdateServer 发生故障,比如机器突然停电,RAID 卡能够确保将缓存中的数据刷入磁盘,不会出现丢数据的情况。另外,UpdateServer 还实现了写事务的 group commit 机制,将多个用户写操作凑成一批一次性提交,进一步减少磁盘 IO 次数。
7.6 SSD 支持
磁盘随机 IO 是存储系统性能的决定因素,传统的 SAS 盘能够提供的 IOPS 不超过 300。关系数据库一般采用 Buffer Cache 的方式缓解这个问题,读取操作将磁盘中的页面缓存到 Buffer Cache 中,并通过 LRU 或者类似的方式淘汰不经常访问的页面;同样,写入操作也是将数据写入到 Buffer Cache 中,由 Buffer Cache 按照一定的策略将内存中页面的内容刷入磁盘。这种方式面临一些问题,例如 Cache 冷启动问题,即数据库刚启动时性能很差,需要将读取流量逐步切入。另外,这种方式不适合写入特别多的场景。
最近几年,SSD 磁盘取得了很大的进展,它不仅提供了非常好的随机读取性能,功耗也非常低,大有取代传统机械磁盘之势。一块普通的 SSD 磁盘可以提供 35000 IOPS 甚至更高,并提供 300MB/s 或以上的读出带宽。然而,SSD 盘的随机写性能并不理想。这是因为,尽管 SSD 的读和写以页(page,例如 4KB,8KB 等)为单位,但 SSD 写入前需要首先擦除已有内容,而擦除以块(block)为单位,一个(block)由若干个连续的页(page)组成,大小通常在 512KB ~ 2MB 左右。假如写入的页(page)有内容,即使只写入一个字节,SSD 也需要擦除整个 512KB ~ 2MB 大小的块(block),然后再写入整个页(page)的内容,这就是 SSD 的写入放大效应。虽然 SSD 硬件厂商都针对这个问题做了一些优化,但整体上看,随机写入不能发挥 SSD 的优势。
OceanBase 设计之初就认为 SSD 为大势所趋,整个系统设计时完全摒弃了随机写:除了操作日志总是顺序追加写入到普通 SAS 盘上,剩下的写请求都是对响应时间要求不是很高的批量顺序写,SSD 盘可以轻松应对,而大量查询请求的随机读,则发挥了 SSD 良好的随机读的特性。摒弃随机写,采用批量的顺序写,也使得固态盘的使用寿命不再成为问题:主流 SSD 盘使用 MLC SSD 芯片,而 MLC 号称可以擦写 1 万次(SLC 可以擦写 10 万次,但因成本高而较少使用),即使按最保守的 2500 次擦写次数计算,而且每天全部擦写一遍,其使用寿命为 2500/365=6.8 年。
7.7 数据正确性
数据丢失或者数据错误对于存储系统来说是一种灾难。前面 8.4.1 节中已经提到,OceanBase 设计为强一致性系统,设计方案上保证不丢数据。然而,TCP 协议传输、磁盘读写都可能出现数据错误,程序 Bug 则更为常见。为了防止各种因素导致的数据损毁,OceanBase 采取了以下数据校验措施:
数据存储校验
每个存储记录(通常是几个 KB 到几十 KB)同时保存 64 位 CRC 校验码,数据被访问时,重新计算和比对校验码。
数据传输校验
每个传输记录同时传输 64 位 CRC 校验码,数据被接收后,重新计算和比对校验码。
数据镜像校验
UpdateServer 在机群内有主 UpdateServer 和备 UpdateServer,集群间有主集群和备集群,这些 UpdateServer 的内存表(memtable)必须保持一致。为此,UpdateServer 为 memtable 生成一个校验码,memtable 每次更新时,校验码同步更新并记录在对应的 commit log 中。备 UpdateServer 收到 commit log 重放更新 memtable 时,也同步更新 memtable 校验码并与接收到的校验码对照。UpdateServer 重新启动后重放日志恢复 memtable 时也同步更新 memtable 校验码并与保存在每条 commit log 中校验码对照。
数据副本校验
定期合并时,新的 tablet 由各个 ChunkServer 独立地融合旧的 tablet 与冻结的 memtable 而生成,如果发生任何异常或者错误(比如程序 bug),同一 tablet 的多个副本可能不一致,则这种不一致可能随着定期合并而逐步累积或扩散且很难被发现,即使被察觉,也可能因为需要追溯较长时间而难以定位到源头。为了防止这种情况出现,ChunkServer 在定期合并生成新的 tablet 时,也同时为每个 tablet 生成一个校验码,并随新 tablet 汇报给 RootServer,以便 RootServer 核对同一 tablet 不同副本的校验码。
7.8 分层结构
OceanBase 对外提供的是与关系数据库一样的 SQL 操作接口,而内部却实现成一个线性可扩展的分布式系统。系统从逻辑实现上可以分为两个层次:分布式存储引擎层以及数据库功能层。
OceanBase 一期只实现了分布式存储引擎,这个存储引擎支持如下特性:
支持分布式数据结构,基线数据逻辑上构成一颗分布式 B+树,增量数据为内存中的 B+树;
支持目前 OceanBase 的所有分布式特性,包括数据分布、负载均衡、主备同步、容错、自动增加/减少服务器,等;
支持根据主键更新、插入、删除、随机读取一条记录,另外,支持根据主键范围顺序查找一段范围的记录;
二期的 OceanBase 版本在分布式存储引擎之上增加了 SQL 支持:
支持 SQL 语言以及 Mysql 协议,Mysql 客户端可以直接访问;
支持读写事务;
支持多版本并发控制;
支持读事务并发执行;
从另外一个角度看,OceanBase 融合了分布式存储系统和关系数据库这两种技术。通过分布式存储技术将基准数据分布到多台 ChunkServer,实现数据复制、负载均衡、服务器故障检测与自动容错,等等;UpdateServer 相当于一个高性能的内存数据库,底层采用关系数据库技术实现。我们后来发现,有一个号称“世界上最快的内存数据库”MemSQL 采用了和 OceanBase UpdateServer 类似的设计,在拥有 64 个 CPU 核心的服务器上实现了每秒 150 万次单行写事务。OceanBase 相当于 GFS + MemSQL,ChunkServer 的实现类似 GFS,UpdateServer 的实现类似 MemSQL,目标是成为可扩展的、支持每秒百万级单行事务操作的分布式数据库。
评论