解决Redis和数据库一致问题

昨天面试了一家公司,简历上我写了用redis做缓存,当面试官问到,当有用户修改了信息,怎么做到缓存的信息和数据库一致呢,当时或许是紧张或许是真不知道,还是脑子短路了,就没回答出来。

面试完和我舍友提到的时候,慢慢就想起以前看到过类似的解决方法:那就是当用户的信息有更新的时候就进行缓存和数据库双写,然后在具体的操作中,考虑延迟双删策略:即

  • 删除缓存
  • 更新数据库
  • 再次删除缓存

实现伪代码如下:

这里做一个介绍:

  1. 首先,代码先删除 Redis 中的缓存数据,以确保接下来的读操作会从数据库中读取到最新的数据。
  2. 接着代码更新了数据库中的数据,将数据更新为最新的值
  3. 在此之后,代码让当前线程睡眠一段时间,这个时间是为了给数据库操作足够的时间来完成,确保数据已经持久化到数据库中。
  4. 最后,代码再次删除Redis中的缓存数据。这是缓存双删的关键步骤。由于之前已经删除了缓存数据,再次删除的目的是为了放置在 Thread.sleep(N)的时间内有其他线程读取到就得缓存数据。因为这段时间内,缓存数据已经被清空,所以其他线程在读取数据时会发现缓存中不存在,然后从数据库中读取最新的数据并写入缓存,从而保证了数据的一致性。

这就避免了并发产生的的脏数据,在这其中延伸出几个思考:

1、为什么选择删除缓存呢?

缓存中的数据可能不只是简单的一个字符串类型,还有可能是其他json串的。如果需要对其进行修改的话则需要进行序列化之后,再解析出其中的字段,把其修改之后再序列化,最后再更新到缓存之中。由此可知更新缓存的动作相比于直接删除缓存更加的复杂,页容易出错。

2、先写数据库还是先删缓存?

先写数据库:

因为数据库和缓存的 操作是两个不同的操作,所以没办法做到原子性,所以就有可能一个操作成功另外一个操作失败。把删除缓存放到第二步,如果失败了,会出现一个情况,那就是数据库的数据已经更新,但是缓存还是旧数据,导致数据不一致的问题。

另外在并发环境下,如果直接更新数据库而不删除缓存,会导致脏数据问题。考虑下面场景:

  1. 线程A读取缓存中的旧数据
  2. 线程B更新数据库中的数据,并删除缓存
  3. 线程A继续使用缓存中的旧数据,因此此时他不知道缓存已经被删除。

先删缓存

如果是先删缓存后写数据库的话,那么第二步的失败还是可以接受的,以为这样不会有脏数据,没什么影响,只需要重试就好。

第二个好处就是:缓存删除的失败概率比较低,一般只有网络问题或者是缓存服务器宕机才出现,所以先删除缓存还是很有保障的。

先删缓存后写数据库这种方式,也会存在一个问题。我们先捋一下使用缓存后读线程的过程:

  • 查询缓存,如果缓存中有值,则直接返回
  • 查询数据库
  • 把数据库中的查询结果更新到缓存之后

所以对于一个读线程来说,虽然不会写数据库,但是会更新缓存,所以在并发情况下会存在数据不一致的情况。

也就是说,假如一个读线程,在读缓存的时候没查询值就是到数据库中查询。但是如果在查到结果之后,更新缓存之前,数据库被另一个线程进来更新了,但是前面那个读线程是完全不知道的,那么就是导致最终缓存会被更新成一个“旧值”覆盖掉了。

虽然发生的概率比较低,但是还是有很低的概率发生,因为读的操作是比写操作要耗时的。一旦这种情况发生,后果也是比较严重的,那就是缓存中的值一旦是错的,就会导致后续所有的从缓冲中查询到的结果都是错的,所以为了进一步解决这种问题,那就是过几秒钟之后再删除一次缓存,就是一开始提到的延迟双删。

3、有第二次删除,第一次还有意义吗?

如果没有第一次删除,只保留第二次删除那么这个流程就变成了:

  • 更新数据库
  • 更新缓存

这个方案一旦删除缓存失败了会导致数据不一致的问题。

那如果延迟 双删的第二次删除不也一样有可能失败么?

的确,第二次删除也还是有失败的概率,但是比因为我们在延迟双删的方案中先做了一次删除,而延迟双删的第二次删除只为了 尝试解决  因为读写并发导致的不一致问题,或者说尽可能降低这种情况发生的概率。

如果没有第一次删除,只靠第二次删除,那么就不只是解决读写并发情况下不一致的问题了。即使没有并发,第二次只要删除失败,就会存在缓存不一致的问题,所以,第一次删除的目的就是降低不一致的发生概率。

 

因此,延迟双删策略只能在一定程度上提高数据一致性的概率,但不能完全解决数据一致性的问题。更加严格的数据一致性保证需要使用更复杂的机制,比如使用消息队列等。
 

4、先更新数据库后删除缓存 PK 延迟双删

方案

优点

缺点

实现复杂度

适用场景

先更新数据库后删除缓存

减少了一次删除缓存的开销

在数据库更新期间,读取请求可能读取到失效的缓存数据

简单

数据一致性要求较低、对性能要求较高的场景

延迟双删

保证了数据一致性,读取请求不会读取到失效的缓存数据

需要进行两次缓存删除操作,增加了一定的资源开销

复杂

数据一致性要求较高的场景,同时对性能影响有一定容忍度的场景

 

 

posted @   星桐  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示