如何有效地使几个django缓存值无效?

时间:2017-07-26 23:41:48

标签: django django-cache

TLDR

有没有办法标记缓存的值,以便我可以执行以下操作:

cache.filter('some_tag').clear()

详细

在我的项目中,我有以下模型:

class Item(models.Model):
    month = models.DateField('month', null=False, blank=False, db_index=True)
    kg = models.BigIntegerField('kg')
    tags = models.ManyToManyField('Tag', related_name='items')
    // bunch of other fields used to filter data

我有一个report_view根据提供的kgfilters 按月按标记返回总和在URL查询中。

这样的事情:

--------------------------------
|Tag    |jan    |fev    |mar   |
--------------------------------
|Tag 1  |1000   |1500   |2000  |
--------------------------------
|Tag 2  |1235   |4652   |0     |
--------------------------------

由于我的Item表已有超过400万条记录并且一直在增长,我的report_view会被缓存。

到目前为止,我完成了所有这些工作。

问题是:网站用户可以从tags更改Items,每次发生这种情况我都要使缓存无效,但我想做它采用更多粒度方式。

例如,如果用户更改tag中的Item january,该tag应该使该月的所有总计无效(我更喜欢按月缓存,因为有时会更改一个URL 1}}对其他人有级联效应)。但是,我不知道所有已缓存的视图,因为有数千种不同的过滤器可能会更改tag

到目前为止我做了什么:

  • 设置信号以在cache.filter('some_tag').clear()更改
  • 时使我的所有缓存无效
@receiver(m2m_changed, sender=Item.tags.through)
def tags_changed(sender, **kwargs):
    cache.clear()

但这可以清除一切在我的情况下不是最佳的东西。有没有办法像Django缓存框架那样做pip install cherrypy

1 个答案:

答案 0 :(得分:2)

https://martinfowler.com/bliki/TwoHardThings.html

  

计算机科学中只有两件事:缓存失效和命名事物。

     

- Phil Karlton

假设您使用Django's Cache Middleware,您需要定位相关的缓存键。您可以在Django项目中看到它们如何从这两个文件生成缓存键:

-  https://github.com/django/django/blob/master/django/middleware/cache.py#L99
- https://github.com/django/django/blob/master/django/utils/cache.py#L367
- https://github.com/django/django/blob/master/django/utils/cache.py#L324

_generate_cache_key

def _generate_cache_key(request, method, headerlist, key_prefix):
    """Return a cache key from the headers given in the header list."""
        ctx = hashlib.md5()
        for header in headerlist:
            value = request.META.get(header)
            if value is not None:
                ctx.update(force_bytes(value))
        url = hashlib.md5(force_bytes(iri_to_uri(request.build_absolute_uri())))
        cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % (key_prefix, method, url.hexdigest(), ctx.hexdigest())
        return _i18n_cache_key_suffix(request, cache_key)

基于来自请求和散列值的属性和头部生成缓存密钥(即,对URL进行散列并将其用作密钥的一部分)。响应中的Vary标头指定要用作缓存的一部分的其他标头。

如果您了解Django如何缓存您的视图并计算缓存密钥,那么您可以使用它来定位适当的缓存条目,但这仍然非常困难,因为网址是经过哈希处理的,您无法定位网址模式(你可以使用https://stackoverflow.com/a/35629796/784648 cache.delete_patterns(...)

Django主要依靠超时来使缓存失效。

我建议查看Django Cacheops,此软件包旨在与Django的ORM一起使用来缓存和使QuerySet无效。这似乎对您的需求更加实用,因为您希望在Item QuerySet上进行细粒度的失效,而您根本无法从Django的缓存中间件中获得这种失效。看看github repo,我已经使用过它,如果你花时间阅读文档并理解它,它会很好用。