如何在Python中设计一个忘记(几乎只忘记)内存压力的缓存?
假设我有一个Python中的URL类。URL类表示URL并提供下载其内容的方法。它还缓存该内容以加速后续调用如何在Python中设计一个忘记(几乎只忘记)内存压力的缓存?,python,caching,Python,Caching,假设我有一个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.u
@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还是100.000中的1000并不重要
也许一个解决方案将只由垃圾收集器删除URL实例。然后我可以尝试相应地配置垃圾收集器。但是也许有人已经想出了一个更聪明的主意,所以我可以避免重新发明轮子。我为我所面临的具体问题找到了一个很好的解决方案。不幸的是, 这不是一个具体问题的解决办法,这个问题只涉及一个问题 隐藏物因此,我不会接受我的答案。然而,我希望这将是鼓舞人心的 对其他人来说 在我的应用程序中,有一个存储后端将调用
url.get\u 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\u\u URL
是巨大的缓存get\u content()
可能会导致内存问题。这里的解决方案是,替换(或操纵)
URL对象,以便新的URL对象从存储中检索数据
后端
list\u of\u URL=storage.sync()
其中storage.sync()
返回新的URL
实例URL.get_content()
将允许用户
完全不了解这里的性能考虑再次上网。我希望这个答案对其他人有所帮助。你对“记忆压力”的标准是什么?缓存占用了x%的应用程序内存,或x%的机器内存,或分配内存失败?我还没有一个标准。我希望有一个明智的选择,通过一个迄今为止我还没有找到的解决方案来实现。我希望任何解决方案至少都能防止内存错误。防止在缓存之外没有所有者的情况下释放对象可能是一个好的开始。“防止在缓存之外没有所有者的情况下释放对象”只意味着将weakref容器替换为常规容器–例如,将
WeakValueDictionary
替换为dict
。在构建基于内存的缓存时,您正在努力解决哪些方面的问题?原则上,您知道如何为任何策略构建缓存吗?我相信我可以实现LRU或LFU缓存。使用dict
将导致禁用缓存行为。试图在基于dict
的自定义缓存中捕获MemoryError
是不够的,因为我不能保证MemoryError
将发生在缓存中。我不太明白:您所说的“使用dict
将导致禁用缓存行为”是什么意思?具有显式策略的缓存确实使用强引用容器,以便显式管理生存期。例如,functools.lru\u cache
使用自定义的orderedict
实现。至于保证错误发生的位置,如果任何其他代码需要内存,是否希望缓存释放一个项?如果某个不相关的数值计算需要内存,那么URL缓存将逐出一个项?