6

我正在从 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”的图形时,两条路径的缓存都需要失效。这可以做到吗?

4

3 回答 3

10

您可能需要考虑采用分代缓存策略,它似乎适合您想要完成的任务。在您提供的代码中,您将为每个绝对 url 存储一个“世代”编号。因此,例如,您将初始化“/graph/123”以生成一代,然后其缓存键将变为类似于“/GENERATION/1/graph/123”的内容。当您想要使该绝对 url 的缓存过期时,您增加其生成值(在这种情况下为 2)。这样,下次有人去查找“/graph/123”时,缓存键就变成了“/GENERATION/2/graph/123”。这也解决了所有子页面过期的问题,因为它们应该引用与“/graph/123”相同的缓存键。

一开始理解起来有点棘手,但它是一个非常优雅的缓存策略,如果正确完成意味着您不必实际从缓存中删除任何内容。有关更多信息,这里是关于分代缓存的演示文稿,它适用于 Rails,但概念是相同的,与语言无关。

于 2010-01-28T18:08:41.060 回答
1

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

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

安装很容易pip

$ pip install diskcache

然后配置您的CACHES设置:

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在内部使用diskcache.FanoutCache。对应的 FanoutCache 可通过_cache属性访问并公开一个evict方法。要驱逐所有标记为/graph/123简单的键:

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

尽管访问带下划线前缀的属性可能会让人感觉很尴尬,但 DiskCache 项目是稳定的,不太可能对实现进行重大更改DjangoCache

Django 缓存基准测试页面讨论了替代缓存后端。

  • 免责声明:我是 DiskCache 项目的原作者。
于 2016-03-21T18:08:50.667 回答
-1

结帐shutils.rmtree()os.removedirs()。我认为第一个可能是您想要的。

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

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

您如何遍历缓存以查找无效项目取决于它的实际存储方式。如果是文件,您可以简单地使用 glob 和/或递归目录删除,如果是其他机制,那么您将不得不使用其他机制。

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

第二次更新:@andybak:所以我猜你的评论意味着我所有的商业 Django 网站都会在火焰中爆炸?感谢您对此的关注。我注意到您没有尝试解决问题。

Knipknap 的问题在于,他有一组缓存项,由于它们的名称,它们看起来是相关的并且在层次结构中,但是缓存机制的密钥生成逻辑通过创建路径 + 变化的 MD5 哈希来消除该名称。由于没有原始路径/参数的痕迹,您将不得不详尽地猜测所有可能的路径/参数组合,希望您能找到正确的组。我还有其他更有趣的爱好。

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

因为我们需要与 OP 的问题无关的需求,所以我们在 2 年前控制了模板片段缓存——特别是密钥生成。它允许我们以多种方式使用正则表达式来有效地使相关缓存项组无效。我们还添加了可配置的默认超时和变量名称(在运行时解析)settings.py,更改了名称和超时的顺序,因为总是必须覆盖默认超时以命名片段是没有意义的,使片段名称可解析(即它可以是一个变量)更好地使用多级模板继承方案以及其他一些东西。

我最初回答的唯一原因(这对于当前的 Django 确实是错误的)是因为我一直在使用更健全的缓存键,我真的忘记了我们离开的简单机制。

于 2010-01-03T16:22:14.183 回答