uncle-lv
(Uncle Lv)
1
我在去年由于个人需求,需要选型、使用一款Python本地缓存库。但令人遗憾的是,我发现我的选择并不多,大概只有以下两个:
我最终选择了cacheout。因为python-diskcache是以SQLite、MongoDB等数据库为存储后端的,性能不如内存缓存。这点从它的名字里也能看出来。
cacheout基本满足了我的需求。但是相较于其他语言生态下的缓存库,只能说是差强人意。
于是我参考Java中的caffeine缓存库,完成了cacheout的监听器和数据统计模块。在这个过程中,也发现了一些我个人认为设计得不合理的地方(我会在下面的评论中列出,欢迎探讨)。逐渐萌生了直接参考caffeine,实现一个Python缓存库的想法。
由于辞职,最近难得有了一段空闲时间。所以我决定将这个想法付诸于实践。(PS:如果各位大佬有内推,欢迎砸向我 )
除了包含cacheout的现有功能外,在我目前的构想中,它主要包括以下要点:
我仍不确定这项工作是否有意义,所以请大家投个票吧。
你是否需要这样一个本地缓存工具库?
uncle-lv
(Uncle Lv)
2
①时间淘汰策略
cacheout使用了一个字典来记录每一个缓存实例的过期时间。每次进行淘汰时,都需要遍历字典,时间复杂度为O(n)。
而多级时间轮算法的平均复杂度仅为O(1)。
②缓存统计
在cacheout的最初规划中,有统计缓存实例大小(字节数)的功能(作用类似于redis中的--bigkeys
参数),但是最后由于Python对象的实际大小难以计算放弃了。
由于Python本身的特性,一些优化手段(如多线程和涉及到内存的优化)也无法使用,所以到后期可能考虑使用扩展的方式重写(C或Rust)。这个功能应该能够实现。
③监听器
这是我最想吐槽的点。
on-get回调
我真的想不出来在什么场景下需要on-get回调 。但是作者坚持,好吧
on-delete回调
caffeine在注册删除回调时,使用了RemovalCause
枚举类语义化地指明元素被移除的原因,以便针对不同情况进行不同处理。
- EXPLICIT:缓存被手动指定删除,类似于字典中实例被
pop(key)
删除
- REPLACED:缓存并没有被实际移除,只是值被替换了
- EXPIRED:缓存由于过期被移除
- SIZE:由于容量限制,缓存被移除
COLLECTED:和Java垃圾回收机制相关,我们可以不关注
最初,我参考了这个设计。但是由于cacheout中参照字典提供了一个popitem()
方法导致这个设计不再适用。
popitem()
的作用是根据当前淘汰策略,删除下一个应该被淘汰的缓存。但它的语义不甚明晰。
而且由于cacheout实际上只提供了有界缓存(默认容量为256),手动淘汰缓存不是必须的,似乎没有必要暴露这样一个接口。
最后的结果是,我们被迫将移除原因与具体的方法挂钩:
class RemovalCause(Enum):
DELETE = auto()
SET = auto()
EXPIRED = auto()
FULL = auto()
POPITEM = auto()
这违反了依赖倒置原则。
jingfelix
(Felix Jing)
3
1 个赞