Redis学习——数据不一致怎么办?更新缓存失败了又怎么办?

文章目录

引言

  • 最近面试快手的时候被问到了缓存不一致怎么解决?一开始还是很懵的,因为会默认将自己带到一个错误的前提下,就是数据已经不一致了,你怎么办?可能会想,遍历缓存,删除比对数据是不是一致的。停下来想一下,一般是修改数据库的时候,你应该怎么操作缓存,才能保证缓存的一致性。我就知道要么删除缓存后续重建,要么直接修改缓存,但是我对于这两种场景的方法了解的并不多,今天借这个机会,完全整理一下!

正文

数据一致分几种情况?

  • 缓存中有数据,必须要和数据库中的数据一致
  • 缓存中本身没有数据,数据库中的值必须要是最新的

常见的缓存的策略主要有两种,分别是读写缓存和只读缓存,不同策略保证缓存一致性的方式是不一样。具体如下

读写缓存的数据一致性

  • 读写缓存是对于数据的所有增删改都需要在缓存中进行 ,然后在采取对应的写回策略。主要有两种写回策略
    • 同步直写策略
    • 异步写回策略

同步直写策略

  • 写缓存的时候,同步写数据库,缓存和数据库中的数据是一致的
    • 需要通过事务机制,保证缓存和数据库更新的原子性 ,一般是通过tranactional修饰
      • 要么同步更新成功
      • 要么同步更新失败,下次重试

异步写回策略

  • 写缓存的时候,不同步写入缓存,等到数据从缓存中淘汰的时候,在写入数据库。
    • 问题
      • 一旦缓存崩溃,在缓存中保存的数据,都会崩溃丢失

只读缓存的数据一致性

  • 如果有数据更新(数据增加和修改),直接写入数据库,缓存只负责读数据

    • 在下述过程中,可以看到整个过程是涉及到多步骤多线程的,除了对于数据的操作是具有原子性的,不可打断的,其他的读数据包括数据库同步删除缓存中的数据,都会存在可能的中断,或者其他线程切入的操作

新增数据情况==》不会出现数据不一致的情况

  • 但是删除数据或者修改数据不一样了,因为中间涉及到需要删除缓存中的数据,这一步执行失败会导致数据不一致的情况,具体情况见下一节!
删除和修改数据不一致问题
操作执行失败导致数据不一致

操作分析

  • 对于删除或者修改数据的操作,需要同时修改缓存和操作数据库,任何一方都有可能执行失败,因为没有办法保证双方的操作具有原子性,也就是同时失败或者同时成功。
  • 下面按照情况,进行逐个分析,分别是删除缓存失败和删除数据库失败两种情况。

删除缓存失败

  • 在下述过程中,删除缓存失败,导致缓存中保存旧版的数据,数据库是新版的数据,两者不一致。

修改数据库失败

  • 修改数据库失败了,删除缓存成功了,但是在二次访问会重建缓存,将数据库中的脏数据,还原到缓存中,还是会出现访问错误数据的情况

总结

  • 两种操作执行失败带来的数据不一致的情况如下
不同情况 潜在问题
先删除缓存,在更新数据库 数据库更新失败,导致请求再次访问缓存时,发现缓存缺失,重建缓存,读到的还是旧的数据
先更新数据库,再删除缓存的 缓存删除失败,有请求再次访问缓存时,发现缓存命中,返回脏数据,和数据库不一致
解决办法
  • 在上述两种情况中,如果修改数据库的操作执行失败了,其实不会产生数据不一致的问题,因为缓存中的数据和数据库中的数据是一致的,后续数据库执行相应的处理策略。但是缓存删除执行失败,就不一样了。所以这个解决办法,主要是针对缓存删除失败的情况而指定的。

消息队列重试

  • 在进行删除或者修改数据时,将数据保存到消息队列中,如果执行失败,再通过消息队列重试执行,如果执行成功了,就从消息队列中删除对应的值。

在重试这段时间,一般是存在数据不一致性,一般是通过如下方式解决

  • 引入分布式锁,加锁,防止其他数据在访问,会降低并发性
多线程访问导致数据不一致问题
  • 因为同步缓存需要两步,分别是修改数据库和缓存,这两个步骤之间一般来说不会加锁,这样会降低并发性,不加锁就意味着会出现多线程访问问题,导致数据的不一致。
  • 按照更新数据库和缓存的先后,主要分为两种情况,这里具体讨论一下

1、先修改数据库,然后在删除缓存访问

时间 线程1 线程2 问题
t1 删除数据库中的数据a
t2 读取缓存中的数据a,缓存命中,读取到不存在的旧值a 线程1尚且没有完全删除缓存值,线程2读到缓存中的脏数据a
t3 删除缓存中的数据a
  • 总结
    • 这种情况持续的时间会很短,因为仅仅只要线程1被切换回来,成功删除缓存中值,就能保证后续数据的一致性,并不需要额外的人工干预。影响小

1、先修改数据库,然后在删除缓存访问

时间 线程1 线程2 问题
t1 删除缓存中的数据a
t2 读取缓存中的数据a,缓存没有命中,访问旧版数据库,重建缓存a 1、线程1没有更新数据库的值,线程2会读到旧值;只要缓存不过期中间,后续所有线程读到的都是旧值
t3 更新数据库中的数据a 缓存中是旧值,数据库中是新值,两者不一样!

解决办法===延迟双删

  • 修改完数据库之后,再进行一次缓存删除操作,执行两次,保证的缓存的数据重建来自更新之后的数据库
  • 具体指令如下
powershell 复制代码
redis.delKey(x);
db.update(x);
Thread.sleep(N);
redis.delKey(x);
总结
  • 推荐先删除数据库,再删除缓存,主要原因有以下两点
    • 先删除缓存再删除数据库,导致请求因为缓存缺失而访问数据库,给数据库带来压力
    • 延迟双删的时间不好确定
  • 如果对于数据一致性有强要求
    • 更新数据库的时候,暂停redis缓存发送请求,等都弄完了再发送

总结

  • 不总结不知道,之前知道的太浅薄了,关于redis了解的不够深刻,实际上数据不一致不仅仅是删除或者更新缓存那么简单,中间还会涉及到多线程的问题,因为多个更新数据库和删除缓存并不具有原子性,中间有可能被打断,所以需要分情况进行讨论。
  • 出现数据不一致的情况的以及对应的解决方式有两种,具体如下
数据不一致原因 解决办法
删除redis执行失败 消息队列重试
多线程导致数据不一致 延迟双删策略

参考信息

缓存异常------如何解决缓存和数据库不一致的问题

相关推荐
狂飙的张兴发13 分钟前
认知小文2《成功之路:习惯、学习与实践》
学习·考研·职场和发展·跳槽·学习方法·改行学it·高考
爱编程的小新☆17 分钟前
C语言内存函数
c语言·开发语言·学习
夜清寒风1 小时前
opencv学习:图像掩码处理和直方图分析及完整代码
人工智能·opencv·学习·算法·机器学习·计算机视觉
吃着火锅x唱着歌2 小时前
Go语言设计与实现 学习笔记 第七章 内存管理(1)
笔记·学习·golang
我命由我123453 小时前
2.使用 VSCode 过程中的英语积累 - Edit 菜单(每一次重点积累 5 个单词)
前端·javascript·ide·vscode·学习·编辑器·学习方法
Pluses3 小时前
Datawhale X 李宏毅苹果书 AI夏令营 《深度学习详解》第十九章 ChatGPT
人工智能·笔记·深度学习·学习
南山933 小时前
如何快速学习拼音打字?
学习·大学生·打字侠
2401_856926934 小时前
图片转PDF技巧揭秘:四款高效工具推荐!
学习·pdf·图片转pdf·图片转pdf工具
Austim小白5 小时前
QT消息对话框学习
qt·学习
DYS_000015 小时前
阿里短信服务+Redis创建定时缓存
数据库·redis·缓存