创建在执行之间持续存在的内存缓存

Posted

技术标签:

【中文标题】创建在执行之间持续存在的内存缓存【英文标题】:Creating an in-memory cache that persists between executions 【发布时间】:2012-06-13 08:32:25 【问题描述】:

我正在开发一个 Python 命令行实用程序,它可能涉及对一组文件的相当大的查询。这是一个相当有限的查询列表(想想索引的 DB 列)为了提高进程中的性能,我可以生成排序/结构化列表、映射和 一次,然后重复点击它们,而不是点击文件系统每次。

但是,这些缓存在进程结束时会丢失,并且每次脚本运行时都需要重新构建,这大大增加了我的程序的运行时间。我想确定在我的命令的多次执行之间共享这些数据的最佳方式,这些执行可能是并发的、一个接一个的,或者在执行之间有很大的延迟。

要求:

必须快速 - 应尽量减少任何类型的每次执行处理,包括磁盘 IO 和对象构造。 必须与操作系统无关(或者至少能够挂钩到 Unix/Windows 上的类似底层行为,这更有可能)。 必须允许相当复杂的查询/过滤 - 我认为键/值映射不够好 不需要是否需要是最新的 - (简单地说)陈旧数据完全没问题,这只是一个缓存,实际数据正在单独写入磁盘。 不能使用重量级的守护进程,如 mysql 或 MemCached - 我想尽量减少安装成本,要求每个用户安装这些服务太多了。

偏好:

如果可能的话,我想避免任何长时间运行的守护进程。 虽然我希望能够快速更新缓存,但在更新时重建整个缓存并不是世界末日,但快速读取比快速写入更重要。

在我理想的幻想世界中,我能够在执行之间直接保留 Python 对象,有点像 Java 线程(如 Tomcat 请求)共享单例数据存储对象,但我意识到这可能是不可能的。不过,我越接近它越好。

候选人:

内存中的 SQLite

SQLite 本身对于我的用例来说似乎不够快,因为它由磁盘支持,因此每次执行时都必须从文件中读取。也许这并不像看起来那么糟糕,但似乎有必要将数据库持久存储在内存中。 SQLite 允许 DB 到 use memory as storage,但这些 DB 在程序退出时被销毁,并且不能在实例之间共享。

使用mmap将平面文件数据库加载到内存中

在频谱的另一端,我可以将缓存写入磁盘,然后使用 mmap 将它们加载到内存中,可以在不同的执行之间共享相同的内存空间。但是,如果所有进程都退出,我不清楚 mmap 会发生什么。如果 mmap 最终从内存中刷新没关系,但我希望它停留一段时间(30 秒?几分钟?),以便用户可以一个接一个地运行命令,并且可以重用缓存。 This example 似乎暗示需要一个打开的 mmap 句柄,但我还没有找到任何关于内存映射文件何时从内存中删除并需要从磁盘重新加载的确切描述。

我想我可以实现这个,如果 mmap 对象在退出后仍然存在,但感觉非常低级,我想有人已经实现了一个更优雅的解决方案。我不想开始构建它只是为了意识到我一直在重建 SQLite。另一方面,感觉会很快,我可以根据我的具体用例进行优化。

使用 Processing 在进程之间共享 Python 对象

处理包指示“Objects can be shared between processes using ... shared memory”。浏览其余文档,我没有看到任何进一步提及这种行为,但这听起来非常很有希望。谁能指导我了解更多信息?

将数据存储在 RAM 磁盘上

我关心的是特定于操作系统的功能,但我可以create a RAM disk 然后简单地读/写我喜欢的(SQLite?)。 fs.memoryfs 包似乎是与多个操作系统一起工作的有前途的替代方案,但 cmets 意味着相当多的限制。

我知道pickle 是一种存储 Python 对象的有效方式,因此它可能比任何类型的手动数据存储都具有速度优势。我可以将泡菜加入上述任何选项吗?这会比平面文件或 SQLite 更好吗?

我知道有很多与此相关的问题,但我进行了相当多的挖掘,但找不到任何直接解决我关于多个命令行执行问题的问题。

我完全承认,我可能想多了。我只是想了解一下我的选择,以及它们是否值得。

非常感谢您的帮助!

【问题讨论】:

我不会太快下结论:“SQLite 本身对于我的用例来说似乎不够快,因为它由磁盘支持,因此必须从文件中读取每次处决。”你只知道如果你尝试它是否会足够快。此外,操作系统无论如何都会进行缓存。 Redis 或 Memcached 也可能是可能的解决方案。 为什么使用长时间运行的进程?这个用例正是 Redis 和 Memcached 解决的问题,另外一个优势是您可以在自己的程序重新启动时保留缓存。 如果您需要在 key/val 之外进行查询,那么除非您正在腌制您自己的自定义查询启用类,否则 pickle 将如何成为候选人?除非您在文件访问周围添加锁定,否则 pickle 也不是共享内存的好解决方案。您是否需要多个进程之间的共享内存,或者这仅适用于单个进程?如果他们都只是在阅读,Pickle 就可以了 @SvenMarnach,这是关于操作系统缓存的一个好点,我没有考虑到这一点。但即使文件被缓存,我也(可能不必要地)担心每次 SQLite 解析/加载文件的成本。 【参考方案1】:

我只会做the simplest thing that might possibly work。 ...在您的情况下,这可能只是转储到泡菜文件中。如果您发现它不够快,请尝试更多相关的东西(例如 memcached 或 SQLite)。 Donald Knuth 说“过早的优化是万恶之源”!

【讨论】:

以上是关于创建在执行之间持续存在的内存缓存的主要内容,如果未能解决你的问题,请参考以下文章

cpu,内存,虚拟内存,硬盘,缓存之间是什么关系??

深入学习重点分析java基础---第二章:java并发 volatile

Linux缓存与缓冲

内存屏障和缓存刷新

java内存缓存,节省内存

JVM--内存模型与线程