完全不可容忍缓存未命中时如何设计与数据库强一致的零回源缓存系统
全量一致无回源缓存系统设计方案
核心设计逻辑
你需要的本质是缓存作为关系型数据库的全量可信副本,彻底废弃缓存miss回源逻辑:缓存存在key等价于数据库存在对应记录,缓存不存在key等价于数据库无对应记录。核心约束只有一条:绝对不允许出现"数据库有记录、缓存无对应key"的漏判,至于"缓存有多余key"的误判,可以通过定期对账清理,不影响核心正确性。
落地架构步骤
- 全量初始化阶段
不要用业务接口逐行扫表写缓存,直接基于数据库一致性快照做初始加载:以MySQL为例,取带明确GTID点位的一致性快照,将所有需要查询的业务key及关联字段全量写入缓存。加载完成后先做数量对账:快照内统计的有效key总数、缓存内实际写入的key总数完全匹配后,才将查询流量切到缓存;初始化过程中所有查询暂时走数据库兜底,不对外提供缓存判定服务。 - 增量同步阶段
完全放弃业务层双写逻辑,通过数据库变更流做顺序同步:从全量快照对应的GTID/WAL点位开始,消费binlog(MySQL)或WAL日志(PostgreSQL等其他关系库),按日志顺序更新缓存:- 行插入/更新事件:将对应key和字段写入缓存
- 行删除事件:将对应key从缓存中删除
同步过程采用手动位点提交机制:只有缓存写入/删除操作明确返回成功,才提交对应消费位点,进程崩溃重启后自动从上一个成功提交的位点重新消费,写入逻辑做幂等处理,避免重复消费导致数据异常。所有缓存key不设置TTL,生命周期完全和数据库行绑定,禁止key自动过期导致的误判。
- 对账校验阶段
定期做两层对账:一是增量对账,每消费1000条变更事件就随机抽1%的key比对缓存和数据库的值是否一致;二是全量对账,低峰期按分片比对全量key的存在性,一旦发现不一致立刻暂停缓存服务,切回数据库兜底排查问题,避免错误扩散。
工具选型建议
很多人认为Redis无法满足强一致要求,本质问题不在Redis本身,而在常规用法的同步链路没有一致性保障,只要严格按照上述CDC同步+位点提交+对账的逻辑实现,Redis完全可用,毕竟你的有效数据只占总查询量的5%,内存成本完全可控。
如果不想自己实现同步校验逻辑,可以选以下两类工具:
- 内存容量足够承载全量有效数据时:可以直接用带持久化的Redis部署形态,搭配Debezium、Canal这类成熟CDC组件做同步,不需要额外替换存储组件。
- 有效数据规模过大、内存成本过高时:选基于RocksDB的持久化KV存储,比如嵌入式的Pebble、RocksDB,或者分布式强一致KV存储TiKV,这类存储将数据存在磁盘,成本更低,同步逻辑和上述方案一致。
- 可选优化层:可以在缓存前加一层布隆过滤器,全量初始化时把所有有效key写入过滤器,查询先过布隆过滤器:过滤器判定不存在的key直接返回空结果,判定存在的再查缓存。布隆过滤器不存在假阴性(不会把存在的key判定为不存在),完全不违反零漏查要求,还能挡住90%以上的空查询,进一步降低缓存层的负载。注意布隆过滤器不能替代缓存做最终判定,其假阳性问题会导致少量空查询打到缓存层,不影响最终结果正确性。
相关技术参考关键词
你可以沿着以下方向查找落地实践细节:
- 缓存模式:
Full Copy Cache(全量副本缓存)、Write Through Cache(写穿透缓存),区别于常规依赖回源的Lookaside Cache(旁挂缓存) - 数据同步:
CDC(Change Data Capture,变更数据捕获)、Binlog 位点对齐 - 一致性保障:
快照一致性加载、缓存-数据库周期性对账 - 空值拦截优化:
Bloom Filter(布隆过滤器)前置拦截
内容的提问来源于stack exchange,提问作者wanaryytel
相关产品推荐
相关产品推荐

