【问题标题】:Transactional, in-memory, object/key/value storage library?事务性、内存中、对象/键/值存储库?
【发布时间】:2011-07-04 06:11:39
【问题描述】:

我有一个 C++ 应用程序,它的内存数据集由一组对象组成,每个对象都附加了一个键/值集。对象和键由 int id 引用,值始终是单个类的实例。 Key id 在对象中是唯一的,对象 id 在 Universe 中是唯一的。

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

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

(a) 告诉我这个问题实际上叫什么,这样我就知道要查找什么了;

(b) 建议我应该查看的任何代码。

请注意,我希望这只是一个内存数据存储,因此像 Berkeley DB 这样的 NoSQL 方法不适合 --- 我不想继续读取和写入值对象(它是中等复杂的)。

到目前为止,我找到了不执行事务的简单方法(例如 boost_multi_index,甚至只是嵌套的 STL 映射),或者使用持久存储的复杂方法,但两者之间没有。我可以在基本存储之上实现自己的事务层,但老实说,我宁愿不这样做。

我错过了什么?

编辑:好吧,似乎没有人能够推荐一个;所以我写了我自己的。这是令人惊讶的繁琐但实际上并不是很多代码。现在它只是一个使用嵌套映射进行存储的模板类,但为了简单起见,我正在考虑改为使用 boost::multi_index_container。它没有完善,可能充满了错误,但如果有人认为他们可以使用它,请告诉我。

更多编辑:作为参考,我正在寻找的 Googleable 名称是一个实体/属性/值数据库 (EAV)

【问题讨论】:

  • 用 :memory: pseudo-DB 查看 SQLite
  • 这仍然需要我在访问它们时序列化/反序列化我的值。我想使用本地数据存储,以便我的值只是指向 C++ 对象的指针,从而避免 I/O 开销。
  • 我不太明白。在数据库中,您使用事务以避免导致部分成功的操作或其他客户端由于此类操作而看到不一致的状态。由于您的目标是纯粹的内存映射,因此您可能不想回滚任何复杂的多步骤操作。如果您将访问包装在互斥锁中,那么您就是线程安全的。如果这样一个受互斥体保护的 std::map 还不够,你还想做什么?
  • 我希望能够中途完成数据库的突变,达到错误条件,然后撤消迄今为止发生的所有更改,以便数据库保持一致。如果用户要求我的程序以不允许的方式更改对象,则会出现错误情况。我真的不想在每次突变之前都经过单独的验证阶段。
  • @David:它应该是多线程的吗?

标签: c++ database key-value


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    您真正追求的是一个异常安全的容器。

    阅读这些: 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

    您的问题很快就会减少到确保存储对象的构造函数、复制构造函数、赋值运算符等中的特定异常保证。 如果这没有发生,那么很可能您在设计对象类型时没有足够小心地分离关注点。

    【讨论】:

    • 为什么?由于您寻求的“回滚”概念是强有力的保证。 ◦基本保证:保留组件的不变量,不泄露资源。 ◦强力保证:操作要么成功完成,要么抛出异常,使程序状态与操作开始前完全一致。 ◦不抛出保证:操作不会抛出异常。
    • 是的,很好发现。听起来 OP 的“事务”要求确实更好地表达为强大的异常安全保证。 (除非将线程安全添加到混合中,否则真正的 ACI(D) 类事务会更合适)
    • 异常安全容器是否允许跨多个突变回滚?我需要能够将整个应用程序的业务逻辑包装在事务中,这样我就可以原子地提交或回滚它,而不是对容器进行单独的低级操作。但令我感到鼓舞的是,我现有的自制代码似乎使用了 Dr.Dobbs 文章中描述的相同逻辑;感谢您的链接。
    • 我确实想知道您是否忽略了对多个对象修改的需要。推荐的技术是复制(整个容器)/修改/交换。然而,这与多线程代码不兼容。这将需要完整的锁定机制,例如锁定/修改/解锁
    • 然后是这篇论文,它描述了更大的问题空间。 “用于 C++ 的软件事务内存库”diku.dk/forskning/performance-engineering/Kasper/thesis.pdf 很明显,您正在解决一个大问题。现在很明显,问题在于更新的粒度。这就引出了一个问题,您是否可以重新设计您的对象以使其包含所有状态,或者您的应用程序的架构是否需要任意变化的关系?
    【解决方案3】:

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

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

    【讨论】:

    • 我确实看过 Redis --- 不幸的是它不是内存数据库,所以在序列化/反序列化我的值时仍然会产生 I/O 开销。
    • @David - 我为某个项目广泛研究了 Redis(毕竟没有选择它),据我所知 is 是一个内存数据库。它具有记录更新的选项,以便在重新启动后可以恢复数据库的状态。此日志记录是可配置的(例如,每 1 分钟或每 N 次更新),或者可以完全关闭。
    • 是的,但是 redis 仍然将其所有数据存储在外部服务器进程中 --- 所以从我的应用程序的角度来看,我仍然需要从数据存储中序列化和反序列化我的对象的所有 I/O 开销,我想避免。理想情况下,我只想直接访问 C++ 对象。
    猜你喜欢
    • 1970-01-01
    • 2017-02-11
    • 2010-11-08
    • 1970-01-01
    • 2021-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    相关资源
    最近更新 更多