2011-02-25 39 views
5

我有其存储器内数据集包括一组对象,其每一个具有一个附加的键/值集合的C++应用程序。对象和键由int id引用,并且值始终是单个类的实例。键ID在对象中是唯一的,对象ID在宇宙中是唯一的。事务性,内存中的对象/键/值存储库?

这是几乎除了我有需要能够枚举连接到一个特定对象的密钥有关的附加需求的map<pair<int, int>, value>。这一切都需要事务性处理,所以如果出现问题,我可以回滚更改。

这一切都让我觉得是一个完全标准的问题,我应该能够获得现成的代码,但我一直无法找到任何东西。任何人都可以:

(一)告诉我,这个问题实际上是调用,所以我知道要寻找什么;

(二)建议的任何代码,我应该看看。

请注意,我只希望这是一个内存数据存储,所以NoSQL的方法就像Berkeley DB不合适---我不想继续读和写值对象(这是适度复杂的) 。

到目前为止,我发现,不做交易(如boost_multi_index,甚至只是嵌套的STL地图),无论是简单的办法,或者使用永久存储,但没有在两者之间复杂的方法。我可以在基本存储上实现自己的事务层,但说实话,我宁愿不。

我错过了什么?

编辑:好了,没有人似乎已经能够提出一个;所以我写了我自己的。令人惊讶的是,它并不是非常多的代码。现在它只是一个使用嵌套地图进行存储的模板类,但我正在考虑改为使用boost :: multi_index_container来代替简单。它没有打磨,可能充满了虫子,但如果有人认为他们可以使用它,让我知道。

更多编辑:作参考,事实证明,什么我要找的Googleable名称是实体/属性/值数据库(EAV)

+2

用下面的语句看看SQLite:memory:pseudo-DB – 0xC0000022L 2011-02-25 11:38:54

+0

这仍然要求我在访问它们时序列化/反序列化我的值。我想使用本地数据存储,以便我的值只是指向C++对象的指针,避免了I/O开销。 – 2011-02-25 12:14:28

+0

我不太明白。在数据库中,您使用事务以避免部分成功的操作,或另一个客户端由于此类操作而看到不一致的状态。由于您的目标是纯粹的内存映射,因此不需要复杂的多步骤操作,您可能需要回滚。如果你在互斥体中包装访问,那么你是线程安全的。你还想做什么,这样一个互斥保护的std :: map是不够的? – DevSolar 2011-02-25 12:22:43

回答

3

你真的是一个异常安全的容器。

阅读这些: http://www.boost.org/community/exception_safety.html http://lmzr.perso.neuf.fr/attic/Exception_Safe_Generic_Containers.pdf http://www.drdobbs.com/184401771;jsessionid=TTP1SXYYVJZPLQE1GHPCKH4ATMY32JVN

您的问题迅速降低到确保存储对象的构造特定的异常担保,拷贝构造函数,赋值运算符等。 如果这没有发生那么很可能你还没有足够的照顾,关注点分离设计的对象类型。

+1

为什么?由于您寻求的“回滚”概念是强有力的保证。 ◦基本保证:保留组件的不变量,并且不泄漏资源。 ◦强有力的保证:操作已成功完成或引发异常,使程序状态与操作开始之前的状态完全相同。 ◦不抛出保证:操作不会抛出异常。 – 2011-02-27 11:55:02

+0

是的,很好的发现。听起来好像OP的“交易”要求更好地表达为强大的例外安全保证。(除非线程安全被添加到组合中,那么真正的ACI(D)类事务会更合适) – jalf 2011-02-27 12:13:13

+0

异常安全的容器是否允许跨多个突变回滚?我需要能够在事务中包装我的整个应用程序的业务逻辑,以便我可以自动提交或回滚它,而不是容器上的单个低级操作。但我鼓励我现有的自制代码似乎使用Dr.Dobbs文章中描述的相同逻辑;感谢您的链接。 – 2011-02-27 18:38:42

0

你考虑过Redis吗?这是一个数据结构服务器,因此您可以使用地图结构并轻松枚举附加到对象的键(请参阅hash families of commands)。它也支持transactions

我想缺点是我不确定打包成库是多么容易。

+0

我确实看过Redis--不幸的是它不是内存数据库,所以在对我的值进行序列化/反序列化时,仍然会产生I/O开销。 – 2011-02-25 12:14:53

+0

@David - 我为某个项目广泛研究了Redis(毕竟没有选择它),并且据我所知它**是**内存数据库。它具有记录更新的选项,以便在重新启动后可以恢复数据库的状态。该日志记录是可配置的(例如,每1分钟或每N次更新),或者可以完全关闭。 – davka 2011-02-25 21:28:17

+0

是的,但redis仍然将其所有数据存储在外部服务器进程中---所以从我的应用程序的角度来看,我仍然有所有I/O开销,从数据存储序列化和反序列化我的对象,这是我想避免的。理想情况下,我只想直接访问C++对象。 – 2011-02-26 12:40:06

0

LMDB会处理这个问题。您可以将它与tmpfs上的文件一起使用,以确保所有操作仅在内存中,或者在明确的ext3/ext4文件系统上使用它,提交时间非常长,以避免更改被写入磁盘。它完全是事务性的并使用内存映射文件;如果您的文件系统缓存写回时间设置得足够长,并且您使用异步提交运行它,那么您的更改将只存在于RAM中,并且永远不会刷新到磁盘。 它可以做零拷贝的读写操作;您可以使用最少的序列化编写对象,并用零反序列化引用它们。