为什么与使用迭代器相比,大型键集的 Get 和 MultiGet 明显慢
Why are Get and MultiGet significantly slower for large key sets compared to using an Iterator?
我目前正在玩RocksDB (C++),并对我经历过的一些性能指标感到好奇。
出于测试目的,我的数据库键是文件路径,值是文件名。我的数据库中有大约 2M 个条目。我在MacBook Pro 2016(SSD)上本地运行RocksDB。
我的用例以读取为主。完整密钥扫描很常见,包含"大量"密钥的密钥扫描也很常见。(50%+)
我对以下观察结果感到好奇:
1. 执行完整密钥扫描时,Iterator
比调用Get
要快得多。
当我想查看数据库中的所有键时,我看到使用Iterator
而不是为每个键调用Get
时性能提高了 4-8 倍。使用MultiGet
没有区别。
在调用Get
大约 2M 次的情况下,键之前已被提取到向量中并按字典顺序排序。为什么反复调用Get
比使用Iterator
慢得多?有没有办法缩小两个 API 之间的性能差距?
2. 当获取大约一半的键时,使用Iterator
和Get
之间的性能开始变得可以忽略不计。
随着要获取的密钥数量的减少,对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
RocksDB 在内部将其数据表示为一个日志结构的合并树,默认情况下它有几个排序层(这可以通过插件/配置进行更改)。保罗第一个答案的直觉成立,除了没有经典索引;数据实际上是在磁盘上排序的,并带有指向下一个文件的指针。查找操作具有平均对数复杂度,但在排序范围内推进迭代器是恒定时间。因此,对于密集的顺序读取,迭代要快得多。
成本平衡的点不仅取决于读取的密钥数,还取决于数据库的大小。随着数据库的增长,查找速度变慢,而Next()
保持不变。最近的插入可能会很快被读取,因为它们可能仍在内存中(内存表)。
对键进行排序实际上只会提高缓存命中率。根据您的磁盘,差异可能非常小,例如,如果您有 NVMe SSD,则访问时间的差异不再像 RAM 与 HDD 时那样剧烈。如果你必须对相同甚至不同的键集执行多个操作,则按键顺序(f(a-c) g(a-c) f(d-g)...)而不是按顺序执行这些操作应该可以提高性能,因为您将有更多的缓存命中率,并且还可以从RocksDB块缓存中受益。
调优指南是一个很好的起点,尤其是有关数据库解决方案的视频,但如果 RocksDB 对您来说太慢,也可以考虑使用基于不同存储算法的数据库。LSM 通常更适合写入头型工作负载,虽然 RocksDB 可以让您很好地控制读取、写入和空间放大,但基于 b 树或 ISAM 的解决方案对于范围读取/重复读取可能要快得多。
我对RocksDB本身一无所知,但我可以从第一原则回答很多问题。
在执行完整密钥扫描时,迭代器比调用 Get 快得多。
这可能是因为Get
必须在底层索引中进行全面查找(从顶部开始),而推进迭代器只需从当前节点移动到下一个节点即可实现。 假设索引实现为红黑树或类似方法,则第二种方法的工作比第一种方法少得多。
当获取大约一半的键时,使用迭代器和 Get 之间的性能开始变得可以忽略不计。
所以你通过多次调用iterator->Next ()
来跳过条目? 如果是这样,那么总有一天,为每个密钥调用Get
更便宜,是的。 确切的时间将取决于索引中的条目数(因为这决定了树中的级别数)。
按排序顺序提取键似乎不会提高性能。
不,我不希望它。Get
(大概)是无国籍的。
对于读取密集型用例,建议进行哪些设置?
我不知道,对不起,但你可能会读到:
https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide
- Cpp-Tuple使用带有变量的get
- 通过 get-Method 访问变量在类外不起作用
- 为什么无论你输入什么,这"while(cin.get(str,3))"只运行一次?
- Boost.Asio/OpenSSL HTTPS GET certificate trouble
- C++中是否有任何函数等同于python中字典的get函数?
- 使用istringstearm和get行缓慢读取文件
- 如何将 c++ get 函数代码转换为 opencv 算法中使用的 python
- 使用 cin.get() 初始化字符数组
- std::variant<>::get() 不能使用 Apple LLVM 10.0 编译
- 编写 cin.get() 以接收字符数组时出现问题
- 作用域枚举和标准::get
- Visual Studio 2017 中的 cpprestapi 问题找不到 POST/GET 常量
- 在 C++/C 中使用 CURL 发出带有数据文件的 GET HTTP 请求
- 使用"push_back"作为带有"get"函数C++的向量
- 使用 cin.get 读取字符数组
- 如何使用 fstream 的 .get 字符串?
- std::get like (partial) template specialization
- 从 unique_ptr::get 发送指针作为指针引用进入函数
- addr2line get 行号始终返回 0
- 为什么与使用迭代器相比,大型键集的 Get 和 MultiGet 明显慢