Characterizing, Modeling, and Benchmarking RocksDB Key-Value Workloads at Facebook

FAST'20

Zhichao Cao, University of Minnesota, Twin Cities, and Facebook; Siying Dong and Sagar Vemuri, Facebook; David H.C. Du, University of Minnesota, Twin Cities

本文主要关注真实生产环境下的RocksDB的负载特征,以及当前常用的Benchmark——YCSB能否在测试的时候还原生产环境的负载特征展开。主要收集了Facebook中三个使用RocksDB作为底层存储引擎的组件的trace数据,分别是1)UDB:用于MySQL中用来存储社交图数据,使用RocksDB作为底层存储;2)ZippyDB:用于存储分布式对象存储的元数据的分布式KV;3)UP2X:用于存储AI/ML数据的分布式KV。然后根据收集的数据分析到达RocksDB的各种负载特征,并与YCSB的测试结果进行对比,得出YCSB并不能准确代表生产环境的结论,而这其中最关键的因素是YCSB没有考虑到真实负载所表现出的key space loaclity(我的理解是key range的局部性,即热点key会按照key range进行分布,而不是随机分布在整个key空间内)。最后根据收集到的负载特征进行建模,自己构建了一个benchmark,达到了更加准确地还原生产环境负载的目的。

问题背景 & Motivation

  1. 现有的关于真实workload的描述以及分析的工作很少,然而KV Store的性能又跟workload十分相关
  2. 针对KV Store的workload的分析方法与传统的存储系统(诸如块存储系统或者文件系统)有较大区别
  3. 使用benchmark测试KV Store的性能的时候,并不知道benchmark生成的workload能否代表真实的workload

三个系统的介绍

UDB

UDB是Facebook用来存储社交图数据(社交关系图)的组件,向上提供MySQL接口,UDB上层有读cache,read miss以及write数据都通过UDB处理,数据通过MyRocks转换为KV并写入RocksDB。数据主要由objectassociations构成,分别代表图的点和边。

UDB的RocksDB通过6个ColumnFamily来存储不同类型的数据,分别是:

Object:存储object数据

Assoc:存储associations数据

Assoc_count:存储每个object的association数

Object_2ry,Assoc_2ry:object和association的secondary index

Non-SG:存储其他非社交图数据相关的服务

ZippyDB

ZippyDB是一个分布式的KV Store,并通过Paxos协议保证一致性,KV数据被分为不同的shard,每个shard通过一个单独的RocksDB实例进行存储。ZippyDB主要用于存储上层的对象存储的元数据。

UP2X

UP2X是一个分布式KV Store,在Facebook中主要被用来存储AI/ML计算的数据集,AL/ML的数据集的特点是经常被Update,如果先Get数据再Put则效率很低,所以UP2X主要使用RocksDB的Merge来实现Read-Modify-Write(Merge的实现就是将要修改的数据的delta数据put进去,避免随机读开销,delta数据在compaction的时候会被删除)

主要的分析工具

  • Tracing:收集RocksDB的操作,主要记录操作类型,CF,key,value,时间戳
  • Trace Replaying:通过db_bench回放trace file
  • Trace Analyzing:分析workload,但是因为生产环境的workload涉及隐私不能公布,所以最终输出内容是一些描述,主要包括:1)每个CF内KV的操作次数,操作类型;2)KV size的统计数据;3)KV数据热度(Popularity?);4)key空间的局部性;5)QPS统计
  • Modeling and Benchmarking:对workload进行建模

workload分析

workload常规统计分析

查询操作的组成

结论:Get在UDB与ZippyDB中占比最多,UP2X最多的操作是Merge

UDB

UDB query composition
  • Get/Put/Iterator是占比最高的操作,而且主要是在Object / Assoc / Non_SG这几个CF
  • Object_2ry中Iterator为主要操作
  • 没有重复update操作的CF,如Assoc_2ry通过Single_Delete删除数据

ZippyDB

  • 只有一个CF,Get : Put : Delete : Iterator = 78 : 13 : 6 : 3

UP2X

  • Merge : Get : Put = 92.53 : 7.46 : 0.01

KV热点分布

结论:UDB和ZippyDB中大部分KV数据是冷数据

UDB:

KVP access CDF of UDB

上图是UDB的Get与Put操作的KV数据访问的CDF图。对于Get,除了Assoc之外,其他CF的数据60%或者以上的数据都只被访问了一次。对于Put,超过75%的数据都只被访问一次,Put次数超过10的数据仅占不到2%,所以UDB中的KV数据大部分很少被Update。

KVP scan and scan length CDF of UDB

上图是UDB的Iterator操作的start key统计以及scan length统计,大部分的scan起始key都只被访问一次,没有表现出局部性,而scan长度超过65%都只有1次,但也有少部分的scan长度非常长。

Unique key access

上表是统计的UDB在一段时间内访问的unique key的占比,可以看到24小时内访问的key最高不超过3%,而14天内访问的key最高不超过15%,因此对UDB来说,大部分数据在RocksDB内都是冷数据

ZippyDB:

ZippyDB access

对于ZippyDB,大约80%的key只被访问一次,1%的key被访问超过100次,因此表现出较好的局部性。约73%的数据只被Put一次,访问次数超过10次的数据仅有0.001%,因此Put的局部性较差。Iterator操作的阶梯式增涨主要来源于ZippyDB的作为对象存储元数据存储的使用场景,当上层访问一个对象的时候需要对元数据做scan,那么总是会从这个对象的起始位置开始扫,这个起始key就总会被访问。

UP2X:


UP2X access

对于UP2X,Get与Merge的访问次数分布较广,并且访问次数高的数据占比比较大。

QPS

结论:UDB的部分CF表现出较强的昼夜模式,这跟社交网络用户习惯相关,ZippyDB和UP2X没有表现出这样的特征

QPS of UDB

KV size分析

结论:key size通常比较小,value size的大小与具体数据类型有关,key size的标准差较小但是value size较大, UDB平均的value size比其他两个例子要大

Average KV size

上表是三个使用场景的平均KV size以及KV size的标准差,可以看到三种情况的平均key size都较小,并且标准差都较小,而UDB的value size较大,并且三种情况的value size的标准差都较大。

KV size CDF

上图是三种使用情况的KV size的CDF图

UDB:

对于key size,除了Assoc_2ry其他的CF的key size都不超过40B,而Assoc_2ry因为其作为secondary index特殊的构成,所以会有超过50B的key存在。value size变化较大,Object这个CF主要用于存储用户数据,所以value size从16B到10KB都有,而像是Assoc只存储Object的联系,所以value就相对较小只有100-200B左右。Assoc_count只存储Object的边的个数,数据格式固定,所以value大小也固定20B。

ZippyDB:

大部分的key size分布于[48, 53]以及[90,91]这两个范围内,这与其作为Object storage的元数据存储工作场景有关。对于value size,其分布较为广泛,存在较大的value,但超过90%的value都只有34B左右。

UP2X:

超过99.99%的Get的key只有9B,merge的key size有6%为9B,94%为17B,17B的key都会被compaction清理掉,所以Get只会访问到9B的key。

Key space以及访问模式分析

统计方法:对key按递增顺序编号,然后统计每个key的访问次数绘制heat-map,统计key的访问时间绘制time-series

结论:heat-map可以看到三种DB的访问具有较强的key space locality,也就是热数据往往聚集分布在某些key space内。UDB的Delete/Single Delete以及UP2X的Merge的time-series表明其访问具有时间局部性

UDB:

Heat map of UDB

上图是UDB的Object和Assoc_count的Get操作的heat-map,其中红线表示不同的MySQL table。可以看到部分MySQL table访问热度较高,而部分基本没有访问,表现出较强的key-space locality。

Time series figure of UDB

上图为UDB的Delete以及Assoc_2ry的SingleDelete的time-series图,一个明显的规律是,当个key被删除的时候,它临近的key也会很快被删除。

总结:KV数据访问并不会随机分布在整个key space,而是根据key-space进行区分,部分key-space访问热度较高,这部分数据占比较小,而部分基本没有访问。属于同一个MySQL table的数据物理上也相邻存储,部分SST和block具有较高的热度,可以考虑基于这个优化compaction以及cache。

ZippyDB:

ZippyDB access patern

可以看到ZippyDB的访问具有较为明显的key-space locality

UP2X:


UP2X access pattern

UP2X的访问也表现出较强的key-space locality,只有后半段key被访问,而前半段key基本没有访问。merge的time-series表现出来merge每一段时间内会集中访问一个range内的key。

建模以及测试

YCSB对比测试

目前YCSB是最常用的一个用于测试NoSQL数据库性能的一个benchmark,YCSB能够设置各种参数来生成不同的workload,但是YCSB生成的workload是否能让下层的IO符合生产环境的情况是个问题,所以本文做了测试。

主要对比参数:block reads,block_cache_hits,read bytes,write_bytes

对比对象:YCSB与真实ZippyDB的trace

YCSB设置:设置workload a/b的参数尽量靠近ZippyDB(YCSB不能设置compression ratio只能用默认设置)

测试结果:

  • YCSB的block read是真实trace的7.71×
  • read byte是真实trace的6.2×
  • write bytes是真实trace的0.74×
  • block cache hit是真实trace的0.17×

总的来说YCSB测workload相比于真实的trace会造成更大的读放大以及更小的写放大,同时cache命中率也更低。其中的主要原因在于忽略了真实workload的key space locality,YCSB中的热点数据随机分布在整个key范围内,访问这些key会造成大量的block读并且被缓存,而这些block可能仅包含较少的热数据,cache大小有限所以降低了cache命中率。对于put,随机的热点分布使得数据在前几层就被compaction掉,所以造成更小的写放大,update的数据具有key space locality,那么新数据会不断写入,就数据一直往下compact直到新数据遇到旧数据才会被处理掉。

建模

  • 选取key range 大小:sst大小
  • 建模
    • 首先将key size,value size以及QPS套到模型里面去
    • 然后处理kv的访问次数,访问顺序以及每个range的平均访问次数
  • 测试:

Prefix_dist:基于建模构建的workload

Prefix_random:随机将冷热数据分布到各个key-range

All_random:热数据随机分布到整个key space

All_dist:热数据集中放置

YCSB对比

根据实验结果,本文对YCSB提出几条建议:

1)增加基于key range的分布

2)增加throughput的控制

3)增加KV size分布的控制

4)增加compression ratio的设置

本文又针对UDB的Assoc workload进行了建模并与真实workload进行了对比:

对UDB的workload的仿真

你可能感兴趣的:(Characterizing, Modeling, and Benchmarking RocksDB Key-Value Workloads at Facebook)