二级缓存
二级缓存(L2)扩展了一级内存缓存的能力,通过 Redis 实现跨实例的分布式缓存。启用 L2 后,多个数据墙DBW 实例可以共享缓存结果,容器重启后仍可从 Redis 恢复缓存,避免冷启动导致的数据库压力。
IMPORTANT
L2 需要在全局配置中显式启用 Redis 连接。如果全局未启用 L2,即使实体配置了 "level": "L1L2",实际行为也会退化为纯 L1。
全局启用 L2
在 runtime.cache 中配置二级缓存连接:
{
"runtime": {
"cache": {
"enabled": true,
"ttl-seconds": 30,
"level-2": {
"enabled": true,
"provider": "redis",
"connection-string": "localhost:6379",
"partition": "prod-api"
}
}
}
}| 字段 | 必需 | 说明 |
|---|---|---|
level-2.enabled | 是 | 启用二级缓存 |
level-2.provider | 是 | 当前仅支持 "redis" |
level-2.connection-string | 是 | Redis 实例的连接地址 |
level-2.partition | 否 | 缓存命名空间分区,隔离不同环境或应用 |
L1L2 请求流程
当实体配置为 "level": "L1L2" 时,一次查询请求的缓存查找路径为:
请求 → 检查 L1(进程内存)
├── L1 命中 → 直接返回
└── L1 未命中 → 检查 L2(Redis)
├── L2 命中 → 回写 L1 → 返回
└── L2 未命中 → 查询数据库
├── 回写 L2 → 回写 L1 → 返回这个三层查找策略的意义:
- L1 命中:进程内内存读取,延迟极低(< 1ms),适用于同一实例的重复请求。
- L2 命中:Redis 网络读取,延迟较低(< 5ms),适用于不同实例的首次请求或重启后恢复。
- 数据库查询:仅在前两级都未命中时发生,触发后将结果回填到 L2 和 L1。
实际场景举例
场景:三个数据墙DBW 实例通过负载均衡对外服务,L2 已启用。
- 实例 A 收到
GET /api/Product?$first=20,L1 和 L2 都未命中,查询数据库,结果回写 L2 → L1。耗时 50ms。 - 实例 A 再次收到相同请求,L1 命中。耗时 < 1ms。
- 实例 B 收到相同请求,L1 未命中,L2 命中,回写实例 B 的 L1。耗时 < 5ms。
- 实例 A 重启,L1 清空。收到请求后 L1 未命中,L2 命中,回写 L1。不会触发数据库查询。
Redis 配置
基本连接
{
"level-2": {
"provider": "redis",
"connection-string": "localhost:6379"
}
}带密码认证
{
"level-2": {
"provider": "redis",
"connection-string": "localhost:6379,password=your_redis_password"
}
}或使用 @env() 避免在配置文件中写入连接信息:
{
"connection-string": "@env('REDIS_CONN_STR')"
}NOTE
Redis 连接字符串使用 StackExchange.Redis 标准格式,常见参数包括 password、ssl、abortConnect 等。完整格式参考:host:port,password=xxx,ssl=true,abortConnect=false。
Redis 生产部署建议
| 建议 | 说明 |
|---|---|
| 使用独立 Redis 实例 | 不要与业务 Redis 混用,避免键冲突和资源争抢 |
| 启用持久化(RDB/AOF) | Redis 重启后可以恢复缓存,减少数据库冷启动压力 |
| 配置内存上限 | 设置 maxmemory 和淘汰策略(建议 allkeys-lru) |
| 监控内存使用 | 跟踪 Redis 内存使用量,确保不超过实例容量 |
| 高可用 | 生产环境建议使用 Redis Sentinel 或 Cluster |
分区(Partition)
partition 参数用于在同一个 Redis 实例中隔离不同环境或应用的缓存空间:
{
"level-2": {
"partition": "prod-api"
}
}分区作为 Redis 键前缀生效。只有配置了相同 partition 值的数据墙DBW 实例才会共享缓存。典型用途:
| 场景 | 分区值 |
|---|---|
| 生产环境和测试环境用同一 Redis | "prod-api" / "test-api" |
| 多租户 SaaS,每个租户的数据墙DBW 实例 | "tenant-a" / "tenant-b" |
| 同一应用的不同微服务 | "service-orders" / "service-products" |
L1 vs L1L2 选择
| 维度 | 纯 L1 | L1L2 |
|---|---|---|
| 部署复杂度 | 最低,无外部依赖 | 需要 Redis 实例 |
| 读取延迟 | < 1ms | L1 命中 < 1ms,L2 命中 < 5ms |
| 多实例共享 | 不支持 | 支持 |
| 重启恢复 | 不支持,冷启动 | 支持,L2 命中可避免数据库查询 |
| 适合场景 | 单实例、低流量 | 多实例、水平扩展、频繁部署 |
缓存失效与 L2
L2 的失效机制与 L1 相同——当实体发生写操作时,该实体在 L1 和 L2 中的所有缓存条目同时失效。数据墙DBW 通过 Redis 的 Pub/Sub 机制在实例间同步失效事件:
- 实例 A 收到
POST /api/Book,执行写入。 - 实例 A 清除自己的 L1 缓存,并向 Redis 发布失效消息。
- 实例 B、C 通过 Redis Pub/Sub 接收到失效消息,清除各自的 L1 缓存。
- L2 中的该实体缓存条目被实例 A 清除。
这个机制确保在多实例场景下,一个实例的写入能及时让所有实例的缓存失效,避免返回过时数据。
L2 降级
如果 Redis 不可用(网络故障、Redis 宕机),数据墙DBW 不会拒绝请求——L2 未命中时直接查询数据库,但不回写 L2。服务继续正常运行,退化为纯 L1 行为。Redis 恢复后,L2 功能自动恢复。
