memcached缓存命中率低主因是django配置与请求模式不匹配:应选pymemcachecache并启用连接池,键需包含用户态/语言等变量,过期时间须匹配业务节奏,空结果也需缓存防穿透。

缓存命中率低,不是Memcached没装好,而是Django缓存配置和使用方式没对齐实际请求模式。
Memcached后端选哪个客户端:PyMemcache vs MemcachedCache
Django官方支持两种Memcached后端:django.core.cache.backends.memcached.MemcachedCache(基于旧版python-memcached)和django.core.cache.backends.memcached.PyMemcacheCache(推荐)。前者在高并发下容易因socket超时阻塞主线程;后者默认启用连接池、异步解析、自动重连,更适合生产环境。
-
PyMemcacheCache需额外安装:pip install pymemcache - 必须显式启用连接池:
'use_pooling': True,否则每次请求新建连接,缓存延迟反而升高 - 若遇到
ConnectionResetError或TimeoutError,优先检查'no_delay': True和'ignore_exc': True是否已设
缓存键生成不一致导致重复写入
Django默认用请求路径+查询参数生成键,但实际中常忽略几个关键变量:用户登录态、语言头、设备类型。比如未登录用户看到的首页和已登录用户的首页,缓存成同一个key,必然击穿。
- 整站中间件缓存(
UpdateCacheMiddleware)默认不包含Cookie或Accept-Language,需手动覆盖CACHE_MIDDLEWARE_KEY_PREFIX或自定义KEY_FUNCTION - 视图级缓存如
@cache_page可传key_prefix,但更灵活的是用cache_page(300, key_prefix='en-us')按语言分片 - 模板片段缓存
{% cache 300 sidebar user.id %}要显式传入user.id等动态变量,否则所有用户共用同一份侧边栏
缓存过期时间与业务节奏错配
设TIMEOUT=300(5分钟)看似安全,但若页面内容每小时才更新一次,频繁失效会把压力推回数据库;反之,新闻列表设24小时过期,用户就看不到新文章。
- 静态资源(CSS/JS压缩后)可用长时效:
django-compressor配合Memcached时,建议TIMEOUT=86400(24小时),靠文件哈希变更自动淘汰 - 用户专属数据(如购物车)必须用带用户标识的key,且
TIMEOUT不宜超过15分钟,避免脏数据滞留 - 数据库查询结果缓存,优先用
cache.get_or_set('key', lambda: db_query(), timeout=600),比先get再set少一次判断,也避免竞态
命中率监控不能只看Memcached stats
stats命令显示的get_hits/cmd_get是底层命中率,但Django可能在中间件或装饰器里提前返回了304 Not Modified,这部分不计入Memcached统计,却真实降低了后端负载。
- 在
settings.py中开启'KEY_FUNCTION': 'django.core.cache.utils.make_key'并打日志,观察key是否重复生成或意外截断 - 用
caches['default'].get_stats()(需Django 4.2+)获取各缓存alias的hits/misses,比直接连Memcached更贴近Django层逻辑 - 最易被忽略的是缓存穿透:大量请求查不存在的ID(如
/user/999999),Memcached不存空值,每次都会打到DB——必须对空结果也cache.set('user_999999', None, 60)
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











