乐趣区

关于数据库:数据库存储与索引技术三LSM树实现案例

咱们在前文介绍过,多款分布式数据库都应用了 LSM 树作为底层的存储引擎,其中就包含 TiDB 和 Oceanbase。与 TiDB 等数据库的存储引擎将 RocksDB 作为一个黑盒应用不同,尽管 OceanBase 的存储尽管也是基于 LSM 树,但却是齐全本人实现,并且和本人的存储引擎做了深度的定制和整合。与 RocksDB 中可能存在 5,6 层的 LSM 不同,OceanBase 的实现中,将 LSM 树划分为三层,第一层 MemTable,第二层 OceanBase 成为增量层,也称转储层 (即 LSM 树 C0 层),第三层 OB 称为基线层(即 LSM 树 C1) 层。与 RocksDB 中实现一样,OceanBase 的增量层 (C0) 多个 SSTable 之间 Key 会存在区间重叠的关系,读取的时候须要遍历查找每个增量 SSTable 能力确定 Key 是否存在,本文将以 OceanBase 3.x 版本为例,解说其如何针对这一问题进行优化。OceanBase 的基线层数据是全局有序的,所有 SSTable 之间的 Key 不会存在区间重叠的问题。

  1. MemTableOceanBase 数据库的内存存储引擎 MemTable 由 BTree 和 Hashtable 组成。在插入 / 更新 / 删除数据时,首先记录 Redo 日志,并通过 RPC 进行 Paxos 协定复制到其余少数正本,而后再将数据写入内存块,在 HashTable 和 BTree 中存储的均为指向对应数据的指针。正本通过接管 Master 节点发送过去的 Redo 日志进行重做回放将数据到本身的 MemTable。

    MemTable 在内存中保护了历史版本的事务,每⼀⾏将历史事务针对该⾏的操作依照工夫程序组织成⾏操作链,新事务提交时会往⾏操作链尾部追加新的⾏操作。如果⾏操作链保留的历史事务过多,将影响读取性能,此时须要触发内存 compaction 操作,交融这些历史事务以⽣成新的⾏操作链。两种索引构造各自有优缺点,独自无奈很好的实现数据库的点查和范畴查找的需要,所以 OB 将两者联合起来应用,惟一的代价就是在多线程并发环境下数据写入须要对两种数据结构都进行操作,实现绝对简单。数据结构长处毛病 HashTable· 插入一行数据的时候,须要先查看此行数据是否曾经存在,当且仅当数据不存在时能力插入,查看抵触时,Hashtable 要比 BTree 快。· 事务在插入或更新一行数据的时候,须要找到此行并对其进行上锁,避免其它事务批改此行,OceanBase 数据库的行锁放在 ObMvccRow 中,须要先找到它,能力上锁。不适宜对范畴查问应用 HashTable。BTree 范畴查找时,因为 BTree node 中的数据都是有序的,因而只须要搜寻部分的数据就能够了。单行的查找,也须要进行大量的 rowkey 比拟,从根结点找到叶子结点,而 rowkey 比拟性能是较差的,因而实践上性能比 HashTable 慢很多。2. SSTable 当 MemTable 的大小达到某个阈值后,OceanBase 数据库会将 MemTable 中的数据转存于磁盘上,转储后的构造称之为 SSTable。OceanBase 的 SSTable 是不定长的,其外部会被切分为 2MB 一个的定长数据块,OB 称之为宏块(Macro Block)。宏块是 OB 写数据到磁盘的根本 IO 单位。宏块外部会被划分为多个 16KB 左右的变长数据块,OB 称之为微块(Micro Block)。微块中会存储若干数据行(Row),微块是 OB 数据文件读取的根本单位。微块在结构时会写入间断的数据行,到 16KB 左右,而后再通过通用压缩算法(LZ4, ZSTD 等),而后再退出到宏块当中。宏块是定长的,大小为固定的 2MB,长度不能够被调整;微块默认大小为 16KB,经通用压缩后是变长的。OB 在进行 IO 读取时,会依照 4KB 来做 IO 对齐,因而一次 IO 读的长度并不一定与微块长度完全一致。微块长度能够被批改,通过以下的语句能够对于不同的表设置不同的微块长度:ALTER TABLE mytest SET block_size = 131072; 一般来说微块长度越大,数据的压缩比会越高,但相应的一次 IO 读的代价也会越大;微块长度越小,数据的压缩比会越低,但相应的一次 IO 读的代价会更小。

  2. 数据压缩 3.1. 传统关系数据库数据压缩实现传统关系型数据库实践和架构在 20 世纪 70,80 年代被确定下来,直到 2010 年的挪动互联网时代之前,其架构都没有大的扭转。晚期数据库系统,CPU 算力较弱,而数据压缩是十分消耗 CPU 的操作,因而传统基于页面行存的关系数据库中一开始都没有为数据压缩进行顶层设计。到互联网时代面对海量数据,传统的关系型数据库也开始思考减少压缩个性,升高用户的存储老本。传统的关系型数据库的压缩,次要有两个方向,一个是针对 OLTP,即事务处理的 workload 提供的计划。另外一种是针对 OLAP,即数据分析型的 workload 提供的计划。针对 OLTP 的 workload 提供的计划次要是基于页面的通用压缩。OLTP 型的 workload,通常须要对行内的数据进行更改,一般来说,采纳行存储性能较好。这种负载下,一个页面内的行可能会被下层利用重复批改,因而行与行之间的数据压缩根本难以进行,支流厂商个别都是才用了基于页面的整体通用压缩。以 MySQL 的 InnoDB 引擎为例,一个页面未压缩前,在磁盘上占用 16KB 的空间,压缩后只有 11KB,那么依照 4KB 进行 IO 对齐的话,压缩后的数据占用 12KB 的磁盘空间。InnoDB 的这个页面在文件中依然占用 16KB 的空间,然而利用操作系统的稠密文件 (Sparse File) 个性,操作系统理论只为这个页面申请了 12KB 的磁盘空间,因而能够节约 4KB 的空间。

    这种压缩形式也会带来很多问题:如果一个页面被批改很频繁的话,重复的读出和写入数据的过程中须要进行压缩 / 解压,对 CPU 的开销也不可疏忽。如果页面被批改并压缩后,其大小比原来减少了,如原来压缩后是 12KB,当初压缩后是 13KB。操作系统须要为新减少的数据调配磁盘空间,此时调配的磁盘空间通常都不是和之前的 12KB 的空间间断的。这样对这个页面的读取,IO 是不间断的,性能会变差。总结起来就是,传统数据库的基于行存页面的压缩,通常状况下,只能帮忙用户节约一部分存储空间,并不会带来性能的晋升。如果使用不当,性能有可能还会降落。OLAP 型负载的数据压缩,通常数据都是只读的,没有了数据更新的限度,数据压缩做的十分激进。在页面内的数据能够不按行存,而是依照列存。雷同列的数据放在一起,能够做十分多的压缩,如常量编码,字典编码,Delta 编码,RLE 编码等等。这些数据编码能够无效缩小页面的存储空间,同时不会对数据查问有任何的影响。因为 OB 也采纳了相似的压缩计划,因而这里的列存压缩计划,会放到 OB 的压缩实现中去讲。传统的基于页面存储的关系数据库还有一个问题就是,OLAP 和 OLTP 两种负载的不可和谐性。要想 OLAP 解决能力强,数据个别都是行存的;要想 OLTP 解决能力强,数据须要是列存的。如果要想两者都要(小孩才做抉择,成年人全都要),那么必须要部署两套异构的存储(只管数据库服务程序可能是一个 / 一套),一套专门用于 OLTP,一套用于 OLAP,二者之间还须要通过数据链路进行同步。这无疑显著减少了硬件和运维治理的老本。3.2. LSM 树数据压缩劣势综述基于 LSM 的存储引擎,其在磁盘上的数据,在下一次合并之前,都是只读而不会被批改的。通过 BigTable 和 LevelDB 引入的 SSTable,SSTable 外部再分 Block 存储的概念目前曾经是 LSM 树实现的标配。这些只读的 Block,类比传统数据库的页的话,是十分好的压缩指标。相比 OLTP 数据库的页面,SSTable 的 Block 压缩有以下几个劣势:数据只读,不会因为重复批改而带来重复的压缩 / 解压的 CPU 耗费,也不会因为重复批改带来的数据收缩导致的 IO 不间断。数据内聚,一个 Block 内通常有几十乃至更多的行,能够在 Block 写入的时候一次性的针对这些行做列式压缩,能够失去更好的压缩比,而且能够减速后续的查问。业内基于 LSM 的存储其实也看到了这些压缩劣势并有了相当的实现,如 Facebook 提出的 RCFile。然而这类利用通常也都是 Hadoop 等批处理场景,理论在 OLTP 数据库场景使用这种压缩计划的,据理解就 Spanner 和 OceanBase。

    3.3. OB 数据压缩实现咱们后面提到,OB 的 SSTable 的格局,是分为 SSTable,Macro Block, Micro Block 三级。OB 的开源 3.x 版本只实现了 Micro Block 内的行存储,外部版本中实现了 Micro Block 内的列存储。OB 在 Micro Block 内基于列存储进行高效的编码压缩,而后在整体以 Micro Block 为单位进行通用算法压缩。依照 OB 晚期的文章的声称,客户在进行 MySQL 数据迁徙的时候,教训公式是依照 6:1 进行容量布局的,即 OB 依照列式编码压缩 + 通用压缩之后的数据,只有等同 MySQL 的 1 /6(该论断未做深刻考据,不确定是否蕴含 OB 三正本,以及 MySQL 数据是否启用压缩,仅供参考)。3.3.1. 通用压缩咱们当初能见到的,压缩解压代价绝对可控的通用压缩算法,根本都是基于 Abraham Lempel 与 Jacob Ziv 在 1977/1978 论文中发表的 LZ77/LZ78 算法变种得来,如 LZW,LZ4,Snappy,ZSTD 等。LZ 系的通用压缩算法实质是一种在字节流上的基于滑动窗口的字典压缩算法。算法会动静的在最近一段数据流中寻找反复呈现的字节片段,而后进行压缩。

    OB 在 Micro Block 粒度是反对抉择通用压缩算法来对整个 Micro Block 进行通用压缩的,用户能够通过表的属性来进行设置。通用压缩尽管也能带来肯定的数据压缩比,然而这是有性能损失的。OB 的 Micro Block 在压缩前是 16KB,是一次读 IO 读取的数据的大小。当 OB 的 Micro Block 被通用压缩之后,OB 一次 IO 的大小依然是 16KB。压缩后的数据还须要解压之后能力应用。即是是以后最快的通用压缩算法 LZ4,其解压速度也只有约 5GB/S,只有内存数据拷贝 (memcpy) 的 35% 左右。CompressorRatioCompressionDecompressionmemcpy1.00013700 MB/s13700 MB/sLZ4 default (v1.9.0)2.101780 MB/s4970 MB/sLZO 2.092.108670 MB/s860 MB/sSnappy 1.1.42.091565 MB/s1950 MB/sZstandard 1.4.0 -12.883515 MB/s1380 MB/sLZ4 HC -9 (v1.9.0)2.72141 MB/s4900 MB/ s 况且,通用的数据压缩算法并不是为数据库专门设计的,这些压缩算法将输出看成是一个间断的字节流,并不对数据的 pattern 做任何先验假如。但实际上数据库中寄存的是结构化数据,是有明确的 schema 的,每行数据每个字段都有明确的类型和大小。利用这些信息,采纳肯定的数据编码技术,就能够实现比间接应用通用压缩算法好得多的压缩成果。这些数据编码技术,岂但能进步数据的压缩比,还能根本不升高数据的查问效率。某些查问状况下,甚至还能减速查问。3.3.2. 编码压缩 OB 中的 Micro Block 正是在列存的根底上,联合了数据 schema,进行了一系列的高效的数据编码压缩。其次要的数据编码压缩算法有以下几类。字典编码:所有数据编码技术中最罕用、也是最无效的办法。字典编码的思维很简略,就是把重复性较高的数据进行去重,把去重后的数据建设字典,而把原来存放数据的中央存成指向特定字典下标的援用。数据的拜访逻辑是十分间接的,没有解码过程。特地要留神,字典中的各数据是按类型序排序的,这样做有两个益处:一是有序的数据更有利于压缩;二是有序的数据意味着咱们在做谓词计算时,能够将谓词逻辑间接下压到字典,并通过二分逻辑实现疾速迭代,这点在咱们反对简单计算时,将施展十分重要的作用。RLE 编码:实用于间断相等的数据,比方形如 1,1,1,2,2,… 之类的,咱们能够把这些间断相等的数据去重,并只保留其起始行号,RLE 编码在数据库中次要用于解决有序的数据(比方索引的前缀)。常量编码:针对近似常量化的数据,编码辨认一个最常见的数据作为常量,而只记录所有不等于这个常量的异样值和它们的行号,常量编码在理论业务数据中十分有用,后者往往存在着一些业务减少、然而并不应用的字段,这些字段同样占据了空间,并呈现出常量化(默认值)的数据分布。差值编码(数值字段):实用于在一个小值域范畴内散布的整数型数据,通过计算区间内的 MIN、MAX,将数据减去 MIN 后,用更小的位宽进行编码。比拟常见的数据是工夫类型,间断生成的工夫类型数据通常有十分好的局部性(比方差值在几秒钟以内)。差值编码(字符字段):实用于定长的、有确定业务规定的字符型数据,通过结构公共串,将数据中只记录差别局部。这类数据在业务中十分常见,通常用于主键,比方订单号、用户账号等。前缀编码:实用于前缀雷同的字符型数据,通过结构公共前缀,将数据中只记录差别的后缀。其它一些数据库,比方 SQL Server、HBase 中也常常应用,这类数据在业务中也很常见,比方局部主键。后面提到的数据编码都是列内编码,即只思考同一列内各字段的数据相似性,除此之外,OceanBase 还实现了一组列间的数据编码,也就是思考同一行的不同列间数据的相似性。列间编码(数值字段):实用于两列近似相等的整数型数据,这样,其中的一列只须要存储和另一列的差别值。这种列间相等的数据编码有它很强的实用场景,比方咱们晓得业务在数据表中通常都会记录生成和最近一次更新的工夫戳,如果某些流水数据从不会产生更新,那么这两个字段就存在很强的列间相等关系。列间编码(字符字段):实用于两列存在子串关系的字符型数据,也就是一列是另一列的子串(包含前缀、后缀、相等关系)。理论业务中许多长字符型字段并不是由用户随便生成的,而是基于肯定的业务规定,将多个根底字段拼接而成,这时候这些根底字段就和前者存在子串关系。

    OB 所采纳的这些编码压缩算法,解码过程非常简单,解码特定一行的数据,并不需要依赖其它行,这种近似 O(1)复杂度的设计进步了数据投影的性能,使得其可能被用于在线零碎。尽管这些编码压缩算法解码简略,然而编码过程却并不那么 trivial。只管 OB 晓得每张表每一列数据的属性,然而数据的散布 OB 却并没那么的理解,如某一列尽管是 64 位整数,然而 OB 事先并不一定晓得它所有值都是一个无限的枚举类型,只有等到须要 Compact 压缩一个 Micro Block 的时候能力晓得。为了加重 DBA 的心智累赘,OB 并不心愿把每一列的编码压缩算法都由 DBA 来指定,而心愿是 OB 本人依据以后数据的散布来智能抉择。这就波及到一个比拟大的问题,如何智能的为每一列来抉择最优的编码压缩算法。OB 的做法是如果上一个 Micro Block 某一列通过计算抉择了某种编码算法,那么如果数据分布稳固的话,下一个 Micro Block 的雷同列大概率最优的编码算法跟上一个 Block 一样。OB 会优先尝试通过这个编码算法对列进行压缩,如果压缩后的后果 (压缩比) 证实这个抉择正确,那么就会采纳这个编码算法来压缩当前列;否则会退回从新抉择适合的编码算法。OB 的公开材料示意,这块还有较大的优化空间。而且业内目前也有一些新鲜的想法,如通过机器学习来预测数据的最优编码压缩算法。置信后续 OB 的新版本中会有继续改良和晋升。3.4. TiDB 数据压缩实现 TiDB 的存储层 TiKV 是根底 RocksDB 的,前文咱们也提到,RocksDB 是在 LevelDB 的根底上改良来的,而原始的 LevelDB 只实现了基于 Block 的整体通用压缩,并未在 Block 内采纳列存及数据编码压缩。因而采纳 RocksDB 作为底层存储的 TiDB,也未应用 Block 内的列存及数据编码压缩。TiDB 为了应答 OLAP 类需要,专门另外波及并实现了一套存储格局,采纳了列存的形式,此处不展开讨论,有趣味的同学能够参考文档(PS: TiDB 不愧是开源标杆,文档十分欠缺,轻易翻翻也能学习到不少货色)。部署时,须要部署专门的从库来接管主库的数据同步,而后改为列存存储以应答 OLAP 查问需要。

  3. 缓存策略因为很多数据存储于 SSTable,为了减速查问,OB 须要对数据进行缓存。OceanBase 数据库并不需要对缓存的大小进行设置,相似于 Linux 对于 page cache 的控制策略,OceanBase 数据库会尽量应用租户的内存,直到租户的内存达到肯定阈值后,才会触发对 Cache 的淘汰。同时 OceanBase 数据库也是一个多租户零碎,对于每一个租户都会有各自的 Cache,但 OceanBase 数据库会对所有租户的缓存进行对立治理。4.1. Row Cache 传统关系型数据库并不对独自的某行进行缓存,OB 基于 LSM 的存储引擎却实现了行级缓存。OB 的 Row Cache 缓存具体的数据行,在进行 Get/MultiGet 查问时,可能会将对应查到的数据行放入 Row Cache,这样在进行热点行的查问时,就能够极大地晋升查问性能。4.2. Block Cache 传统的关系数据库中,缓存是以 IO 页面为单位进行缓存的。在 LSM 为存储引擎的数据库中,IO 页面为单位的缓存策略也须要随着 LSM 构造来进行适配。咱们之前提到过,OB 的 IO 读的最小单位是微块,OB 的 Block Cache 是以微块 (Micro Block) 为单位的,其缓存的数据是解压缩后的微块数据,大小是变长的。另外,数据查问的时候,如果没有在 Row Cache 或者 MemTable 中查问到数据话,咱们须要去 Block Cache 或者磁盘中查问数据。查问数据的第一步是须要先确定数据在哪个微块,这也须要咱们将一部分微块的索引缓存在内存中。OB 的 Block Index Cache 即缓存微块的索引,相似于 BTree 的中间层,在数据结构上和 Block Cache 有一些区别,因为中间层通常不大,Block Index Cache 的命中率通常都比拟高。4.3. Filter CacheLSM 树是基于排序的的存储构造,这类构造通常在进行点查问的时候,对不存在的数据的查问和确认是不高效的。不存在的数据,通常咱们在内存中也无奈缓存,内存查问也就无奈命中,只有通过 IO 去读取数据所在范畴的 SSTable(微块)来确认数据是否存在。这样对于数据的存在性查问将变得十分低效。因而各种 LSM 树的实现中都引入了 Blook Filter 来进行存在性过滤,OB 也在宏块 (Macro Block) 层引入了 Bloom Filter 用于数据存在性过滤。

    Bloom Filter 是一种只能插入元素,无奈删除元素的数据结构。在传统的关系数据库中,IO 页面 (Page) 随时可能会被利用批改,一行数据随时都可能被删除,因而无奈高效的应用 Bloom Filter 来作为空数据过滤器。而基于 LSM 树存储引擎则不同,其磁盘上的数据,在下一次合并 (Compaction) 之前,都是不可变的。Compaction 间隔时间通常都是以小时为单位,这种场景非常适合 Bloom Filter 的应用。Bloom Filter 也是一种能够非常容易通过空间来调整查问错误率的数据结构,利用能够轻易的通过调整每个元素所占用的空间 (Row per bits),来调整查问的错误率(即数据不在 SSTable 中,然而通过 Bloom Filter 查问被确认存在的概率)。OB 中的 Bloom Filter 的构建策略是按需构建,当一个宏块上的空查次数超过某个阈值时,就会主动构建 Bloom Filter,并将 Bloom Filter 放入 Cache。5. 合并策略后面咱们曾经讲过合并是 LSM 树类存储系统中最简单,也是对系统性能 / 稳定性影响最大的一个过程。因而,OB 实现的 LSM 树在数据合并上也是进行了十分粗疏的波及,提供了多种合并策略,力求合并过程平滑可控。OceanBase 的合并策略,依据表上是否有 DDL 操作,如加列,减列,批改压缩算法等,会才去不同的策略。当表上无 DDL 操作时,OceanBase 次要采取增量合并策略;反之则会采纳渐进合并或者全量合并策略。5.1. 增量合并增量合并的状况下,咱们晓得表的 Schema 没有批改,因而能够在合并的时候,在不同粒度上看数据是否能够重用。OB 的 Macro Block 划分为 2M 一个,这个是一个绝对较小的尺寸。当表绝对较大的时候,如一张 100G 的表,零碎里会有 102400 个 Macro Block。数据更新的时候,这些 Macro Block 中很多有可能都没有被批改过,因而能够间接重用,这种形式称之为增量合并。绝对于全量合并的把所有的宏块的重写一边而言,增量合并只重写产生了批改的宏块。增量合并极大地缩小了合并的工作量,也是 OceanBase 数据库目前默认的合并算法。更进一步地,对于宏块外部的微块,很多状况下也并不是所有的微块都会被批改。当发现宏块有行被批改过期,在解决每一个微块时,会先判断这个微块是否有行被批改过,如果没有,只须要把这个微块的数据间接拷贝到新的宏块上,这样没被批改过的微块就省去了解析行、抉择编码规定、对行进行编码以及计算列 Checksum 等操作。微块级增量合并进一步缩小了合并的工夫。5.2. 渐进合并在执行某些 DDL 操作时,例如执行表的加列、减列、批改压缩算法等操作后,数据须要齐全重写一遍能力在磁盘上失效。OceanBase 数据库并不会立刻对数据执行重写操作,而是将重写动作提早到合并时进行。基于增量合并的形式,无奈实现对未修改数据的重写,为此 OceanBase 数据库引入了“渐进合并”,即把数据的重写扩散到屡次合并中去做,在一次合并中只进行局部数据的重写。用户能够通过参数指定渐进合并的轮次,如:ALTER TABLE mytest SET progressive_merge_num=60;
    示意将 mytest 表的渐进轮次设置为 60,在 DDL 操作实现之后的 60 次渐进合并过程中,每一次合并会重写 60 分之一的数据,在 60 轮合并过后,数据就被整体重写了一遍。当未对表的 progressive_merge_num 进行设置时,其默认值为 0,目前语义为在执行须要重写数据的 DDL 操作之后,做渐进轮次为 100 的渐进合并。当表的 progressive_merge_num 被设置为 1 时,示意强制走全量合并。5.3. 全量合并 OB 的全量合并和 HBase 与 Rocksdb 的 Major Compaction 过程是相似的。顾名思义,在全量合并过程中,会把以后的基线数据都读取进去,和增量数据合并后,再写到磁盘下来作为新的基线数据。在这个过程中,会把所有数据都重写一遍。全量合并会极大的消耗磁盘 IO 和空间,如非必要或者 DBA 强制指定,OceanBase 数据库个别不会被动做全量合并。OceanBase 数据库发动的全量合并个别产生在列类型批改等 DDL 操作之后。DDL 变更是实时失效的,不阻塞读写,也不会影响到多正本间的 Paxos 同步,将对存储数据的变更延后到合并的时候来做,这时就须要将所有数据重写一遍。5.4. 轮转合并一般来说合并会在业务低峰期进行,但并不是所有业务都有业务低峰期。在合并期间,会耗费比拟多的 CPU 和 IO,此时如果有大量业务申请,势必会对业务造成影响。为了躲避合并对业务的影响。借助 OceanBase 数据库的多正本分布式架构,引入了轮转合并的机制。个别配置下,OceanBase 数据库会同时有 3 个数据正本,当一个数据正本在进行合并时,能够将这个正本上的查问流量切到其余没在合并的集群下面,这样业务的查问就不受每日合并的影响。等这个正本合并实现后,再将查问流量切回来,持续做其余正本的合并,这一机制称之为轮转合并。为了防止流量切过去后,Cache 较冷造成的 RT 稳定,在流量切换之前,OceanBase 数据库还会做 Cache 的预热,通过参数能够管制预热的工夫。6. 总结到此,《数据库存储与索引技术》系列的内容就完结了。存储和索引技术作为数据库底层的重要根底,把握其相干常识有助于咱们进一步提高对数据库底层原理的意识。心愿通过本系列三篇文章的介绍,可能让大家对数据库底层技术以及目前国内支流的分布式数据库底层实现有所理解,在大家当前的工作中可能有所帮忙!参考文献 1. Oceanbase 文档系列• OB 数据库存储架构:https://www.oceanbase.com/docs/oceanbase-database/oceanbase-d…• OB 数据存储管理:https://www.oceanbase.com/docs/oceanbase-database/oceanbase-d…• OB 数据库压缩个性:https://zhuanlan.zhihu.com/p/49161275• OB 存储引擎详解:https://www.modb.pro/db/137286• OB 博客文章全系列:https://open.oceanbase.com/articles2. 其余• LZ4 Benchmark : https://github.com/lz4/lz4

退出移动版