Append写入方式的收益和成本分析

Append写入收益:

  1. SSD友好:追加写vs随机写
  2. CPU缓存友好:减少链表的访问方式
  3. CPU多核友好:
  • LockFree,无Page锁,可以做到行级锁或者行级无锁。提升多核场景下CPU缓存命中。
  • 追加写,不原地修改数据,提升多核CPU 缓存命中率。

Append成本分析:

首先分析Append方式对数据访问的影响:

在常见的数据库系统中,对数据的访问通常分为两大部分:确认在哪个Page,然后在Page内进行快速查找(比如N分查找)。

Append更新方式都是在page这个级别进行的,比如:

  1. BwTree是在物理Page上进行Append。
  2. RocksDB是在逻辑Page(某个范围的Row组成的逻辑Page)上进行Append。

其原因是于Row的粒度过于细,访问成本过高。具体分析是:由于Append更新,导致Page的物理位置常规性发生变化。这里可以采用Page层面甚至是SSt层面的路由进行进行管理。如果是在Row粒度上,要采用Row级别粒度的路由信息。路由信息大概在50倍的膨胀,路由信息难于管理,并且在Compaction过程中(后面会提到)开销也很高(因为移动数据的时候要改Row的路由信息)

由上述更新方式,导致Page的版本链较长。较长的版本链会导致CPU访问性能下降、IO次数变多。

Compaction的目标:

有上述分析可以得出,Compaction的目标是:

回收空间:回收过期数据的存储空间

降低版本链:(RocksDB的版本链就是L0的文件 + 多层的查找方式《待确认》)

Compaction的原则:

控制写入放大:

有目的的合并:减少版本链

可回收数据的标识:

RocksDB Compaction 成本分析(逐层缓存,Batch合并):

写入放大:

从L1到L2层的Compaction开始分析:

每层的存储,从范围上几乎是相当的,不同的是每层的数据量。因此造成了即每层单位数据量能够覆盖的数据范围是逐层下降的。从层上来看,密度是逐层上升的,即最后一层密度是100%。倒数第二次,密度是10%。

逐层缓存:RocksDB利用这个特征,可以保证数据下层过程中,每层的写放大都是10 +1。因为Ln层单位数据量覆盖的数据范围是Ln+1层的10倍,因此需要10个Ln+1层的数据块才可以满足Ln层的数据范围。所以是10 + 1。

所以总的写入放大是 4 + 11 (n-1),在2TB-3TB这个量级的数据量下,n=5

因此RocksDB的写入放大:

  • 在update场景,写入放大很低,因为分层结构天生支持冷热分离。写入放大是37,可以按n=4计算。
  • 在Insert场景,写入放大可能更低,对于主键这种递增插入的话,写入放大很低了,不涉及到合并。在Batch基础上可以做到个位数。对于二级索引这种随机插入的方式,由于二级索引本身数据量就较少,按照全量250GB计算,也是在37左右。
  • 所以整体来看,RocksDB的写入放大在40左右,或者40以内。

同时由于数据存储有较好的空间冷热属性(至少相对于写操作是这样),因此在空间回收过程中更有针对性。

有目的的合并:

通过数据有序,有目的的合并,减少逻辑Page上的版本链。

BwTree(LLAMA) 合并成本分析(这个可能不准):

由于论文在GC部分描述不清楚,且没有开放源码,所以首先描述我认为的问题、解决方案以及根据解决方案计算的写放大。

存在的问题:

  • 内存版本链长度,降低CPU开销。---这个还好,RocksDB也存在内存链的访问,具体是在计算BloomFilter的时候需要访问。所以这个不是太大,比如在7-8以内都还可以。
  • 存储上的版本链长度,导致读IO较高。--- 这里RocksDB是通过布隆过滤器实现1%的误判,来减少读IO的可能。
  • 空间回收的IO成本,它的空间是日志这种数据搬迁的方式,没有天生实现冷热分离。

解决方案:

  • merge delta record:内存中根据链表长度定期Merge
  • 将merge后的,新生成的delta record 持久化,减少磁盘上版本链的长度。降低IO开销(会增加部分写放大)。

 

 

======================================================

如果换成热点数据的10%的增量数据的话,实际上也不会有很大空间。大概百分之一?1TB大概有10GB?

感觉BwTree的问题在于IO不可控上,相比RocksDB,可以通过BloomFilter和BLockIndex,来保证命中率。降低IO次数。

同时,合并和Append Update-D是互斥的。

可以存在这样的策略:每个update-D同时记录链表长度,从某个链表节点就可以知道deltalink长度。

 

 

 

目的性差一些,存储都是无序的。这块也可以参考CF搞成关系性强一些的存储方案。但对于某一个表还是无序的。

因为目的性较差,所以在Merge Base和Delta链时,开销会高一些。

整体来看:

缩减版本链的写放大是在10 = 1 + 1(4个delta merge一次) + 10 = 12

空间清理写放大好像会多一些:

为了读取某个delta需要把周边的都读上来。

 

<未完>

你可能感兴趣的:(数据库)