Cer*_*rin 5 python django memcached django-models django-views
如何缓存分页的 Django 查询集,特别是在 ListView 中?
我注意到一个查询需要很长时间才能运行,因此我尝试缓存它。查询集很大(超过 100k 条记录),因此我尝试仅缓存其分页部分。我无法缓存整个视图或模板,因为有些部分是特定于用户/会话的并且需要不断更改。
ListView 有几个用于检索查询集的标准方法get_queryset(),返回非分页数据,并按paginate_queryset()当前页面过滤数据。
我首先尝试在 中缓存查询get_queryset(),但很快意识到调用cache.set(my_query_key, super(MyView, self).get_queryset())导致整个查询被序列化。
然后我尝试重写paginate_queryset():
import time
from functools import partial
from django.core.cache import cache
from django.views.generic import ListView
class MyView(ListView):
...
def paginate_queryset(self, queryset, page_size):
cache_key = 'myview-queryset-%s-%s' % (self.page, page_size)
print 'paginate_queryset.cache_key:',cache_key
t0 = time.time()
ret = cache.get(cache_key)
if ret is None:
print 're-caching'
ret = super(MyView, self).paginate_queryset(queryset, page_size)
cache.set(cache_key, ret, 60*60)
td = time.time() - t0
print 'paginate_queryset.time.seconds:',td
(paginator, page, object_list, other_pages) = ret
print 'total objects:',len(object_list)
return ret
Run Code Online (Sandbox Code Playgroud)
然而,尽管只检索了 10 个对象,并且每个请求都显示“重新缓存”,这意味着没有任何内容被保存到缓存中,但运行时间几乎需要一分钟。
我的settings.CACHE样子:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
Run Code Online (Sandbox Code Playgroud)
并service memcached status显示 memcached 正在运行,但tail -f /var/log/memcached.log什么也没显示。
我究竟做错了什么?缓存分页查询以便不检索整个查询集的正确方法是什么?
编辑:我认为它们可能是 memcached 或 Python 包装器中的错误。Django 似乎支持两种不同的 memcached 后端,一种使用 python-memcached,一种使用 pylibmc。python-memcached 似乎默默地隐藏了缓存paginate_queryset()值的错误。当我切换到 pylibmc 后端时,现在我收到一条明确的错误消息“error 10 from memcached_set: SERVER ERROR”,追溯到 set 中的 django/core/cache/backends/memcached.py,第 78 行。
事实证明,这个问题是多种因素综合作用的结果。主要是,返回的结果包含paginate_queryset()对无限查询集的引用,这意味着它本质上是不可缓存的。当我调用 时cache.set(mykey, (paginator, page, object_list, other_pages)),它试图序列化数千条记录,而不仅仅是page_size我期望的记录数,导致缓存项超出 memcached 的限制并失败。
另一个因素是 memcached/python-memcached 中可怕的默认错误报告,它会默默地隐藏所有错误,并在出现任何问题时将 cache.set() 转换为 nop,这使得追踪问题非常耗时。
我通过基本上重写来解决这个问题,paginate_queryset()完全放弃 Django 的内置分页器功能并自己计算查询集:
object_list = queryset[page_size*(page-1):page_size*(page-1)+page_size]
Run Code Online (Sandbox Code Playgroud)
然后缓存它 object_list。
| 归档时间: |
|
| 查看次数: |
6493 次 |
| 最近记录: |