【问题标题】:Python Disk-Based DictionaryPython 基于磁盘的字典
【发布时间】:2022-01-01 16:43:05
【问题描述】:

我正在运行一些动态编程代码(试图强力反驳 Collat​​z 猜想 =P)并且我正在使用 dict 来存储我已经计算过的链的长度。显然,它在某个时候耗尽了内存。是否有任何简单的方法来使用dict 的某些变体,当空间用完时,它会将自身的一部分分页到磁盘?显然它会比内存中的字典慢,而且它可能最终会占用我的硬盘空间,但这可能适用于其他不是那么无用的问题。

我意识到基于磁盘的字典几乎是一个数据库,所以我使用 sqlite3 手动实现了一个,但我没有以任何聪明的方式做到这一点,而是让它一次查找数据库中的每个元素...速度慢了大约 300 倍。

创建我自己的字典集,一次只在内存中保留一个,并以某种有效的方式分页它们是最聪明的方法吗?

【问题讨论】:

    标签: python database dictionary disk-based


    【解决方案1】:

    第3方shove模块也值得一看。它与 shelve 非常相似,因为它是一个简单的类似 dict 的对象,但是它可以存储到各种后端(例如文件、SVN 和 S3),提供可选的压缩,甚至是线程安全的。这是一个非常方便的模块

    from shove import Shove
    
    mem_store = Shove()
    file_store = Shove('file://mystore')
    
    file_store['key'] = value
    

    【讨论】:

    • 这值得更多关注。如果您不想使用单独的服务器或 Berkeley DB,如果您不想使用 SQLite,它也可以与 SQLite 一起使用。
    • 是的,我一直在寻找这种模块。计划添加 redis 支持,因为我们用于 kv 存储。
    • sn-p 中缺少 file_store.sync() 以将数据保存到文件中,很难处理文档,以及 file_store.close() 和程序结束。
    【解决方案2】:

    Hash-on-disk 通常使用 Berkeley DB 或类似的东西来解决 - Python Data Persistence documentation 中列出了几个选项。你可以在它前面加上一个内存缓存,但我会先测试本机性能;有了操作系统缓存,结果可能差不多。

    【讨论】:

      【解决方案3】:

      shelve 模块可以做到这一点;无论如何,它应该很容易测试。而不是:

      self.lengths = {}
      

      做:

      import shelve
      self.lengths = shelve.open('lengths.shelf')
      

      唯一的问题是书架的键必须是字符串,所以你必须替换

      self.lengths[indx]
      

      self.lengths[str(indx)]
      

      (根据您对 Charles Duffy 帖子的评论,我假设您的密钥只是整数)

      内存中没有内置缓存,但您的操作系统可能会为您执行此操作。

      [实际上,这并不完全正确:您可以在创建时传递参数 'writeback=True'。这样做的目的是确保在架子中存储列表和其他可变的东西正常工作。但副作用是整个字典都缓存在内存中。由于这给您带来了问题,这可能不是一个好主意:-)]

      【讨论】:

      • 我实际上已经尝试过了,但它太慢了.. 我想我需要某种手动分页解决方案来获得任何合理的速度。
      【解决方案4】:

      上次我遇到这样的问题时,我改写为使用 SQLite 而不是 dict,并且性能大幅提升。这种性能提升至少部分是由于数据库的索引能力。取决于你的算法,YMMV。

      __getitem____setitem__ 中执行 SQLite 查询的瘦包装器不需要编写太多代码。

      【讨论】:

      • 您将如何使用 sqlite 的索引?我在这里做的方法是创建一个像这样的表:“cur.execute('create table vals (indx INTEGER, chainlen INTEGER)')”,然后我“cur.execute('SELECT * from vals where indx=% d' % i)" 进行查找。
      • 创建表 vals (indx INTEGER PRIMARY KEY, chainlen INTEGER)
      • @Claudiu - 我的程序是这样的,我可以在数据库层实现一些逻辑,所以我可以让数据库进行过滤等;这不仅仅是一家愚蠢的商店。
      • 你也可以使用缓存大小的编译指示告诉 sqlite 使用更多的内存来缓存:sqlite.org/pragma.html
      • @Claudiu ...顺便说一句,您在那里使用的做法(使用% 通过字符串格式将值替换到您的SQL 语句中)基本上是不安全的;这对小数来说没什么大不了的,但如果你对字符串这样做,它会让你容易受到 SQL 注入攻击。 cur.execute('SELECT * from vals where indx=?', (i,))更安全,由于sqlite模块缓存prepared statements,在多次调用时也可以运行得更快。
      【解决方案5】:

      稍加思考,您似乎可以让shelve module 做您想做的事。

      【讨论】:

        【解决方案6】:

        我听说你认为 shelve 太慢了,你试图用 sqlite 破解你自己的字典。

        另一个也这样做了:

        http://sebsauvage.net/python/snyppets/index.html#dbdict

        它看起来非常高效(sebsauvage 是一个非常好的编码器)。也许你可以试一试?

        【讨论】:

          【解决方案7】:

          如果有一些启发式方法可以知道接下来最有可能检索哪些项目,那么您应该一次带多个项目,并且不要忘记 Charles 提到的索引。

          【讨论】:

            【解决方案8】:

            对于简单的用例sqlitedict 可以帮忙。但是,当您拥有更复杂的数据库时,您可能会尝试其中一个更受欢迎的答案。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-04-20
              • 1970-01-01
              • 1970-01-01
              • 2011-01-05
              • 2013-12-02
              • 1970-01-01
              相关资源
              最近更新 更多