基于标记的失效和dogpile效应预防的python缓存库
HermesCache的Python项目详细描述
赫尔墨斯卡什
hermes是一个python缓存库。其设计满足的要求:
- 基于标记的缓存失效
- 防止狗桩效应
- 螺纹安全性
- 简洁的设计
- 实现多个后端的接口
实现的后端:redis,memcached,dict(没有过期)。
安装
pip install HermesCache
确保已安装所需的后端客户端库。见下文。
使用量
下面演示所有最终用户api。
importhermes.backend.rediscache=hermes.Hermes(hermes.backend.redis.Backend,ttl=600,host='localhost',db=1)@cachedeffoo(a,b):returna*bclassExample:@cache(tags=('math','power'),ttl=1200)defbar(self,a,b):returna**b@cache(tags=('math','avg'),key=lambdafn,*args,**kwargs:'avg:{0}:{1}'.format(*args))defbaz(self,a,b):return(a+b)/2.0print(foo(2,333))example=Example()print(example.bar(2,10))print(example.baz(2,10))foo.invalidate(2,333)example.bar.invalidate(2,10)example.baz.invalidate(2,10)cache.clean(['math'])# invalidate entries tagged 'math'cache.clean()# flush cache
注意
api鼓励导入时实例化Hermes,以允许最终用户 装饰它的功能。实例化没有副作用。潜在的 后端服务器连接很懒。
有关高级示例,请查看 test suite。
标记缓存条目
首先让我们看看基本缓存是如何工作的。
importhermes.backend.dictcache=hermes.Hermes(hermes.backend.dict.Backend)@cachedeffoo(a,b):returna*bfoo(2,2)foo(2,4)print(cache.backend.dump())# {# 'cache:entry:foo:515d5cb1a98de31d': 8,# 'cache:entry:foo:a1c97600eac6febb': 4# ↓# argument hash# }
基本上我们有一个密钥值存储,其中O(1)复杂度为^ {TT5} $,^ {TT6}$,^ {TT7}$。 这意味着操作速度是恒定的,与已经存在的项目数无关 存储。当一个可调用的(函数或方法)被缓存时,从 可调用自身并传递参数。callable的返回值保存到密钥。下一次调用 我们可以使用缓存中的值。
“There are only two hard problems in Computer Science: cache invalidation and naming things.” — Phil Karlton
所以这是一个复杂的应用程序。有一种情况是某些方法的操作相同 数据和使单个条目无效是不切实际的。尤其是当 方法返回跨越多个实体的复杂值。缓存标记使标记成为可能 这组方法产生一个标记,并使它们同时失效。
这是埃里克的article 弗洛伦扎诺解释了这个想法。让我们看看代码。
importhermes.backend.dictcache=hermes.Hermes(hermes.backend.dict.Backend)@cache(tags=('tag1','tag2'))deffoo(a,b):returna*bfoo(2,2)print(cache.backend.dump())# {# 'cache:tag:tag1': '0674536f9eb4eb19',# 'cache:tag:tag2': 'db22b5ab2e504895',# 'cache:entry:foo:a1c97600eac6febb:c1da510b3d42bad6': 4# ↓# tag hash# }
当我们要标记缓存项时,首先需要创建标记项。表示每个标记 通过它自己的入口。每次创建标记时,标记项的值都设置为随机值。一次全部标记 值存在,它们被连接并散列。标记哈希被添加到缓存项密钥。
一旦我们想使标记的条目失效,我们只需要删除标记条目。没有任何标签 值标记哈希是用创建的,因此无法构造条目键,因此标记的缓存 条目变得不可访问,因此无效。通常是建立在另一个特性之上的特性 增加复杂性。
表演。所有操作都变成o(n),其中n是条目标记的数目。但是既然我们可以 很少需要超过几十个标签,实际上仍然是o(1)。标签输入操作是 批处理,因此对网络操作数的影响如下:
- set–最坏情况下是3x个后端调用(get + 2 * set)。当 将创建所有已使用的标记项。
- get–2x个后端调用。
- delete–2x个后端调用。
内存开销包括标记项和过时的缓存项。演示如下。
importhermes.backend.dictcache=hermes.Hermes(hermes.backend.dict.Backend)@cache(tags=('tag1','tag2'))deffoo(a,b):returna*bfoo(2,2)print(cache.backend.dump())# {# 'cache:tag:tag1': '047820ac777abe8a',# 'cache:tag:tag2': '126365ec7175e851',# 'cache:entry:foo:a1c97600eac6febb:5cae80f5e7d58329': 4# }cache.clean(['tag1'])foo(2,2)print(cache.backend.dump())# {# 'cache:tag:tag1': '66336fec212def16', ← recreated tag entry# 'cache:tag:tag2': '126365ec7175e851',# 'cache:entry:foo:a1c97600eac6febb:8e7e24cf70c1f0ab': 4,# 'cache:entry:foo:a1c97600eac6febb:5cae80f5e7d58329': 4 ← garbage# }
因此,ttl的选择要慎重。对于redis后端,也建议 将maxmemory-policy设置为volatile-lru。
后端和客户端库
支持的依赖项列在 tox.ini 包裹的一部分。他们的组合方式是 drone.io CI page。
内存缓存
hermes.backend.memcached依赖于纯python python-memcached (python3-memcached如前一个仍然是 在python 3上(包含二进制数据)或,libmemcached包装器上损坏, pylibmc。pylibmc给出关于提高50%。
指令
hermes.backend.dict既不是完整的后端,也不是为任何分布式使用而设计的。 最初的目的是开发需要,实际上它只是pythondict上的一个包装器。它 不实现条目过期和任何内存限制。虽然它可以在有限的情况下使用 其中缓存条目大小是一个先验的小值,而实际状态仅由手动维护 无效。
性能
这里有一些关于后端和客户端库性能的线索。不是故意的 提供一些具有统计意义的性能估计。这些只是 一个CI版本。
审查实施
在我写书之前,我翻遍了奶酪店,想找一家适合我需要的。 不幸的是,没有一个,但是有些是部分匹配的,是 某些方面:
- 专业版:
- 清洁最终用户API
- 简洁的设计
- 继续:
- 无自动缓存键计算
- 无狗桩效应预防
- 没有缓存条目标记
- 使用实例方法失败
- 专业版:
- 成熟
- 有很好的记录
- 防止狗桩效应
- 继续:
- 没有缓存条目标记
- 复杂的代码库
- 不简洁的最终用户API
- 专业版:
- 缓存条目标记
- 继续:
- 为新闻网站脚手架框架设计
- 因此,周围都是臃肿