切换日光/暗黑模式
055. Redis 连接池与实体缓存
学习目标
这一节开始实现 Redis 缓存。
学完后,你应该能理解:
- Redis 在项目里缓存哪些数据;
- 为什么缓存能减少数据库查询;
- Redis 连接池负责什么;
- 为什么缓存也要设置过期时间;
RedisCache如何缓存数据库实体;- 为什么要区分“没有缓存”和“数据库没有值”。
为什么需要 Redis
Redis 是内存型缓存数据库。
它适合缓存经常读取、变化不太频繁的数据。
项目里可以缓存:
- 当前用户信息;
- 通用模块配置信息;
- Dify 插件调用后端接口用的 API token;
- AI 模型配置;
- 其他高频查询数据。
这些数据如果每次请求都查 MySQL,会给数据库带来压力。
放进 Redis 后,请求可以优先从缓存读取。
缓存不是越多越好
Redis 使用内存。
缓存越多,占用内存越多。
学习环境的服务器内存比较紧张,前端、后端、Dify、数据库等服务都会吃内存。
所以课程项目只缓存必要内容。
真实业务服务器内存更大,可以根据访问频率和成本决定缓存范围。
Redis 连接池
后端连接 Redis 时,不应该每次请求都新建连接。
更合理的方式是创建连接池。
连接池负责复用连接:
- 请求需要 Redis 时,从池里拿连接;
- 用完后连接回到池里;
- 程序退出时关闭连接池。
这样能减少频繁创建连接的成本。
连接健康检查
应用启动时可以检查 Redis 是否正常。
常见做法是:
- 获取 Redis client;
- 执行
ping; - 写入一条临时缓存;
- 再读出来验证值是否一致;
- 设置短过期时间,避免测试数据长期残留。
如果 Redis 连不上,应用启动时就能尽早发现。
Docker 里的 Redis
课程环境里的 Redis 通过 Docker 运行。
宿主机端口映射到容器里的 Redis 端口。
Redis Commander 作为 Web 管理工具,也在同一个 Docker 网络里访问 Redis。
这能帮助你在浏览器里看到缓存 key 是否写入、过期和删除。
RedisUtil
RedisUtil 负责通用 Redis 能力。
它包含:
- 初始化连接池;
- 获取 Redis client;
- 检查连接是否正常;
- 关闭连接池;
- 读取 Redis 环境变量配置。
业务代码不应该到处重复创建 Redis client。
统一工具类能减少连接管理混乱。
为什么缓存实体
用户信息这类数据会被频繁查询。
例如认证中间件几乎每次请求都可能需要当前用户。
如果每次都查 MySQL,压力会很大。
可以把用户实体按用户名缓存起来。
后续再需要这个用户时,先查 Redis。
Redis 没有命中,再查 MySQL,并把结果写回 Redis。
RedisCache
RedisCache 是面向实体类的缓存封装。
初始化时会指定:
- 缓存 key 前缀;
- 对应的模型类;
- 查询字段;
- 默认缓存过期时间;
- 空值缓存过期时间。
例如用户缓存可以用 user 作为前缀,用用户名作为查询字段。
最终缓存 key 可以类似:
txt
user:zhangsan没有缓存与没有值
读取缓存时有两个不同情况:
- Redis 里没有这个 key;
- Redis 里记录了这个 key 对应的数据库数据不存在。
这两个不能混在一起。
如果只是 Redis 没有缓存,可以去 MySQL 查。
如果已经缓存了“数据库没有值”,就不必每次都继续查 MySQL。
这叫空值缓存。
空值缓存
空值缓存用一个特殊标记表示数据库没有对应记录。
例如查询不存在的用户名:
- 第一次 Redis 没命中;
- 去 MySQL 查,也没有;
- 把“无值标记”写入 Redis;
- 下一次相同查询直接返回无值。
空值缓存过期时间通常比较短。
这样能减少恶意或错误请求反复击穿数据库。
写入缓存
写缓存时要把模型对象转成字典,再序列化成 JSON 字符串。
如果对象里有 None 字段,可以按项目需要去掉。
Redis 里统一存字符串,读取时再解析回字典。
这样缓存格式更可控。
缓存命中后的变化
第一次查询用户时,可能会看到 SQL 执行日志。
因为 Redis 没有命中,需要查 MySQL。
第二次查询同一个用户时,如果缓存还没过期,就不会再执行 SQL。
这就是缓存减少数据库访问的直接效果。
这一节的重点
这一节搭起 Redis 缓存基础。
你需要记住:
- Redis 适合高频读取数据;
- 连接池负责复用连接;
- 缓存要设置过期时间;
- 实体缓存要有 key 前缀和查询字段;
- 空值缓存能避免不存在的数据反复打到 MySQL。