Skip to content

Setting 缓存架构

Setting 模块采用三层分布式缓存架构,遵循 DDD 依赖倒置原则,实现了高效的多层缓存系统。

Table of Contents

架构概览

三层架构设计

graph TB
    subgraph Application["Application 层接口"]
        SC[SettingsCacheService
缓存 API 响应 DTO] SSC[SettingCacheService] SCC[SettingCategoryCacheService] USC[UserSettingCacheService] USQC[UserSettingQueryCacheService] end subgraph Infrastructure["Infrastructure 层"] IMPL[Redis 实现] WARM[预热服务] end SC -.实现.-> IMPL SSC -.实现.-> IMPL SCC -.实现.-> IMPL USC -.实现.-> IMPL USQC -.实现.-> IMPL IMPL --> WARM style Application fill:#fff4e1 style Infrastructure fill:#e1ffe1

依赖方向: Application → Infrastructure

各层职责

位置缓存接口缓存内容
Applicationapplication/setting/SettingCacheService 等Domain 实体 + App DTO
Infrastructureinfrastructure/cache/Redis 实现 + 预热服务-

架构原则:所有缓存接口统一定义在 Application 层,Domain 层不定义缓存接口。

缓存类型

Setting 实体缓存

系统设置定义缓存,支持启动预热和按 Key 查询。

属性
接口SettingCacheService
Key 格式{prefix}setting:{key}
TTL7 天
预热✅ 启动时自动预热

核心方法:

go
// 单条操作
Get(ctx, key) → *setting.Setting
Set(ctx, *setting.Setting)

// 批量操作(用于预热)
GetAll(ctx) → map[string]*setting.Setting
SetAll(ctx, settings)

SettingCategory 缓存

设置分类缓存,支持双索引(ID 和 Key)。

属性
接口SettingCategoryCacheService
Key 格式{prefix}setting_category:id:{id}
{prefix}setting_category:key:{key}
TTL7 天
预热✅ 启动时自动预热

UserSetting 缓存

用户设置采用二层缓存设计:

graph LR
    subgraph Layer1["Repository 层"]
        USQC[UserSettingQueryCache
原始记录] end subgraph Layer2["Application 层"] USC[UserSettingCache
合并后有效值] end DB[(Database)] --> Layer1 Layer1 --> Layer2 Layer2 --> API[API 响应] style Layer1 fill:#e1f5ff style Layer2 fill:#fff4e1
缓存服务存储内容Key 格式TTL
UserSettingQueryCacheService原始 UserSetting{prefix}usersetting:user:{uid}30 分钟
UserSettingCacheService合并后的有效值{prefix}user:{uid}:setting:{key}30 分钟

为什么分层?

  • Repository 层:需要原始 UserSetting 记录,与 Setting 合并计算有效值
  • Application 层:直接使用已合并的 EffectiveUserSetting,包含 UI 元数据

Schema 缓存

缓存 API 响应(SchemaCategoryDTO),避免重复构建。

属性
接口SchemaCacheService
Key 格式{prefix}schema:admin:{categoryKey}
{prefix}schema:user:{userID}:{categoryKey}
TTL30 分钟

特点: 直接序列化 Application DTO,无独立缓存 DTO。

缓存策略

Cache-Aside 模式

所有缓存装饰器采用 Cache-Aside 模式:

sequenceDiagram
    participant Client
    participant Cache
    participant DB

    Client->>Cache: 1. 查询缓存
    alt 缓存命中
        Cache-->>Client: 返回结果
    else 缓存未命中
        Client->>DB: 2. 查询数据库
        DB-->>Client: 返回结果
        Client->>Cache: 3. 同步回写
    end

关键特性:

  • 缓存未命中不返回错误,fallback 到数据库
  • 同步回写(延迟可忽略 < 1ms)
  • 损坏缓存自动清除

预热机制

应用启动时预热 Setting 和 SettingCategory 缓存。

sequenceDiagram
    participant App as 应用实例
    participant Cache as Redis
    participant DB as Database

    App->>DB: 1. FindAll
    DB-->>App: 返回所有数据
    App->>Cache: 2. SetAll
    Note over App,Cache: 预热完成

设计简化:

  • 无分布式锁:多实例同时预热是幂等操作,数据量小,重复写入可接受
  • 无预热标记:每次启动都执行预热,确保缓存数据最新

为什么这样设计?

方案优点缺点
分布式锁避免重复写入代码复杂,锁 TTL 管理困难
直接预热代码简单,幂等性多实例重复写入(数据量小时可忽略)

失败降级: 预热失败不阻塞启动,降级为 Cache-Aside 模式的惰性加载。

失效策略

写操作后同步删除缓存,并级联失效派生层。

graph TD
    A[Setting 更新] --> B[删除 Setting 缓存]
    B --> C[异步删除关联 UserSetting 缓存]
    C --> D[Schema 缓存自动过期]

    E[UserSetting 更新] --> F[删除 UserSettingQuery 缓存]
    F --> G[删除 UserSetting 有效值缓存]
    G --> H[异步删除 User Schema 缓存]

为什么同步删除?

  • Redis 删除延迟可忽略
  • 避免竞态条件,无需版本控制
  • 代码简单,调试容易

装饰器模式

Repository 装饰器

装饰器装饰目标缓存层
setting_cached_query_repositorySetting QuerySettingCacheService
setting_cached_command_repositorySetting Command+ 级联 UserSetting
setting_category_cached_query_repoCategory QuerySettingCategoryCacheService
user_setting_cached_query_repositoryUserSetting QueryUserSettingQueryCacheService
user_setting_cached_command_repositoryUserSetting Cmd多层缓存失效

装饰器结构:

go
type cachedSettingQueryRepository struct {
    delegate setting.QueryRepository   // 被装饰的原始仓储
    cache    cache.SettingCacheService // 缓存服务
}

func NewCachedSettingQueryRepository(
    delegate setting.QueryRepository,
    cacheService cache.SettingCacheService,
) setting.QueryRepository {
    return &cachedSettingQueryRepository{
        delegate: delegate,
        cache:    cacheService,
    }
}

初始化流程

Container 初始化顺序:
1. Infrastructure (DB, Redis)
2. Cache Services Module ⬅️ 创建所有缓存服务
3. Repositories Module   ⬅️ 使用缓存服务装饰仓储
4. Cache Warmup          ⬅️ 预热 Setting/Category 缓存
5. HTTP Handlers
6. Router

入口文件: internal/bootstrap/init_cache.go

Key 命名规范

环境前缀(由 config.Data.RedisKeyPrefix 配置):

环境默认前缀示例
开发环境dev:dev:setting:site_name
生产环境按需配置prod:setting:site_name

各类型 Key 格式:

缓存类型Key 格式
Setting 实体{prefix}setting:{key}
Category (ID){prefix}setting_category:id:{id}
Category (Key){prefix}setting_category:key:{key}
Category (All){prefix}setting_category:all
UserSetting 原始{prefix}usersetting:user:{userID}
UserSetting 有效值{prefix}user:{userID}:setting:{key}
Admin Schema{prefix}schema:admin:{categoryKey}
User Schema{prefix}schema:user:{uid}:{catKey}

调试命令

bash
# 查看所有缓存 key
redis-cli -u $REDIS_URL KEYS 'dev:*'

# 查看 Setting 缓存
redis-cli -u $REDIS_URL JSON.GET 'dev:setting:site_name' '$'

# 查看 Schema 缓存
redis-cli -u $REDIS_URL JSON.GET 'dev:schema:admin:general' '$'

# 清空开发环境缓存
redis-cli -u $REDIS_URL KEYS 'dev:*' | xargs -r redis-cli -u $REDIS_URL DEL

文件位置速查:

类型位置
Setting 缓存接口internal/application/setting/cache.go
Auth 缓存接口internal/application/auth/cache.go
User 缓存接口internal/application/user/cache.go
Redis 实现internal/infrastructure/cache/
预热服务internal/infrastructure/cache/warmup/
装饰器internal/infrastructure/persistence/
Containerinternal/container/cache.go

基于 DDD + CQRS 架构的企业级应用模板