代码之家  ›  专栏  ›  技术社区  ›  knipknap

以递归方式使django缓存中的路径无效

  •  6
  • knipknap  · 技术社区  · 16 年前

    我正在从django缓存中删除一条路径,如下所示:

    from models                   import Graph
    from django.http              import HttpRequest
    from django.utils.cache       import get_cache_key
    from django.db.models.signals import post_save
    from django.core.cache        import cache
    
    def expire_page(path):
        request      = HttpRequest()
        request.path = path
        key          = get_cache_key(request)
        if cache.has_key(key):   
            cache.delete(key)
    
    def invalidate_cache(sender, instance, **kwargs):
        expire_page(instance.get_absolute_url())
    
    post_save.connect(invalidate_cache, sender = Graph)
    

    这是可行的——但是有没有一种递归删除的方法?我的路径如下:

    /graph/123
    /graph/123/2009-08-01/2009-10-21
    

    每当保存ID为“123”的图形时,两个路径的缓存都需要失效。能做到吗?

    3 回复  |  直到 10 年前
        1
  •  10
  •   jkupferman    16 年前

    您可能会考虑采用一种世代缓存策略,它似乎适合您正在尝试实现的目标。在您提供的代码中,您将为每个绝对URL存储一个“生成”编号。因此,例如,您将初始化“/graph/123”以生成一个,那么它的缓存键将变成类似于“/generation/1/graph/123”。当您想使该绝对URL的缓存过期时,您将增加其生成值(在本例中为2)。这样,下次有人查找“/graph/123”时,缓存键变为“/generation/2/graph/123”。这也解决了所有子页过期的问题,因为它们应该引用与“/graph/123”相同的缓存键。

    一开始理解起来有点困难,但这是一种非常优雅的缓存策略,如果正确执行,就意味着您不必从缓存中删除任何内容。欲了解更多信息,请访问 a presentation on generational caching ,它是针对Rails的,但是概念是相同的,不管语言是什么。

        2
  •  1
  •   GrantJ    10 年前

    另一种选择是使用一个支持标记键和逐标记逐出键的缓存。Django的内置缓存API不支持这种方法。但至少有一个缓存后端(不是Django的一部分)有支持。

    DiskCache *是一个Apache2许可的磁盘和文件备份缓存库,用纯Python编写,与Django兼容。要在项目中使用diskcache,只需安装它并配置 CACHES 设置。

    安装很容易 pip :

    $ pip install diskcache
    

    然后配置 高速缓存 设置:

    CACHES = {
        'default': {
            'BACKEND': 'diskcache.DjangoCache',
            'LOCATION': '/tmp/path/to/directory/',
        }
    }
    

    高速缓存 set 方法扩展为可选的 tag 关键字参数如下:

    from django.core.cache import cache
    
    cache.set('/graph/123', value, tag='/graph/123')
    cache.set('/graph/123/2009-08-01/2009-10-21', other_value, tag='/graph/123')
    

    diskcache.DjangoCache 使用A diskcache.FanoutCache 内部的。相应的fanoutcache可通过 _cache 属性并公开 evict 方法。逐出所有标记有 /graph/123 简单地说:

    cache._cache.evict('/graph/123')
    

    虽然访问带下划线前缀的属性可能会感到很尴尬,但diskcache项目是稳定的,不太可能对 DjangoCache 实施。

    这个 Django cache benchmarks 页面讨论了可选的缓存后端。

    • 免责声明:我是diskcache项目的原始作者。
        3
  •  -1
  •   Rais Alam    13 年前

    结账 shutils.rmtree() os.removedirs() . 我想第一个可能是你想要的。

    基于多条评论进行更新 :实际上,django缓存机制比仅使用 path 对于键(尽管您可以在该级别使用它)。我们有一些页面有7或8个单独缓存的子组件,这些子组件根据一系列标准过期。我们的组件缓存名称反映了关键对象(或对象类),并用于标识在某些更新中需要失效的内容。

    我们所有的页面都有一个基于成员/非成员状态的整体缓存键,但这仅占页面的95%。其他5%可以根据每个成员进行更改,因此根本不缓存。

    如何通过缓存迭代查找无效项是它实际存储方式的函数。如果是文件,您可以简单地使用globs和/或递归目录删除,如果是其他机制,则必须使用其他机制。

    我的答案,以及其他人的一些评论,试图说的是,如何完成缓存失效与您使用/存储缓存的方式密切相关。

    第二次更新 :@andybak:所以我猜你的评论意味着我所有的广告网站都将在火焰中爆炸?谢谢你的提醒。我注意到你没有试图回答这个问题。

    Knipknap的问题是他有一组缓存项 出现 由于它们的名称而在层次结构中相互关联,但是缓存机制的密钥生成逻辑通过创建路径+vary_on的MD5哈希来删除该名称。由于没有原始路径/参数的踪迹,您必须彻底猜测所有可能的路径/参数组合,希望您能找到正确的组。我还有其他更有趣的爱好。

    如果希望能够根据路径和/或参数值的某种组合查找缓存项组,则 必须 使用可以直接匹配模式的缓存键 一些保留这些信息以便在搜索时使用的系统。

    因为我们的需求与OP的问题无关,所以我们在两年多前控制了模板片段缓存,特别是密钥生成。它允许我们以多种方式使用regexp,以有效地使相关缓存项组无效。我们还添加了一个默认超时和变量名的变化(在运行时解决),可在 settings.py ,更改了名称&timeout的顺序,因为命名片段时总是必须重写默认超时是没有意义的,这使得片段的名称可以解析(也就是说,它可以是一个变量),以便更好地使用多级模板继承方案,以及其他一些事情。

    我最初回答的唯一原因,对于当前的django来说,这确实是错误的,因为我已经使用了很长时间的更健全的缓存键,实际上我忘记了我们所放弃的简单机制。