实用(Django)缓存策略&实现?缓存长,在数据更改时使缓存无效

时间:2011-09-22 22:24:39

标签: django caching memcached

我有一个Django应用程序可以获得接近实时的数据(推文和投票),尽管更新平均每隔一两分钟就会发生一次。但是,我们希望通过更新站点和api结果来显示数据。

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

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

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

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

2 个答案:

答案 0 :(得分:6)

缓存失效可能是处理您尝试执行的操作的最佳方式。根据您的问题的措辞,我将假设您的应用程序如下:

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

假设上述两件事情都是真的,那么缓存失效肯定是要走的路。这是Django中最好的方法:

  1. 新的API请求进入您的服务器,其中包含要存储的新数据。您将其保存在数据库中,并在模型类上使用post save signal(EG:Tweet,Poll等)来更新您的memcached数据。
  2. 用户访问您的网站并请求阅读他们最近的推文,民意调查等。
  3. 您从memcached中提取数据,轮询等数据,并将其显示给他们。
  4. 这基本上是Django signals的意思。在保存/更新对象后,它们将自动运行,这是使用最新信息更新缓存存储的好时机。

    这样做意味着您永远不需要运行定期扫描数据库并更新缓存的后台作业 - 您的缓存将始终与最新数据保持同步。

答案 1 :(得分:4)

感谢@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指定备用非namspaced缓存,因此版本号可以存储在非命名空间缓存中(因此它不会递归!)。

每当您添加一堆数据并希望清除缓存时(假设您已将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)。