MySQL和redis数据一致性问题

保持数据库和redis的数据一致性大致有两种方案:
一:先删除缓存,再更新数据库

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

  • 1)请求A进行写操作,删除缓存
  • 2)请求B查询发现缓存不存在
  • 3)请求B去数据库查询得到旧值
  • 4)请求B将旧值写入缓存
  • 5)请求A将新值写入数据库

上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。

解决方案:采用延时双删策略

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

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

  • 1)缓存刚好失效
  • 2)请求A查询数据库,得一个旧值
  • 3)请求B将新值写入数据库
  • 4)请求B删除缓存
  • 5)请求A将查到的旧值写入缓存

ok,如果发生上述情况,确实是会发生脏数据

发生上述情况有一个先天性条件,就是步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。可是,大家想想,数据库的读操作的速度远快于写操作的(不然做读写分离干嘛,做读写分离的意义就是因为读操作比较快,耗资源少),因此步骤(3)耗时比步骤(2)更短,这一情形很难出现。

三:最佳解决方案:

方案一

先做一个说明,从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。

方案二

Redis里的数据总是不过期,但是有个背景更新任务(“定时执行的代码” 或者 “被队列驱动的代码)读取db,把最新的数据塞给Redis。这种做法将Redis看作是“存储”。访问者不知道背后的实际数据源,只知道Redis是唯一可以取的数据的地方。当实际数据源更新时,背景更新任务来将数据更新到Redis。这时还是会存在Redis和实际数据源不一致的问题。如果是定时任务,最长的不一致时长就是更新任务的执行间隔;如果是用类似于队列的方式来更新,那么不一致时间取决于队列产生和消费的延迟。常用的队列(或等价物)有Redis(怎么还是Redis),Kafka,AMQ,RMQ,binglog,log文件,阿里的canal等。

注意,没有所谓的最佳解决方案,针对不同的业务场景我们自主选择不同的方案,不大可能达到十全十美

MySQL和redis数据一致性问题

上一篇:Kiwi Syslog Web Access与Active Directory集成认证


下一篇:企业架构NOSQL数据库之MongoDB