kidlj
2022-08-19 21:09:47 +08:00
Friends don't let friends do dual writes.
更新完数据库紧接着更新缓存或者写入消息队列,这就是双写或者多写,总会有第一步成功了下一步失败的概率(比如网络抖动等原因),这时候就会造成数据不一致。
我个人的项目实践了一种事件驱动的异步架构,也就是 CDC ( change data capture )架构,选型上使用的是 Debezium + Kafka connector ,当然 Java 生态的也可以用 Canal 替换 Debezium 。简单来说 Debezium 的工作就是监听数据库的写入变更( pg 的 wal log 或 mysql 的 bin log ),为每条变更记录生成一条 Kafka message (包含变更记录的主键 id 等其他字段信息),通过 Kafka connector 自动写入到一个数据库表对应的 Kafka Topic 。采用这种架构处理缓存过期就很简单了。业务端只要更新数据库就可以,(避免了双写的问题),更新缓存的逻辑起一个线程或 goroutine 监听这个表对应的 Kafka topic ,拿到消息以后解析出主键 id ,然后 purge 掉对应记录的缓存。如果消费了这条消息以后 purge 缓存失败怎么办?有这种可能的。Kafka 的 message 有 commit 机制,purge 失败可以一直重试,当成功了以后再 commit 消息。虽然这种架构是异步的,不过得益于 Kafka 的良好吞吐性能,几乎可以做到 real-time 的使用体验。