用 redis-py + 连接池 + Cache-Aside 模式可稳定承载80%以上读请求,因连接池避免频繁建连耗尽 socket 资源,Cache-Aside 保障数据一致性,且必须用 setex 等原子操作、合理设计缓存键与过期策略。

直接上结论:用 redis-py + 连接池 + Cache-Aside 模式,能稳定扛住 80% 以上读请求,数据库连接数下降明显。
为什么不能直接用 r = redis.Redis()?
每次请求都新建连接,会快速耗尽 socket 资源,尤其在 gunicorn/uwsgi 多 worker 场景下。本地测试可能不显,上线后常出现 ConnectionRefusedError 或连接超时。
- 必须用
redis.ConnectionPool管理连接,设置max_connections=20(根据并发量调) -
decode_responses=True要加,否则所有get()返回bytes,后续 JSON 解析或字符串拼接全报错 - 生产环境禁用
host='localhost',改用域名或内网 IP,避免 DNS 缓存失效导致连接失败
cache.setex() 和 cache.set() + cache.expire() 有什么区别?
前者是原子操作,后者不是。高并发下若先 set 再 expire,中间若进程崩溃或被杀,缓存就永久存在,变成“脏数据”。
- 一律用
cache.setex(key, timeout, value)或cache.set(key, value, ex=timeout) - timeout 单位是秒,别传毫秒(比如误写
ex=3600000就是 1 小时 → 41 天) - 对 JSON 数据,建议先
json.dumps(value)再存,别依赖 Redis 自动序列化
分页查询缓存怎么避免 key 冲突和雪崩?
直接拼 f"list:page:{page}:size:{size}" 是常见错误——不同用户、不同筛选条件共用同一 key,导致返回错乱数据。
立即学习“Python免费学习笔记(深入)”;
- key 必须包含业务维度,例如
f"product:list:cat_{cat_id}:sort_{sort}:page_{page}" - 给分页缓存设随机抖动过期时间,比如基础 10 分钟,再 ±60 秒:
ex=600 + random.randint(-60, 60) - 不要缓存空结果(
None或空列表),否则缓存穿透风险拉满;需配合布隆过滤器或空值缓存(cache.setex(key, 60, "NULL"))
Python Web 中缓存逻辑该放哪一层?
别塞进视图函数里。一旦多个接口查同一张表(如 users),重复写缓存逻辑,维护成本爆炸。
- 封装成独立 service 函数,例如
get_user_cached(user_id),内部统一处理缓存/DB 读写 - 数据库更新时,必须同步
cache.delete(f"user:{user_id}"),而不是等 TTL 过期 - 慎用装饰器自动缓存(如
@cache.memoize),它无法控制 key 构造逻辑,也难做空值/异常处理
最易被忽略的点:Redis 连接池没设 retry_on_timeout=True,网络抖动时请求直接抛 redis.exceptions.TimeoutError,而你还在查 MySQL 日志找慢查询。


















