sql查询缓存需主动设计,优先缓存读多写少的高频固定查询、聚合统计和配置类数据;键名应语义化,值存结构化json;失效以更新驱动为主,组合redis与本地缓存,并防御穿透、雪崩、击穿。

SQL查询缓存不是数据库自带的“开关”,而是需要结合业务特征、数据更新频率和一致性要求,主动设计的一套结果复用机制。核心目标是减少重复查询对数据库的压力,同时避免因缓存陈旧导致业务出错。
缓存什么:优先缓存“读多写少、结构稳定”的查询结果
不是所有SQL都适合缓存。应聚焦以下几类:
-
高频固定参数查询:如
SELECT * FROM users WHERE status = 'active' AND city = 'shanghai',参数固定、调用频繁,缓存键明确(如users_active_shanghai) -
聚合统计类结果:如
SELECT COUNT(*) FROM orders WHERE created_at > '2024-01-01',计算开销大、更新不频繁,缓存有效期可设为小时级 - 配置/字典类数据:如省份列表、订单状态码映射,基本静态,适合长期缓存(TTL设为数天或监听变更刷新)
避免缓存带用户ID、时间戳等高变动参数的查询,或实时性要求极高的场景(如交易余额、库存扣减)。
缓存怎么存:推荐使用带前缀的键名 + 结构化值
缓存键设计要可读、可管理、防冲突:
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除 了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plan Old Java Objects,普通的 Java 对象)映射成数据库中的记录。有需要的朋友可以下载看看
- 键名格式建议:
sql:{md5(原始SQL)}:{参数签名}或更语义化的query:order:summary:monthly:202406 - 缓存值建议存储为结构化JSON,包含:
data(查询结果数组)、meta.timestamp(生成时间)、meta.version(可选,用于灰度或schema变更) - 不建议直接缓存裸ResultSet或二进制对象,不利于调试与跨语言兼容
缓存何时失效:按更新源头驱动,而非单纯依赖过期时间
被动过期(TTL)仅作兜底,主动失效才是保障一致性的关键:
- 在执行
INSERT/UPDATE/DELETE相关表的操作后,同步清理对应缓存键(例如更新orders表,就删掉所有query:order:*开头的键) - 对复杂关联查询,可采用“写穿透+标记失效”策略:更新时不清缓存,而是在缓存值中加
"stale": true字段,下次读取时触发异步回源更新 - 对无法监听写操作的场景(如直连MySQL无中间件),可用轻量级变更日志表(如
cache_invalidation_log)轮询触发清理
缓存放哪:根据延迟、容量、运维成本选择合适载体
常见选项对比:
- Redis:最常用,支持丰富数据结构、原子操作、分布式共享;适合中小规模结果集(单条建议
- Caffeine(本地堆缓存):零网络开销,适合低延迟、读远大于写的场景;但需配合分布式失效通知(如Redis Pub/Sub)解决多实例一致性
- 数据库层缓存(如MySQL Query Cache):已弃用(8.0移除),不推荐;语句级匹配太脆弱,且锁粒度大,易成瓶颈
生产环境通常组合使用:热点小数据走本地缓存,通用/共享/大结果走Redis。
不复杂但容易忽略的是缓存穿透、雪崩、击穿问题——需搭配布隆过滤器、随机TTL、逻辑过期等手段防御,这些属于缓存工程的标配补丁,不是可选项。









