如何在 Python 中设计一个忘记(几乎只)内存压力的缓存?



假设我在Python中有一个URL类。URL 类表示一个 URL,并提供下载其内容的方法。它还缓存该内容以加快后续调用的速度。

@dataclass(frozen)
class URL:
url: str
_cache: Optional[bytes] = None
def get_content(self) -> bytes:
if self._cache is None:
self._cache = requests.get(self.url)
return self._cache

到目前为止,此代码运行良好。现在我被要求在这里下载大量 URL(出于理智的原因)。为了防止滥用的可能性,我想支持每个URL实例在下载所有 URL 之前都处于活动状态的用例。

大量URL处于活动状态并缓存将导致内存耗尽。现在我想知道如何设计一个只有在内存压力时才忘记的缓存。

我考虑了以下替代方案:

  • 一旦删除最后一个强引用,弱值字典就会忘记。这在这里没有帮助,因为它会导致当前情况或禁用缓存。
  • LRUCache需要事先决定容量。但是,只有当它缓存尽可能多的元素时,我需要的缓存才有用。我缓存 100 还是 1000 的 100.000 并不重要。

tl;dr:我如何在Python中实现一个缓存,它保存弱引用,并且很少删除它们并且内存压力很大?

更新:

我心中没有明确的标准。我希望其他人已经开发出我只是不知道的好解决方案。在Java中,我怀疑SoftReferences是一个可以接受的解决方案。

米亚吉先生找到了一个很好的措辞:

假设 URL 缓存会逐出一个项目,如果一些不相关的数字计算需要内存?

我想尽可能长时间地保留元素,但当同一 python 进程的任何其他代码需要它时释放它们。

也许解决方案只会删除垃圾收集器的URL实例。然后我可以尝试相应地配置垃圾收集器。但也许有人已经想出了一个更聪明的想法,所以我可以避免重新发明轮子。

我为我面临的具体问题找到了一个很好的解决方案。不幸 这不是具体问题的解决方案,具体问题只涉及 缓存。因此,我不会接受我的回答。但是,我希望它会鼓舞人心 对于其他人。

在我的应用程序中,有一个存储后端将调用url.get_content().使用存储后端将大致如下所示:

storage = ConcreteStorageBackend()
list_of_urls = [URL(url_str) for url_str in list_of_url_strings]
for url in list_of_urls:
storage.add_url(url)
storage.sync()  # will trigger an internal loop that calls get_content() on all URLs.

很容易看出,当list_of_urls是巨大的缓存get_content()可能会导致内存问题。这里的解决方案是,替换(或操作)以下 URL 对象,以便新的 URL 对象从存储中检索数据 后端。

  1. 更换它们会更干净。用法可能是这样的storage.sync()返回新网址的list_of_urls = storage.sync()实例。
  2. 用新功能覆盖URL.get_content()将允许用户 在这里完全不了解性能注意事项。

在这两种情况下,缓存都意味着避免再次下载内容,并且 而是从存储后端获取它。为此,从 存储后端必须比再次下载内容更快,但我 假设这种情况经常发生。

使用存储后端进行缓存为许多人带来了另一个很酷的好处 案例:操作系统内核本身负责缓存。我假设大多数存储 后端最终将写入磁盘,例如写入文件或数据库。在 至少 Linux 内核将磁盘 IO 缓存在可用 RAM 中。因此,只要您的 RAM 是 足够大,您将url.get_content()以 公羊。但是,缓存不会阻止其他更重要的内存分配, 例如,对于一个评论中提到的一些不相关的数字计算

同样,我知道这与上面的问题并不完全匹配,因为它 依赖于存储后端,并且不会回退到从 再次上网。我希望这个答案对其他人有所帮助。

最新更新