redis连接必须用连接池而非每次新建实例,避免耗尽文件描述符;缓存键需带业务前缀和环境标识,禁止用keys *,注意空值处理与缓存一致性。

Redis连接必须用连接池,别直接redis.Redis()
每次请求都新建redis.Redis()实例,会快速耗尽系统文件描述符,出现ConnectionRefusedError或大量TimeoutError。Flask应用是多线程/多协程的,共享一个连接池才是正确姿势。
- 在应用初始化时创建
redis.ConnectionPool,传给redis.Redis(connection_pool=...) - 避免在路由函数里重复初始化
Redis对象;把它挂到app.config或用全局单例(如g不适用,推荐current_app扩展方式) - 连接池参数建议:
max_connections=20(够中小型项目),socket_timeout=1,retry_on_timeout=True
缓存键设计要带业务前缀和可读分隔符
直接用f"user:{user_id}"看着简单,但线上出问题时根本分不清是哪个服务、哪个环境写的键。更糟的是,不同模块用相同键名会互相覆盖。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 统一格式:
f"{APP_NAME}:{ENV}:user:{user_id}",比如"myflask:prod:user:123" - 避免在键里拼接JSON或复杂结构——Redis键只支持字符串,序列化后不可读、难调试
- 不要依赖
keys *查键(O(n)阻塞操作),改用scan配合前缀,或用redis-cli --scan --pattern "myflask:prod:user:*"
cache.get()返回None不等于“没命中”,可能是缓存了None或空字节串
很多同学写if not cache.get(key): do_db_query(),结果发现数据库被反复打爆——因为redis-py把None、""、b""都当有效值存进去了,而get()查不到时才真返回None。但你没法区分“缓存里存了个空值”和“压根没这个键”。
- 写缓存时显式过滤掉
None和空值:if result is not None and result != "": cache.set(key, result, ex=300) - 读缓存时用
cache.exists(key)辅助判断(注意:它和get()不是原子的,仅作排查用) - 更稳妥的做法:约定“缓存空值”也存为特殊标记,比如
"__NULL__",读到就当没命中
Flask中缓存更新要兼顾一致性,别只删不设
用户资料更新后,只执行cache.delete("user:123"),下次查询还是打到DB。如果并发高,可能多个请求同时查库、同时写缓存,造成雪崩或脏数据。
- 更新DB后立即
cache.set()新值(主动刷新),比被动删除更可控 - 删除+重算逻辑必须加锁(如
cache.set("lock:user:123", "1", nx=True, ex=5)),否则并发下仍可能重复查库 - 对高频更新字段(如计数器),优先用Redis原生命令
incr/decr,别绕路Python运算再set
redis-cli monitor看几秒真实命令流。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










