关于开发一个Python 本地缓存库的想法

我在去年由于个人需求,需要选型、使用一款Python本地缓存库。但令人遗憾的是,我发现我的选择并不多,大概只有以下两个:

我最终选择了cacheout。因为python-diskcache是以SQLite、MongoDB等数据库为存储后端的,性能不如内存缓存。这点从它的名字里也能看出来。

cacheout基本满足了我的需求。但是相较于其他语言生态下的缓存库,只能说是差强人意。
于是我参考Java中的caffeine缓存库,完成了cacheout的监听器和数据统计模块。在这个过程中,也发现了一些我个人认为设计得不合理的地方(我会在下面的评论中列出,欢迎探讨)。逐渐萌生了直接参考caffeine,实现一个Python缓存库的想法。

由于辞职,最近难得有了一段空闲时间。所以我决定将这个想法付诸于实践。(PS:如果各位大佬有内推,欢迎砸向我 :wink:

除了包含cacheout的现有功能外,在我目前的构想中,它主要包括以下要点:

我仍不确定这项工作是否有意义,所以请大家投个票吧。
你是否需要这样一个本地缓存工具库?

  • 需要
  • 不需要
  • 不确定
0 投票人

①时间淘汰策略

cacheout使用了一个字典来记录每一个缓存实例的过期时间。每次进行淘汰时,都需要遍历字典,时间复杂度为O(n)。

而多级时间轮算法的平均复杂度仅为O(1)。

②缓存统计

在cacheout的最初规划中,有统计缓存实例大小(字节数)的功能(作用类似于redis中的--bigkeys参数),但是最后由于Python对象的实际大小难以计算放弃了。

由于Python本身的特性,一些优化手段(如多线程和涉及到内存的优化)也无法使用,所以到后期可能考虑使用扩展的方式重写(C或Rust)。这个功能应该能够实现。

③监听器

这是我最想吐槽的点。

on-get回调

我真的想不出来在什么场景下需要on-get回调 :joy:。但是作者坚持,好吧:man_shrugging:

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()

这违反了依赖倒置原则。

check GitHub - Yiling-J/theine: high performance in-memory cache ?

1 Like

看来没必要了,社区真是卧虎藏龙啊 :grinning: