4. 缓存技术选型与设计:本地缓存与分布式缓存的对比及淘汰策略

在风控系统中,缓存是降低数据库压力、提升规则计算速度的核心手段。本章聚焦于缓存技术的选型与设计,从本地缓存(Caffeine/Guava)与分布式缓存(Redis/Memcached)的对比出发,深入分析淘汰策略(LRU/LFU/TTL)及数据结构设计,为后续的异步任务编排提供高性能、高可用的缓存底座。

4.1 本地缓存 vs 分布式缓存:核心对比

风控场景中,本地缓存适用于单节点高频访问的静态数据(如规则元数据、黑白名单),而分布式缓存适用于跨节点共享的实时数据(如用户画像、设备指纹)。以下从多个维度进行对比:

对比维度 本地缓存 (Caffeine/Guava) 分布式缓存 (Redis/Memcached)
数据一致性 单机强一致,无需网络同步 最终一致,依赖网络协议
访问延迟 纳秒级(堆内访问) 微秒级(网络 I/O)
容量上限 受限于 JVM 堆内存 可水平扩展至 TB 级
持久化 不支持(重启即丢失) 支持 RDB/AOF 持久化
典型场景 规则引擎元数据、频率计数器 用户会话、实时黑名单、设备指纹
淘汰策略 LRU/LFU/TTL 组合 LRU/LFU/TTL/随机淘汰
选型建议: 风控系统中,建议采用“本地缓存 + 分布式缓存”两级架构。本地缓存用于高频读取的静态数据(如规则配置),分布式缓存用于跨节点共享的实时数据(如用户风险等级)。两级缓存可显著降低分布式缓存的 QPS 压力。

4.2 缓存淘汰策略:LRU、LFU 与 TTL

淘汰策略决定了缓存空间不足时如何驱逐旧数据。风控场景中,不同数据对时效性和访问频率的要求不同,需选择合适的策略。

  • LRU(Least Recently Used):淘汰最近最少使用的数据。适用于访问模式有明显时间局部性的场景,如用户会话缓存。
  • LFU(Least Frequently Used):淘汰访问频率最低的数据。适用于热点数据相对稳定的场景,如规则命中统计。
  • TTL(Time To Live):基于过期时间淘汰。适用于对时效性要求严格的数据,如验证码、临时令牌。
实践要点: Caffeine 默认采用 W-TinyLFU 算法,结合 LRU 与 LFU 的优点,能有效应对突发流量和冷热数据切换。Redis 支持 volatile-lru、allkeys-lru、volatile-lfu、allkeys-lfu 等多种策略,建议风控系统使用 volatile-lru 或 volatile-lfu,仅对设置了 TTL 的 key 进行淘汰。
缓存淘汰策略对比图 缓存淘汰策略对比 LRU 最近最少使用 LFU 最不经常使用 TTL 过期时间淘汰 适用场景与注意事项 • LRU:适合时间局部性强的数据,如用户会话;但可能淘汰高频但偶发访问的数据。 • LFU:适合热点数据稳定的场景,如规则命中统计;但需处理频率衰减问题。 • TTL:适合强时效性数据,如验证码;需合理设置过期时间,避免缓存雪崩。

4.3 缓存数据结构设计

风控系统中,缓存的数据结构直接影响查询效率和内存占用。以下列出常见的数据结构及其适用场景:

数据结构 存储内容 典型风控场景 优势
String 单个值(JSON/序列化对象) 用户风险等级、设备指纹 简单、高效,支持原子操作
Hash 字段-值映射 用户画像(多属性聚合) 节省内存,支持部分更新
List 有序列表 事件队列、操作日志 支持双向操作,适合 FIFO
Set 无序集合(去重) 黑名单、白名单 支持交集/并集运算
Sorted Set 有序集合(带分数) 排行榜、频率计数器 支持范围查询和排序
设计陷阱: 避免将大对象(如完整用户画像)直接存入 String 类型,会导致内存膨胀和序列化开销。建议使用 Hash 结构按字段存储,或拆分为多个小 key。对于黑名单等集合数据,优先使用 Set 而非 List,以利用去重和集合运算能力。

4.4 两级缓存架构设计

风控系统推荐采用“本地缓存(L1)+ 分布式缓存(L2)”的两级架构,兼顾性能与一致性。本地缓存使用 Caffeine,分布式缓存使用 Redis。

  • L1 本地缓存(Caffeine):存储规则元数据、静态配置、频率计数器。容量控制在 100MB 以内,淘汰策略使用 W-TinyLFU + TTL。
  • L2 分布式缓存(Redis):存储用户会话、实时黑名单、设备指纹、风险等级。容量按需扩展,淘汰策略使用 volatile-lru。
  • 一致性策略:本地缓存设置较短的 TTL(如 5 秒),分布式缓存更新时通过消息队列广播失效通知。
两级缓存架构图 两级缓存架构设计 风控应用节点 L1 本地缓存 Caffeine (W-TinyLFU) L2 分布式缓存 Redis (volatile-lru) 数据库 说明:应用节点优先查询 L1 缓存,未命中则查询 L2 缓存,L2 未命中则回源数据库并回填两级缓存。

4.5 缓存设计最佳实践

  • Key 设计规范:使用业务前缀 + 冒号分隔,如 risk:user:12345:level,便于管理和监控。
  • 内存预估:根据数据量和 TTL 估算内存占用,预留 20% 的 buffer 空间。
  • 缓存穿透防护:对不存在的数据也缓存空值(设置短 TTL),或使用布隆过滤器。
  • 缓存雪崩防护:设置随机 TTL 偏移量,避免大量 key 同时过期。
  • 监控告警:监控缓存命中率、内存使用率、淘汰次数,低于阈值时触发告警。
代码示例:Caffeine 本地缓存配置
Cache<String, UserRiskLevel> riskCache = Caffeine.newBuilder()
    .maximumSize(10_000)               // 最大条目数
    .expireAfterWrite(5, TimeUnit.SECONDS) // 写入后 5 秒过期
    .recordStats()                     // 开启统计
    .build(key -> loadFromRedis(key)); // 缓存未命中时回源