【问题标题】:Practical (Django) Caching Strategy & Implementation? Cache long, Invalidate cache upon data change实用(Django)缓存策略和实现?缓存长,数据更改时缓存无效
【发布时间】:2011-11-23 05:52:06
【问题描述】:

我有一个 Django 应用程序,它可以获取近乎实时的数据(推文和投票),尽管平均每两分钟才会更新一次。但是,我们希望通过在站点和 api 结果进入时立即更新来显示数据。

我们可能会在这个网站上看到大量的负载,所以我最初的想法当然是缓存!

让某种 Memcached 缓存被另一个进程或事件手动失效是否可行?换句话说,我会缓存视图很长一段时间,然后有新的推文和投票使整个视图无效。

  • 可能适度的性能增强是否足以证明增加的复杂性?
  • 是否有我可以创建的实用实现(我与其他开发人员一起工作,因此围绕每个响应调用破解大量内容并不是一个好的选择)?

我不关心仅使某些对象无效,我考虑将MemcachedCache 后端子类化以在this strategy 之后添加一些功能。但当然,Django 的会话也使用 Memcached 作为直写缓存,我不想使 那个无效。

【问题讨论】:

    标签: django caching memcached


    【解决方案1】:

    感谢@rdegges suggestions,我能够找到一个很好的方法来做到这一点。

    我遵循这个范式:

    • 将渲染的模板片段和 API 调用缓存五分钟(或更长时间)
    • 每次添加新数据时使缓存失效。
      • 简单地使缓存失效比保存时重新缓存更好,因为当没有找到缓存数据时,会自动有机地生成新的缓存数据。
    • 在我完成完整更新(例如从推文搜索)后手动使缓存无效,而不是在每个对象保存时。
      • 这样做的好处是使缓存失效的次数更少,但缺点是不会自动进行。

    以下是您需要这样做的所有代码:

    from django.conf import settings
    from django.core.cache import get_cache
    from django.core.cache.backends.memcached import MemcachedCache
    from django.utils.encoding import smart_str
    from time import time
    
    class NamespacedMemcachedCache(MemcachedCache):
    
        def __init__(self, *args, **kwargs):
            super(NamespacedMemcachedCache, self).__init__(*args, **kwargs)
            self.cache = get_cache(getattr(settings, 'REGULAR_CACHE', 'regular'))
            self.reset()
    
        def reset(self):
            namespace = str(time()).replace('.', '')
            self.cache.set('namespaced_cache_namespace', namespace, 0)
            # note that (very important) we are setting
            # this in the non namespaced cache, not our cache.
            # otherwise stuff would get crazy.
            return namespace
    
        def make_key(self, key, version=None):
            """Constructs the key used by all other methods. By default it
            uses the key_func to generate a key (which, by default,
            prepends the `key_prefix' and 'version'). An different key
            function can be provided at the time of cache construction;
            alternatively, you can subclass the cache backend to provide
            custom key making behavior.
            """
            if version is None:
                version = self.version
    
            namespace = self.cache.get('namespaced_cache_namespace')
            if not namespace:
                namespace = self.reset()
            return ':'.join([self.key_prefix, str(version), namespace, smart_str(key)])
    

    这通过在每个缓存条目上设置一个版本或命名空间,然后将该版本存储在缓存中来实现。该版本只是调用reset() 时的当前纪元时间。

    您必须使用settings.REGULAR_CACHE 指定您的备用非命名空间缓存,以便版本号可以存储在非命名空间缓存中(因此它不会递归!)。

    每当您添加一堆数据并想要清除缓存时(假设您已将 this 设置为 default 缓存),只需执行以下操作:

    from django.core.cache import cache
    cache.clear()
    

    您可以通过以下方式访问任何缓存:

    from django.core.cache import get_cache
    some_cache = get_cache('some_cache_key')
    

    最后,我建议您不要将会话放入此缓存中。您可以使用此方法更改会话的缓存键。 (如settings.SESSION_CACHE_ALIAS)。

    【讨论】:

    • 顺便说一句,这些天我仍在使用这种方法,但使用的是 Redis。我制作了一个保存后信号接收器,每次保存任何内容时都会使整个缓存无效(这有点矫枉过正但非常简单)
    【解决方案2】:

    缓存失效可能是最好的方式来处理你正在尝试做的事情。根据您问题的措辞,我将对您的应用做出以下假设:

    • 您有某种 API 正在接收新的信息更新而不进行轮询。 EG:每隔一两分钟,您就会收到一个 API 请求,并且您会在数据库中存储一些信息。
    • 您已经在使用 Memcached 缓存内容以供阅读。可能通过 cronjob 或类似进程定期扫描您的数据库并更新您的缓存。

    假设以上两点都是真的,缓存失效肯定是要走的路。这是在 Django 中执行此操作的最佳方法:

    1. 一个新的 API 请求进入您的服务器,其中包含要存储的新数据。您将其保存在数据库中,并在您的模型类(例如:Tweet、Poll 等)上使用 post save signal 来更新您的 memcached 数据。
    2. 用户访问您的网站并请求阅读他们最近的推文、民意调查或其他内容。
    3. 您从 memcached 中提取推文、投票等数据,并将其显示给他们。

    这本质上就是Django signals 的用途。它们将在您的对象保存/更新后自动运行,这是使用最新信息更新缓存存储的好时机。

    这样做意味着您永远不需要运行定期扫描数据库和更新缓存的后台作业 - 您的缓存将始终使用最新数据即时更新。

    【讨论】:

      猜你喜欢
      • 2013-07-31
      • 2015-07-21
      • 2010-10-06
      • 1970-01-01
      • 1970-01-01
      • 2012-05-08
      • 1970-01-01
      • 2011-06-10
      • 2011-03-06
      相关资源
      最近更新 更多