必须配连接池、做序列化、加exists判断,否则易现连接耗尽、缓存污染或none误判;redis.redis()默认无连接池,多worker下易报connectionreseterror。

直接用 redis-py 手动管理缓存最可控,但必须配连接池、做序列化、加 exists 判断——否则上线后大概率遇到连接耗尽、缓存污染或 None 值误判。
redis.Redis() 默认不带连接池,多 worker 下很快报 ConnectionResetError
默认每次调用 redis.Redis() 都新建 TCP 连接,Flask/Gunicorn 多进程或多线程时,连接数指数级上涨,系统很快卡死或抛 ConnectionResetError。
- 必须显式创建连接池:
pool = redis.ConnectionPool(host="localhost", port=6379, db=0, max_connections=20) - 所有客户端实例共用该池:
r = redis.Redis(connection_pool=pool) -
max_connections建议设为 Web server worker 数 × 2~3,别盲目设太高,Redis 本身也有连接上限
缓存读写前必须处理 Python 对象序列化和 None 边界
Redis 只存字节串,set() 直接传 dict/list 会报 TypeError;而 get() 返回 None 时,你无法区分是 key 不存在,还是之前真存了 None 字符串。
- 统一用
json.dumps()序列化,json.loads()反序列化,禁用pickle(有反序列化风险 + 跨版本不兼容) - 读缓存前先用
r.exists(key)判断是否存在,再get()+json.loads(),避免把"null"当成业务数据 - 小数据走
setex(key, ttl, json.dumps(data));超 100KB 的响应体建议先zlib.compress()再存
缓存键设计错一个字段,就可能造成用户看到别人的数据
比如接口 /api/user/profile 没把 user_id 或角色 scope 写进 key,管理员请求后缓存被普通用户命中,数据就串了。
- key 必须包含可区分上下文的字段:如
f"v2:profile:{user_id}:{role}" - 禁止把原始 token、session_id、时间戳等动态值直接拼进 key——会导致缓存碎片化、命中率暴跌
- 参数多时别手拼,用
hashlib.md5(json.dumps(sorted(params.items())).encode()).hexdigest()[:8]生成稳定短哈希 - key 总长度控制在 512 字节内,过长会拖慢 Redis 查找(O(N) 复杂度)
单纯 setex(ttl) 在高并发下容易雪崩
大量 key 同一时刻过期,缓存集体失效,所有请求穿透到 DB,瞬间打挂后端。
- 给 TTL 加随机偏移:
ttl = 300 + random.randint(0, 60),让过期时间分散开 - 对关键接口加“逻辑过期”:缓存值里嵌入
{"data": ..., "expire_at": 1749438000},应用层主动判断是否需后台刷新 - 冷启动或配置变更时,用
r.scan(match="v2:profile:*")+r.delete()主动清理旧 key,别等它自然过期
缓存不是开关一开就完事,它本质是另一层状态系统——键名、序列化、连接生命周期、过期分布,每个点都得对齐业务真实节奏。漏掉任意一个,线上问题往往不是慢,而是错得悄无声息。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











