不糟糕的语义失效

发布: (2026年3月3日 GMT+8 22:45)
4 分钟阅读
原文: Dev.to

Source: Dev.to

缓存问题

如果你在一个 web 应用上工作过一段时间,你一定了解缓存的情况。你加入缓存后,一切都变快了,但随后有人更新了某些数据,用户却看到旧的数据——价格、库存等等。TTL 能帮忙,但你总是在新鲜度和负载之间做权衡。

常见的解决办法是手动失效:更新商品后,失效对应的缓存键。这对单一接口有效,但当该商品有评论、评论有回复、商品属于某个店铺、店铺又属于某个组织时,情况会迅速变得混乱。你必须追踪大量关系,并在各处失效键。

介绍 ZooCache

ZooCache 是一个基于 Rust 核心的 Python 缓存库,专注于 语义失效——依据 什么 发生了变化来失效,而不仅仅是 何时

注册依赖

当你缓存某个结果时,需要声明它所依赖的数据标签(dependencies):

from zoocache import cacheable, invalidate, add_deps, configure

configure()

@cacheable()
def get_product(pid):
    add_deps([f"product:{pid}"])
    return db.get_product(pid)

@cacheable()
def get_reviews(pid):
    add_deps([f"product:{pid}:reviews"])
    return db.get_reviews(pid)

@cacheable()
def get_store_products(sid):
    add_deps([f"store:{sid}:products"])
    return db.get_store_products(sid)

@cacheable()
def get_org_stores(oid):
    add_deps([f"org:{oid}:stores"])
    return db.get_org_stores(oid)

这些标签形成层级结构。例如,org:1:stores:2:products:42 就是 PrefixTrie 中的一条路径。

失效标签

当数据变化时,失效相应的标签:

def update_product(pid, data):
    db.update_product(pid, data)
    invalidate(f"product:{pid}")

def update_store(sid, data):
    db.update_store(sid, data)
    invalidate(f"store:{sid}")

def update_org(oid, data):
    db.update_org(oid, data)
    invalidate(f"org:{oid}")

失效 org:1 会清除其下的所有内容——商品、评论、店铺商品等。你不再需要记住哪些函数缓存了哪些数据。

失效操作的时间复杂度是 O(D),其中 D 为标签深度,且与缓存条目数量无关。

跨实例的一致性

如果你运行多个实例,ZooCache 使用 Hybrid Logical Clocks (HLC) 来保证一致性。每一次失效都会带上一个时间戳,该时间戳考虑了时钟漂移,确保即使系统时钟不同步,后来的失效也拥有更高的时间戳。

被动重新同步

每个缓存条目都会存储版本信息。当一个节点从另一个节点读取数据时,会检查这些版本;如果发现更新的版本,节点会自动追上最新状态。这样即可在无需额外协调的情况下保持读取的一致性。

防止缓存击穿

当缓存条目过期且大量请求同时击中数据库时,会出现 击穿(stampede)现象。ZooCache 通过 SingleFlight 模式来缓解:第一个请求执行实际工作,其他请求等待,随后所有请求都得到相同的结果。数据库只会收到一次查询,而不是多次。

功能一览

  • 存储后端:内存、LMDB 或 Redis
  • 框架集成:FastAPI、Django、Litestar
  • 序列化:使用 LZ4 压缩的 MsgPack
  • 可观测性:日志、Prometheus、OpenTelemetry
  • CLI 用于监控
  • Rust 核心 并提供 Python 绑定

基准测试可在文档站点查看。

安装

uv add zoocache

链接

欢迎尝试并分享你的想法——无论是问题、建议还是其他任何反馈。我们始终乐于倾听,帮助改进。

0 浏览
Back to Blog

相关文章

阅读更多 »

无权重新授权此项目

嗨,我是 Mark Pilgrim。你可能记得我,曾写过《Dive Into Python》和《Universal Character Encoding Detector》这些经典作品。我是 chardet 的原作者……