OceanBase 源码解读(六):存储引擎详解
OceanBase 是否依赖其他开源KV数据库(例如:LevelDB、RocksDB)?
OceanBase 底层引擎是什么?是KV吗 ?
OceanBase 内存结构是 B+Tree 还是 LSMTree ?
OceanBase 如何实现高性能服务?
背景
update-in-place :原地更新,较常见于传统关系型数据库( MySQL、Oracle )采用的 B+Tree 结构。优点:更新记录时对原有记录进行覆盖写。有较好的数据局部性,对扫描比较友好。缺点:是引入大量的随机写,同时还有一定的并发问题;并且与业务流量叠加,对业务流量有一定的影响;
log-structure storage:日志更新,例如LevelDB、RocksDB、HBase、BigTable 等采用的 LSMTree 结构。优点:日志更新无锁,不会引入并发问题,能够保证高效写入,并且没有空间碎片。缺点:是读路径变长,例如 LSMTree 结构在扫描时需要读取 memtable 、L0层及其余层的数据,并进行归并,需要通过异步 compaction 进行GC以及均衡各个层级的数据来避免过多的读放大。
01 单机引擎
读写分离架构
OceanBase 的存储引擎采用分层 LSMTree 结构,数据分为两部分:基线数据和增量数据。
基线数据是持久到磁盘上的数据,一旦生成就不会再修改,称之为 SSTable。
增量数据存在于内存,用户写入都是先写到增量数据,称之为 MemTable,通过 Redo Log 来保证事务性。
当 MemTable 达到一定阈值时会触发冻结( Frozen MemTable ),并重新开启一个新的 MemTable( Active MemTable ),Frozen MemTable 被转存到转储 SSTable 中,然后在合并( LSMTree 结构特有的 compaction 动作 )时将转储 SSTable 合并入基线 SSTable,生成新的 SSTable 。
在查询时,需要将 MemTable 和 SSTable 的数据进行归并,才能得到最终的查询结果。
系统为基线数据和增量数据指定不同的版本,数据版本是连续递增的。
每生成一个新的 Active MemTable,都会设置为上个 MemTable 的版本加1(实际生产中,两次合并之间会有多次转储,这两次合并之间生成的所有MemTable 表示一个大版本,每个 MemTable 会用小版本表示,例如下图中的v3.1和v3.2)。当 SSTable 与 Frozen MemTable 合并之后,也会将版本设置为合并的 Frozen MemTable 的版本。
02 分布式架构
数据分区
OceanBase 的两种方式:
① 引入传统关系数据库中的数据分区表( Partition )的概念;
② 兼容传统关系型数据库的分区表语法,支持 hash 分区和 range 分区。
支持二级分区机制,例如对于历史库场景,单表数据量庞大,一级按用户分区,二级按时间分区。从而很好的解决大用户扩展性的问题,如果要删除过期数据,可以通过 drop 分区实现。
也支持生成列分区,生成列指这一列由其他列计算所得,该功能能够满足期望将某些字段进行一定处理后作为分区键的需求。
对分区表查询进行优化:
① 对于查询中包含分区键或者分区表达式的查询,能够准确的计算出该查询对应的分区,称为分区裁剪;
② 对于多分区查询,能够使用分区间并行查询、分区间排序消除、partition-wise join 等技术,从而大大提升查询性能。
同一数据分区的副本构成一个 Paxos Group,自主进行选主,推举出其中某个副本作为 Leader,其他副本自动成为 Follower,后续所有针对这个数据分区的读写请求,都会自动路由到对应的 Leader 进行服务。
OceanBase 采用 share-nothing 的分布式架构,每个 OBServer 都是对等的,管理不同的数据分区。
① 对一个数据分区所有读写操作都在其所在的 OBServer 完成,属于单分区事务;
② 多个数据分区的事务,采用两阶段提交的方式在多个 OBServer 上执行,属于分布式事务。
单机多分区事务,依然需要走两阶段提交,且针对单机做了专门的优化。分布式事务会增加事务延迟,可以通过表格组(table group) 将经常一起访问的多张表格聚集在一起,同一表格组的分区具有相同的 OBServer 分布,且 Leader 位于同一台机器上,避免跨机事务。
传统关系型数据库也支持分区功能,但所有分区仍存放在同一台机器上。
而 OceanBase 能够把所有分区打散到不同物理机器,从而能够真正体现出分布式架构的优势,从而彻底解决可扩展性问题。
当容量或者服务能力不足时,只需要增加更多的数据分区并打散到更多的 OBServer 即可,从而可以通过在线线性扩展的方式提升整体读写性能。在同样系统容量充足或者处理能力富余时,将机器下线,降低成本,提供良好的弹性伸缩能力。
多副本Paxos同步
OceanBase 中的数据分区冗余有多个副本(例如,同城三副本部署架构为3个,三地五副本部署架构为5个),分布于多个 OBServer 上。
事务提交时利用 Paxos 协议在多个副本间达成多数派提交,从而维护副本之间的一致性。当单台 OBServer 宕机时可以维护数据的完整性,并在较短的时间内恢复数据访问,达到 RPO=0、RTO<30秒的SLA。
用户无需关心数据所在的具体位置,客户端会根据用户请求来定位数据所在的位置,从而将读写请求发送给 Leader 进行处理,对外仍然展现为一个单机数据库。
基于以上介绍的多副本架构我们引入了轮转合并的概念,将用户请求流量与合并过程错开来。
比如:一个OceanBase集群中Partition的副本个数为3,这三个副本分布在3个不同的Zone(1,2,3)中,RootService在控制合并时,比如先合并Zone3,会首先将用户流量切到Zone(1,2)中,只要切换所有Partition的Leader到Zone(1,2)即可。Zone3合并完成之后,准备合并Zone2,则将Zone2的流量切走,之后再合并Zone1,最后三个副本全部合并完毕,恢复初始的Leader分布。
多副本架构带来了三个比较大的架构提升:
数据库服务的可用性得到提升,如果某个 OBServer 突发宕机或者网络分区,自动、迅速的把故障 OBServer 的读写服务切换到其他 OBServer 上,RPO=0,RTO<30秒。传统关系型数据库通过主备架构来容灾,在不使用共享存储的情况下,难以完美做到在主库故障时数据零丢失,且由于数据一致性问题无法自动切换,切换效率也难以保证;
数据库的资源使用率得到提升,利用 OceanBase 多库多活的特性,配置让三个 Zone 中的两个提供读写服务,第三个 Zone 作为热备库接受事务日志,随时待命提供读写服务。OceanBase 的机器使用率达到了2/3,而传统关系型数据库主备架构只能使用到1/2的机器;
数据库的数据压缩率得到提升,由于轮转合并的引入,用户请求流量与合并过程错峰,正在合并的 Zone 的 CPU 和磁盘IO可以大量用于复杂的数据编码/压缩,并选择最优的编码算法,并且对业务数据写入零影响。高压缩率不但节省了存储空间,同时也会极大的提升查询性能。传统关系型数据库原地更新数据,与业务流量叠加,在“高压缩率”和“低计算成本”两者之间只能选择后者,甚至建议用户谨慎使用该功能。
03 总结
基于 LSMTree 的单机引擎和基于多副本强同步的分布式架构,才是完整的 OceanBase 存储引擎。这种存储引擎有众多好处,既有类似于 Oracle 的关系型数据库上层,又有类似于 spanner 的分布式底层。
这是 OceanBase 的核心能力,能够规避 LSMTree 缺陷,获得极致性能和丝般顺滑,支持最核心的 OLTP 业务。同时又获得了分布式架构的优势,包括持续可用、线性扩展、自动容错、低成本等特性。
//作者感言
往期推荐