Redis与数据库双写一致性问题

Redis与数据库双写一致性问题

什么是双写一致性问题

所谓的双写一致性是当修改数据库的数据也要同时更新缓存数据,数据库和缓存要保持一致。

因为写和读是并发操作,没法保证顺序,就会出现缓存和数据库的数据不一致的问题。

讨论这个问题的前提是明确业务场景。如果业务一致性要求比较高,需要采用的是一种方案,如果业务允许延迟一致,则又是另一种方案。

业务一致性要求比较高

先删除缓存还是先操作数据库?

两种方式都会有问题。

先删除缓存再操作数据库

该方案会导致不一致的原因是。同时有一个请求A进行更新操作,另一个请求B进行查询操作。那么会出现如下情形:

(1)、请求A进行写操作,删除缓存

(2)、请求B查询发现缓存不存在

(3)、请求B去数据库查询得到旧值

(4)、请求B将旧值写入缓存

(5)、请求A将新值写入数据库 上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。

先更新数据库,再删除缓存

假设这会有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生

(1)请求A查询数据库,得一个旧值

(2)请求B将新值写入数据库

(3)请求B删除缓存

(4)请求A将查到的旧值写入缓存,如果发生上述情况,确实是会发生脏数据。

但发生这种情况的概率又有多少呢?

发生上述情况有一个先天性条件,就是步骤(2)的写数据库操作比步骤(1)的读数据库操作耗时更短,才有可能使得步骤(3)先于步骤(4)。

可是,数据库的读操作的速度远快于写操作的(不然做读写分离干嘛,做读写分离的意义就是因为读操作比较快,耗资源少),因此步骤(2)耗时比步骤(1)更短,这一情形很难出现。

延迟双删

在写数据时,先删除缓存,再修改数据库,然后延迟一小会儿,再删除缓存。

为什么要删除两次,是为了避免脏数据的出现。

为什么要延迟双删,是因为数据库一般是读写分离主从模式,我们需要延迟一会儿把主数据库的数据同步到从数据库。但是延时的时间不太好控制,因此延迟双删仍然有脏数据的风险。

强一致性实现-读写锁

一般写入缓存中的数据都是读多写少。

共享锁:读锁readlock,加锁后,其他线程可以共享读数据

排他锁:独占锁writelock,加锁后,阻塞其他线程读写操作

redisson已经实现了读写锁。在读的时候添加共享锁,可以保证读读不互斥,读写互斥。当我们更新数据时,添加排他锁,它是读写和读读都互斥,这样就能保证在写数据的同时是不会让其他线程读数据的,避免了脏数据。这里需要注意的是读方法和写方法需要使用同一把锁才行。

业务一致性要求没那么高

异步通知保证数据最终一致

修改数据写入数据库时,发一条消息到MQ,缓存服务监听MQ消息,最终去更新缓存。它能保证最终的一致性。它的可靠性主要是基于MQ的可靠性来保证。

Canal的异步通知

修改数据写入数据库后,数据库会把变化记录到binlog二进制文件中,Canal监听mysql的binlog,将数据变化通知到缓存进行更新。Canal实际上是伪装成mysql的一个从节点。

这种方式的优点是几乎没有代码的侵入。

你可能感兴趣的:(数据库,redis,mybatis)