缓存策略文档
📑 目录
概述
monSQLize 提供了强大的内置缓存系统,支持 TTL(生存时间)、LRU(最近最少使用)淘汰策略、多层缓存、缓存失效机制和统计监控。本文档详细说明缓存的配置和使用。
⚠️ 若你关心的是公开缓存类/函数(
MemoryCache、createRedisCacheAdapter()、DistributedCacheInvalidator、withCache()、FunctionCache)的升级迁移,请优先阅读cache-hub 直调迁移说明。本页主要覆盖 monSQLize 的查询缓存与多层缓存接入方式。
核心特性
- ✅ TTL 过期:自动淘汰过期数据
- ✅ LRU 淘汰:缓存满时淘汰最少使用的条目
- ✅ 多层缓存架构:本地缓存(LRU-Cache)+ 远端缓存(Redis/Memcached)
- ✅ 双层缓存机制:查询结果缓存 + Bookmark 分页缓存
- ✅ 手动失效:通过
invalidate()方法清理指定集合的缓存 - ✅ 统计监控:命中率、淘汰统计、内存占用
缓存配置
全局缓存配置
在构造函数中配置全局缓存参数:
查询级缓存配置
在具体查询中指定缓存 TTL(毫秒):
重要说明:
- ✅
cache参数的值是毫秒数(TTL) - ✅
cache: 0表示禁用缓存 - ✅ 默认值:未设置时不使用缓存
- ❌ 不支持
cache: true和单独的ttl参数
缓存键生成
缓存键由以下部分组成:
- 数据库名
- 集合名
- 查询条件(stringify)
- 投影(stringify)
- 排序(stringify)
- limit/skip
相同查询的不同参数会生成不同的缓存键:
TTL(生存时间)过期
自动过期
缓存条目在 TTL 到期后自动失效:
TTL 最佳实践
LRU(最近最少使用)淘汰
淘汰机制
当缓存条目数达到 maxEntries 时,自动淘汰最久未访问的条目。
LRU 访问顺序
多层缓存
monSQLize 提供两种多层缓存机制:
1. 本地 + 远端缓存架构(MultiLevelCache)
支持本地内存缓存(LRU-Cache)+ 远端缓存(Redis/Memcached)的两层架构,实现更高的缓存命中率和更大的缓存容量。
CacheLike 接口规范
要作为 remote 使用,缓存实例必须实现以下 10 个方法(CacheLike 接口):
验证建议:
- 优先直接使用
MonSQLize.createRedisCacheAdapter(),避免手写CacheLike适配器 - 若必须自定义 remote cache,请逐项确保实现了上表列出的 10 个方法
- 当前不再提供旧包装层里的
MemoryCache.isValidCache()校验工具
缓存策略
读操作:
- 优先从本地缓存读取(内存,速度快)
- 本地未命中则查询远端缓存(网络,速度较慢)
- 远端命中则异步回填到本地缓存(可配置)
- 远端失败则优雅降级(返回 undefined)
写操作:
both(默认):本地 + 远端双写,保证一致性local-first-async-remote:本地优先,远端异步写入,提升性能
删除操作:
- 删除本地缓存(立即生效)
- 删除远端缓存(尽力而为)
delPattern支持可选的集群广播机制
配置示例
方式 1:只使用远程 Redis 缓存(无本地缓存)
如果不需要本地内存缓存,可以直接传入 Redis 适配器作为缓存实例:
适用场景:
- 多实例部署,需要共享缓存
- 服务器内存受限,不适合本地缓存
- 缓存数据量较大(百万级)
- 需要持久化缓存(Redis 持久化)
性能特点:
- 读取延迟:1-2ms(网络 + Redis 查询)
- 缓存容量:取决于 Redis 内存(可达 GB 级)
- 缓存一致性:跨实例共享,强一致性
方式 2:本地 + 远程双层缓存(推荐高性能场景)
使用 multiLevel: true 启用本地内存 + Redis 双层架构:
适用场景:
- 高并发读取场景
- 热点数据频繁访问
- 需要极致性能(本地缓存 0.001ms)
- 多实例部署 + 需要缓存一致性
性能特点:
- 本地缓存命中:0.001ms(内存读取)
- Redis 缓存命中:1-2ms(网络 + Redis)
- 数据库查询:10ms+
方式 3:使用已创建的 Redis 实例
方式 4:手动封装 Redis(适用于自定义需求)
策略配置
性能对比
三种缓存策略对比:
场景选择建议:
性能提升示例:
最佳实践
-
本地缓存配置
- 设置合理的
maxEntries(推荐 1-10 万条) - 热点数据优先存入本地
- 设置合理的
-
远端缓存配置
- Redis 连接池配置(避免连接耗尽)
- 设置合理的超时时间(推荐 50-100ms)
- 监控 Redis 内存使用
-
写策略选择
- 强一致性场景:使用
both(默认) - 高并发写入:使用
local-first-async-remote
- 强一致性场景:使用
-
故障降级
- 远端缓存故障自动降级到本地缓存
- 不影响业务正常运行
2. 查询结果 + Bookmark 双层缓存
Bookmark 分页缓存
缓存失效机制
🆕 精准失效(v1.1.6+)
精准失效只清除真正受影响的缓存,而不是整个集合的缓存。
配置方式
方式1: 实例级别全局配置(推荐)
所有写操作默认启用精准失效:
方式2: 写操作级别配置(覆盖实例配置)
单次操作控制是否启用精准失效:
配置优先级: 写操作配置 > 实例配置
⚠️ 重要说明:
autoInvalidate选项只用于写操作(insert/update/delete)- 查询操作(find/findOne)不支持
autoInvalidate选项 - 查询只需要使用
cache选项指定缓存时间
精准失效示例
支持的查询条件
精准失效支持简单查询条件:
✅ 支持的操作符:
- 相等匹配:
{ status: 'active' } $eq:{ status: { $eq: 'active' } }$ne:{ status: { $ne: 'deleted' } }$gt,$gte,$lt,$lte:{ price: { $gte: 100 } }$in:{ category: { $in: ['a', 'b'] } }$nin:{ status: { $nin: ['deleted'] } }
❌ 不支持的操作符(自动跳过,按 TTL 过期):
$regex,$exists,$type$elemMatch,$size,$all$where
ObjectId 字段支持
精准失效完全支持 ObjectId 字段(包括 _id):
手动清理
使用 clearBookmarks() 手动清理 Bookmark 缓存:
统计监控
获取缓存统计
统计指标说明
监控与告警
性能基准
缓存加速效果
以下是 monSQLize 内置的性能基准测试结果:
find 查询缓存
findPage 查询缓存
distinct 查询缓存
结论:缓存命中可以提供 200-300x 的性能提升。
最佳实践
1. 根据数据特征选择 TTL
2. 合理设置 maxEntries
3. 监控缓存健康度
4. 批量预热缓存
5. 缓存穿透保护
常见问题
Q: 缓存会占用多少内存?
A: 每个缓存条目包含查询键(约 100-200 字节)和查询结果(取决于数据大小)。
估算公式:
Q: 如何选择合适的 maxEntries?
A: 根据服务器内存和查询热点数据量选择:
建议:
- 从默认值 100000 开始
- 监控淘汰率(evictionRate)
- 如果 evictionRate > 10%,增加 maxEntries
Q: 如何手动清理缓存?
A: monSQLize 已支持写操作;但在以下场景里仍然建议手动清理缓存:
注意:
- 当使用外部工具修改数据后,仍需手动调用
invalidate()清理缓存 - 对于通过 monSQLize 发起的写操作,可结合
autoInvalidate使用自动失效 - 若你的缓存策略跨进程/跨节点,建议同时结合分布式失效广播
Q: 如何禁用缓存?
A: 有三种方式:
Q: 缓存和 Bookmark 有什么区别?
A:
- 缓存(find/findOne/aggregate/distinct):缓存查询结果(完整的文档列表)
- Bookmark(findPage):缓存分页游标(仅存储每 N 页的起始位置)
缓存失效 API
invalidate()
手动清除指定集合的所有缓存。适用于需要立即刷新缓存的场景。
方法签名
参数说明
无参数。清除当前绑定集合的所有查询缓存。
返回值
返回 Promise<void>。
使用场景
1. 外部工具修改数据后刷新缓存
2. 定时刷新缓存
3. 多集合缓存清除
使用说明
重要提示:monSQLize 当前版本已经支持 insertOne / updateOne / deleteOne 等写操作;通过 monSQLize 写入时,相关缓存会按当前失效策略处理。invalidate() 仍然用于以下场景:
- 外部写入后的显式清理:
- 使用 MongoDB Shell、Compass 或其他应用直接修改数据后
- 旁路批量导入、迁移脚本或手工修复数据后
- 主动刷新策略:
- 定时刷新热点集合缓存
- 临时强制清理某个集合的查询缓存
- 自定义缓存边界:
- 使用自定义 cache adapter 或业务侧额外缓存时,需要自行决定清理范围
当前最佳实践:
- 业务写入优先通过 monSQLize 执行,避免绕过缓存失效链路
- 外部工具或旁路脚本修改数据后,立即调用
invalidate() - 定期监控缓存命中率,决定是否需要定时刷新
- 避免过度使用,仅在必要时清除缓存
最佳实践
1. 避免过度使用
2. 结合缓存监控
3. 批量清除时使用并行
4. 定时刷新的错误处理
注意事项
- 清除范围:
invalidate()只清除指定集合的查询缓存,不影响其他集合 - 性能影响:清除缓存后,下次查询需要访问数据库,会有性能损耗
- 不清除 Bookmarks:
invalidate()不清除 findPage 的 Bookmark 缓存,需要使用clearBookmarks() - 旁路写入限制:通过外部工具或其他服务绕过 monSQLize 写入时,必须手动调用
invalidate()或执行等效的业务清理动作
相关方法
clearBookmarks(collectionName?)- 清除 findPage 的 Bookmark 缓存(参见 Bookmarks 文档)getCache()- 获取缓存实例,可调用clear()清除所有缓存(参见 工具方法文档)