缓存一致性解决方案——改数据时如何保证缓存和数据库中数据的一致性

文章目录

  • 写在前面
  • 缓存数据一致性的方式
    • 双写模式
      • 弊端
    • 失效模式
      • 弊端
    • 分析
  • 总结
  • 我们通常用的方式
  • 拓展:使用canal订阅binlog

写在前面

我们都知道,缓存是为了提高数据的读取速度的,应对的场景是读多写少的场景。

读数据我们通常先读缓存,如果缓存没有再读数据库然后更新缓存。
从查询数据库性能优化谈到redis缓存-谈一谈缓存的穿透、雪崩、击穿

当缓存的数据需要修改的时候,既要修改缓存,又要修改数据库,如何保证缓存和数据库中的数据是一致的呢?

缓存数据一致性的方式

双写模式

双写模式就是先写数据库,再写缓存:
缓存一致性解决方案——改数据时如何保证缓存和数据库中数据的一致性_第1张图片

弊端

高并发下,由于卡顿等原因,导致写缓存2在最前,写缓存1在后面,就导致了缓存与数据库数据不一致的情况,也就是出现了脏数据。

但是,这种脏数据只是暂时的,取决于缓存的过期时间,当缓存过期以后,再次读取数据库就又会得到最新的正确数据。也就是说,最新的数据是有可能会有一定的延迟,但是最终结果是一致的。

缓存一致性解决方案——改数据时如何保证缓存和数据库中数据的一致性_第2张图片

失效模式

缓存一致性解决方案——改数据时如何保证缓存和数据库中数据的一致性_第3张图片

弊端

高并发读写情况下,写db2的删缓存执行在了读db1的更新缓存的前面,就会造成缓存与数据库的不一致,也就是出现了脏数据。

但是,这种脏数据只是暂时的,取决于缓存的过期时间,当缓存过期以后,再次读取数据库就又会得到最新的正确数据。也就是说,最新的数据是有可能会有一定的延迟,但是最终结果是一致的。

缓存一致性解决方案——改数据时如何保证缓存和数据库中数据的一致性_第4张图片

分析

无论是双写模式还是失效模式,都会导致缓存的不一致问题。即多个实例(数据库、缓存)同时更新会造成脏数据。怎么办?

1、如果是用户维度数据(订单数据、用户数据),这种并发几率非常小(用户点击的操作并不会很快),不用考虑这个问题,即使有脏数据,缓存数据加上过期时间,每隔一段时间触发读的主动更新即可。
2、如果是菜单,商品介绍等基础数据,如果对实时性要求比较高,也可以去使用canal订阅binlog的方式。
3、缓存数据+过期时间也足够解决大部分业务对于缓存的要求(业务不关心脏数据,允许临时脏数据可忽略)。
4、通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。
5、加锁会导致系统变笨重,所以能不加还是不加,但是对数据一致性要求很高的,那也只能加上锁了。

总结

1、我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保证每天或者一段时间后拿到当前最新数据即可。
2、我们不应该过度设计,增加系统的复杂性,对于分布式锁,能不加就不加;对于缓存的脏数据,能容忍一段时间的脏数据那就不需要处理,否则系统过重维护起来会很麻烦。
3、遇到实时性、一致性要求高的数据,并且写场景比较多的话,就应该查数据库,即使慢点。
4、读多写少的场景下,又对数据一致性要求比较高的话,可以通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁
5、使用canal订阅binlog的方式可以解决数据一致性问题,但是又引入了新的中间件。

我们通常用的方式

1、缓存的所有数据都加过期时间,数据过期下一次查询触发主动更新。
2、读写数据的时候,加上分布式的读写锁。

拓展:使用canal订阅binlog

binlog是mysql的二进制日志,对于操作数据库的语句,都以此形式保存。Canal是阿里MySQL数据库Binlog的增量订阅&消费组件 。基于数据库Binlog可以监控数据库数据的变化进而用于数据同步等业务。

缓存一致性解决方案——改数据时如何保证缓存和数据库中数据的一致性_第5张图片
简而言之,就是订阅数据库的binlog,如果数据库有发生修改,就获取到这个修改事件同时去修改缓存,保证缓存一致性。

该方式引入了新的中间件,使系统更加复杂化。但是完美确保了缓存一致性,通常只需要维护一次,后续不需要单独维护。

你可能感兴趣的:(架构-理论,缓存,数据库)