与使用Iterator相比,大型键集的Get和MultiGet为什么要慢得多?

时间:2019-03-26 16:35:27

标签: c++ rocksdb

我目前正在使用RocksDB(C ++),并对我经历过的一些性能指标感到好奇。

出于测试目的,我的数据库密钥是文件路径,值是文件名。我的数据库中有大约200万个条目。我正在MacBook Pro 2016(SSD)上本地运行RocksDB。

我的用例主要由读取决定。全键扫描和包含“大量”键的键扫描非常普遍。 (50%+)

我对以下观察感到好奇:

1。在执行全键扫描时,Iterator比调用Get的速度快得多。

当我想查看数据库中的所有键时,使用Iterator而不是为每个键调用Get时,性能会提高4-8倍。 MultiGet的使用没有区别。

在调用Get大约2M次的情况下,密钥先前已被提取到向量中并按字典顺序排序。为什么反复调用Get比使用Iterator慢得多?有没有办法缩小两个API之间的性能差距?

2。当获取一半的密钥时,使用IteratorGet之间的性能开始变得微不足道。

随着要提取的密钥数量的减少,使用迭代器支付扫描Get密钥所需的时间,开始多次使用Iterator进行调用不在所需的键集中。

对于大多数数据库来说,是否存在某种“魔术”比率?例如,如果我需要扫描25%以上的键,则调用Get的速度更快,但是如果它是75%的键,则Iterator的速度更快。但是这些数字只是通过粗略的测试“弥补”的。

3。似乎无法按顺序获取密钥以提高性能。

如果我将要提取的密钥按与Iterator相同的顺序进行了预排序,这似乎不会使调用Get的速度更快。这是为什么?在文档中提到建议在进行批量插入之前对键进行排序。 Get是否无法从Iterator所受益的相同的超前缓存中受益?

4。对于大量阅读的用例,建议使用哪些设置?

最后,对于繁重的用例,是否建议任何特定设置,其中可能涉及一次扫描大量密钥?

macOS 10.14.3,MacBook Pro 2016 SSD,RocksDB 5.18.3,Xcode 10.1

2 个答案:

答案 0 :(得分:1)

我对RocksDB本身一无所知,但是我可以从基本原理中回答很多。

  

执行全键扫描时,迭代器比调用Get快得多。

这可能是因为Get必须在基础索引中进行完整查找(从顶部开始),而前进迭代器可以通过仅从当前节点移动到下一个节点来实现。假设将索引实现为红黑树或类似的树,则第二种方法的工作量要少于第一种。

  

获取一半的密钥时,使用Iterator和Get的性能开始变得微不足道。

因此您要通过多次调用iterator->Next ()来跳过条目吗?如果是这样,那么就会出现一个问题,那就是每个键调用Get会更便宜,是的。确切的发生时间将取决于索引中的条目数(因为它确定了树中的级别数)。

  

按顺序获取键似乎不会提高性能。

不,我不希望如此。 Get(可能)是无状态的。

  

对于大量阅读的用例,建议使用哪些设置?

对不起,我不知道,但您可能会读到:

https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide

答案 1 :(得分:1)

RocksDB在内部将其数据表示为log-structured merge tree,默认情况下该数据具有几个已排序的层(可以使用plugins / config进行更改)。保罗的第一个答案的直觉成立,只是没有经典的索引。数据实际上是使用指向下一个文件的指针在磁盘上排序的。查找操作平均具有对数复杂度,但是在排序范围内推进迭代器是恒定时间。因此,对于密集的顺序读取,迭代要快得多。

成本平衡的点不仅取决于您读取的键的数量,还取决于数据库的大小。随着数据库的增长,查找变慢,而Next()保持不变。由于最近的插入内容可能仍在内存(内存表)中,因此它们可能很快就会被读取。

对键进行排序实际上只会提高缓存的命中率。取决于您的磁盘,差异可能很小,例如,如果您有NVMe SSD,则访问时间的差异不再像RAM与HDD时那样剧烈。如果您必须对相同或什至不同的键集进行多次操作,而不要按顺序执行键顺序(f(ac)g(ac)f(dg)...),则可以提高性能,因为您将具有更多的缓存命中,并且还可以从RocksDB块缓存中受益。

调优指南是一个很好的起点,尤其是video on database solutions,但是如果RocksDB太慢,您还可以考虑使用基于其他存储算法的DB。 LSM通常更适合需要大量写入操作的工作负载,而RocksDB可以让您很好地控制读取与写入以及空间放大,而基于b树或基于ISAM的解决方案对于范围读取/重复读取可能要快得多。 >