在高并发读请求的场景下,为了减少对数据库的直接请求次数,通常会引入缓存系统如Redis来存储频繁访问的数据。
由于数据库和缓存可能被独立更新,这导致两者间可能出现数据不一致的情况。
为了确保缓存与数据库之间的一致性,需要采用一定的策略来管理这两种存储之间的交互。
缓存双删
缓存双删是指在更新数据库数据时,先删除缓存中的相关数据,然后再更新数据库,最后再次删除缓存中的数据。
缓存双删的具体步骤
- 第一次删除缓存: 在更新数据库之前,先删除缓存中的数据。这样做的目的是为了避免在更新数据库的过程中,其他请求读取到旧的数据。
- 更新数据库: 执行数据库更新操作。
- 第二次删除缓存(延迟删除): 在数据库更新操作完成后,延迟一段时间再次删除缓存。这里的延迟时间可以根据实际业务需求和数据读取频率来设定。延迟删除的目的是为了解决在第一次删除缓存后,其他线程可能已经从数据库中读取了旧数据并写回缓存的问题。
缓存双删存在的问题
- 延时设置难以精确:延时时长需要根据业务具体情况来设定,但往往难以精确控制最佳延时时长。
- 依然存在数据不一致风险:即使是延时双删,也不能完全杜绝数据不一致的情况,特别是在高并发场景下。因为在延迟删除期间,如果其他请求读取数据,可能会读取到旧数据。
- 引入分布式锁或异步消息队列:为了解决上述问题,可以通过引入分布式锁或者利用异步消息队列(如MQ、Canal等)来进一步确保缓存删除和数据库更新的顺序性和一致性。
缓存双删与其他策略的比较
- 同步删除 VS 延时双删:同步删除是指更新数据库后立即删除缓存,这种方法容易产生脏数据;延时双删则通过延时二次删除减少脏数据的风险。
- 异步监听+可靠消息删除方案:这种方案通过监听数据库变更日志,然后异步删除对应的缓存项,是大厂常用的方法,但需要较高的系统复杂度。
基于binlog的异步更新策略
基于binlog的异步更新策略是一种通过监听数据库的binlog记录,实现缓存与数据库之间数据一致性的方法。这种方法在现代分布式系统中被广泛应用,以确保在高并发和大数据量场景下的数据同步和一致性。
binlog的基础概念及作用
- 定义:binlog是MySQL等数据库用来记录所有数据表变更的日志文件。
- 作用
- 数据恢复:通过binlog可以恢复到任何时间点的数据状态。
- 主从复制:通过同步binlog,可以实现数据库的主从复制,提高数据的可用性和负载均衡。
- 数据一致性:通过监听binlog,可以实现缓存与数据库之间的数据一致性。
实现
- 整体架构:主要包括MySQL数据库、Canal中间件(用于解析binlog)、消息队列(如Kafka)以及缓存系统(如Redis)。
- 流程
- 配置MySQL开启binlog:配置MySQL的配置文件my.cnf,设置server_id、log-bin、binlog_format等参数。
- 部署Canal中间件:Canal模拟MySQL slave协议,向MySQL master请求binlog,解析后发送到消息队列。
- 消息队列转发:将解析后的binlog数据发送到消息队列,再由消费者服务进行消费。
- 更新缓存:消费者服务从消息队列中获取数据变更信息,根据这些信息更新缓存。
实际应用示例
- 订单处理与库存同步
- 订单服务在创建或更新订单时,会生成相应的binlog记录。
- Canal中间件实时监听并解析这些binlog记录,然后将解析后的数据发送到Kafka。
- 库存服务订阅Kafka中的相关数据,并根据这些数据更新本地缓存(如Redis),确保订单和库存数据的一致性。
优点
- 实时性:通过实时监听binlog,可以确保数据库的每次更新都能即时反映到缓存中。
- 降低数据库负担:通过异步更新缓存,减少了对数据库的直接访问压力。
- 解决分布式事务问题:利用数据库自身的XA事务或ACK确认机制,保证数据的最终一致性。
- 可靠性:即使在更新过程中出现故障,也可以通过binlog的重放机制恢复数据。
缺点
- 增加系统复杂度:引入了Canal和消息队列等中间件,增加了系统的复杂度和运维负担。
- 数据延迟:在极端情况下,可能会出现短暂的数据不一致情况,直到下一次binlog解析和缓存更新。
- 资源消耗:需要额外的资源来存储和处理binlog数据,尤其是在高并发和大数据量的情况下。
参考链接
https://cloud.tencent.com/developer/article/1932934
https://blog.csdn.net/java_2017_csdn/article/details/134329288