将巨大的std::map存储在磁盘上

11
我有一个C++程序,可能会生成大量数据——数十亿个二进制记录,大小不同,大多数可能小于256字节,但有些记录可能会延伸到几K。创建后,程序将很少查看大部分记录,但某些记录将经常被访问和修改。在创建时无法确定哪些记录是哪些。

考虑到数据量,我无法将所有数据存储在内存中。但由于仅需要按其数字(64位整数)对数据进行索引和访问,因此我想要完整的数据库程序开销。理想情况下,我希望将其视为具有其数据存储在磁盘上的std::map

是否有已经编写好的库可以做到我想要的事情,还是我需要自己编写呢?

编辑:经过一些思考,我意识到Rob Walker的答案有一个有效的观点:从一个自制类中得到与真正数据库相同的数据完整性是非常困难的。

尽管BerkeleyDB(如RHM建议的那样)看起来能够完全满足我们的需求,但双重许可证是我们不想处理的麻烦事。当我们完成代码并能够证明BerkeleyDB会显著受益时(它可能会),我们将重新审查这个问题。

我确实看了Ferruccio关于stxxl的建议,但我无法确定它如何处理程序被中断和重新启动(可能带有更改)。有了这么多数据,如果可以保存一些数据,每次都要从头开始,那就太浪费了。

因此,我们决定至少在最初开发阶段使用SQLite数据库。感谢所有回答或投票的人。


我很好奇,这是哪个领域的?为什么/数据是什么? - Tim
抱歉,我目前还不能透露任何关于它的信息。 - Head Geek
好的。我猜想是某种模拟媒体或照片的部分。祝好运。 - Tim
我不能确认也不能否认,但这是一个很好的猜测。 :-) - Head Geek
8个回答

8

看一下STXXL

stxxl::map<>看起来正好符合你的需求。


不,这对他的数据行不通。第一个原因:STXXL希望所有记录都具有完全相同的大小(POD)。第二个原因:它们不允许将数据结构保存到磁盘并稍后加载它,请参见此处http://sourceforge.net/projects/stxxl/forums/forum/446474/topic/3537658。 - Frank

5
我怀疑你很难找到一个完全符合你要求的库,所以你需要决定哪些“特性”对你来说真的非常重要,然后决定是否有现有的数据库解决方案足够接近。
无论如何,数十亿条记录都是庞大的数据集。记录的生成速率是多少?它们的持续时间是多久?访问模式是否会随着时间发生变化?
更新时的数据量是否始终与原始相同?
我建议在开始自己开发之前,先证明数据库解决方案行不通,特别是如果数据的完整性至关重要(通常都是如此...)。在可靠地将这些数据存储到磁盘上方面,确实会面临挑战。更改数据时,您需要任何类型的事务语义吗?客户端是否支持多线程?

4

BerkleyDB可能适合您。它基于字符串进行索引,而不是数字,但您可以将数字格式化为十六进制。对于基于磁盘的键/值查找来说,速度应该非常快。


1
我相信 BerkeleyDB 允许您定义比较函数来支持任何类型的索引,而不仅仅是字符串。 - Ferruccio

2

我在几个项目中使用了Gigabase http://www.garret.ru/gigabase.html,它有一个整洁的C++接口,我处理了数百万条记录,没有出现任何问题,它支持回滚。它采用类似MIT的许可证,而且作者非常快地回答问题并修复错误。


2
你可以使用SQLite,它是一个开源数据库发布到公共领域。

http://www.sqlite.org/

我来翻译一下这段话:

我引用他们的页面:

SQLite是一个软件库,实现了自包含、无服务器、零配置、事务性SQL数据库引擎。SQLite是世界上部署最广泛的SQL数据库引擎。SQLite的源代码属于公共领域。

并且

SQLite的持续开发和维护部分由SQLite联盟成员赞助,包括:Adobe,Symbian,Bloomberg,Mozilla。

如果您需要一个轻量级的数据库,这可能就是它。


1

你可能需要自己编写代码。我建议将其存储在几个 MySQL 表中,并使用惰性加载固定大小的映射(LRU)。如果你真的想避免使用数据库,可以将长度小于 256 或其他值的记录存储在固定记录随机访问文件中,并将较大的记录存储为单独的文件。


0

我同意其他人的看法,BerkeleyDB、sqlite或gigabase应该是不错的解决方案。

但是编写自己的解决方案也不应该太难。

我有一个简单的解决方案,但需要满足以下三个前提条件:

  1. 您至少可以在内存中保留一个具有numkey元素的std::vector<int64>
  2. 您的键是连续的或可以被连续化。
  3. 文件写入后,每个数据记录大小都有一个固定的maxsize,即其大小不能增加。

如果这些前提条件得到满足,直接的解决方案是将每个键(int64)的文件位置(int64)存储在内存中的向量中。对于查找,只需从向量中检索文件位置,seek到该位置,在那里找到记录大小作为其第一个条目,并读取size字节。


很遗憾,问题排除了您的第二个要求:密钥只能使用一次,而某些密钥稍后将被删除(一开始不知道哪些),这会打破连续性。随着记录数量的增加,第一个要求也可能会成为问题。我本来想避免使用实际数据库,但到目前为止,这已被证明是最好的解决方案。然而,该项目仍未准备就绪,这在完成之前可能会发生变化。 - Head Geek
对于#2,删除的项目可以简单地设置为0大小的项目(本质上不是问题)。 - xryl669

0

根据您所需的性能特征,答案是不同的。但仅考虑问题描述中的信息,我认为数据库过于复杂,实际上可能会适得其反。

将每个条目保存为以其键为名称的文件(即键“1”对应于磁盘上的文件“1.dat”)是一种简单的解决方案,可以避免几个问题。假设您可以控制软件要运行的文件系统,如果选择一个具有良好完整性的文件系统,则数据应该具有良好的完整性。您可以编写大量代码来将条目分组到一个文件中,然后必须担心调整大小,或者您可以让文件系统为您处理(它被设计用于处理文件大小变化)。您可以担心以线程安全的方式将它们写入该文件中,或者您可以让文件系统为您处理(文件系统被设计为具有不同的进程同时写入不同的文件)。您可以担心文件部分保存到磁盘并编写检查代码,或者您可以让文件系统为您处理(日志记录和原子写入)。您可以担心安排更改的写入以提高速度,或者您可以让文件系统为您处理此操作(写缓存)。

基本上,一个好的文件系统和操作系统应该为您处理所有这些问题,而在其之上添加试图复制所有这些功能的数据库只会创建更多的复杂性和更多的潜在错误。如果您需要按不同字段索引数据,则数据库可能是有意义的,但在您的描述中,您说您每次只需要按相同的整数键索引数据。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接