【问题标题】:How to invalidate several django cached values efficiently?如何有效地使几个 django 缓存值无效?
【发布时间】:2018-01-02 11:50:54
【问题描述】:

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,它根据URL 查询中提供的filters 返回kg 按月按标签 的总和.

类似这样的:

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

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

到目前为止,我已经涵盖了所有这些内容。

问题是:站点用户可以将 tagsItems 更改为每次发生这种情况时我都必须使缓存无效,但我想在更多粒状方式。

例如,如果用户将 Item 中的 tagjanuary 更改为无效,则该月的所有总数应该无效(我更喜欢按月缓存,因为有时更改一个 tag 会对其他)。但是我不知道所有被缓存的视图,因为有成千上万种不同的过滤器会改变URL

到目前为止我做了什么:

  • tag 更改时,设置一个信号使我的所有缓存失效
@receiver(m2m_changed, sender=Item.tags.through) def tags_changed(sender, **kwargs): 缓存.clear()

但这会清除所有在我的情况下不是最佳的东西。有没有办法用 Django 缓存框架做类似cache.filter('some_tag').clear() 的事情?

【问题讨论】:

    标签: django django-cache


    【解决方案1】:

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

    计算机科学中只有两个难点:缓存失效和命名。

    -- 菲尔·卡尔顿

    假设您使用的是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 如何缓存您的视图并计算您的缓存键,那么您可以使用它来定位适当的缓存条目,但这仍然非常困难,因为 url 是散列的,您无法定位 url 模式(您可以使用https://stackoverflow.com/a/35629796/784648 cache.delete_patterns(...) 否则)。

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

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

    【讨论】:

    • 感谢您的回复。即使知道cache_keys 是如何为URL 生成的,我也无法生成每一个cache_key 的可能性。有许多 filter 字段具有数千个不同的可能值,我将不得不遍历所有可能性以检查缓存是否存在而不是删除它... cache.delete_patterns() 似乎是一个更好的选择,但它需要一个外部包..我不知道是否值得仅为一个功能安装它...我将检查他们如何实现此缓存逻辑...
    猜你喜欢
    • 1970-01-01
    • 2010-12-31
    • 2023-03-21
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 2015-12-26
    • 2015-03-24
    • 1970-01-01
    相关资源
    最近更新 更多