网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
总结:
理论上先更新DB
的线程理应也会先更新缓存,但是并发场景下线程的执行顺序无法保证:
- 若更新缓存的顺序是: 先线程1再线程2,则不会出现数据不一致问题。
- 若更新缓存的顺序是: 先线程2再线程1,此时缓存是线程1的数据,
DB
是线程2的数据,导致缓存和DB
数据不一致。
2 并发读写场景
在写线程更新DB
和更新缓存之间,读线程可以获取到旧数据,但最终会一致。
具体步骤:
- 线程1更新
DB
- 线程2查询,命中缓存返回
- 线程1更新缓存
总结:
线程2获取的缓存是旧数据,但最终都会一致。
3.2 先更新DB,后删除缓存
1 并发写场景
所有线程都是先更新DB
再删除缓存,无论哪个线程先更新DB
再删除缓存,缓存都会被删除,不会导致缓存和DB
数据不一致。
具体步骤:
- 线程1更新
DB
- 线程2更新
DB
- 线程2删除缓存
- 线程1删除缓存
总结:
无论哪个线程先更新DB
再删除缓存,缓存都会被删除,不会导致缓存和DB
数据不一致。
2 并发读写场景
在写线程更新DB
再删除缓存之间,读线程可以获取到旧数据,但最终会一致。
具体步骤:
- 线程1更新
DB
- 线程2查询命中缓存返回
- 线程1删除缓存
总结:
线程2获取的缓存是旧数据,但后续最终都会一致。
3.3 先更新缓存,后更新DB
1 并发写场景
所有线程都是先更新缓存再更新DB
,在某个写线程更新缓存和更新DB
之间,其他写线程也更新了缓存和DB
,导致缓存和DB
数据不一致。
具体步骤:
- 线程1更新缓存
- 线程2更新缓存
- 线程2更新
DB
- 线程1更新
DB
总结:理论上先更新缓存的线程也会先更新DB
,但是并发场景下线程的执行顺序无法保证:
- 若更新
DB
的顺序是: 线程1再线程2,则不会出现数据不一致问题。 - 若更新
DB
的顺序是: 线程2再线程1,此时缓存是线程2的数据,DB
是线程1的数据,导致缓存和DB
数据不一致。
2 并发读写场景
在写线程更新缓存和更新DB
之间,读线程也可以获取到最新的缓存,不会导致缓存和DB
数据不一致。
具体步骤:
- 线程1更新缓存
- 线程2查询,命中缓存返回
- 线程1更新
DB
总结:
可以保证缓存和DB
数据一致,虽然线程1更新DB
的操作还没有完成,但是更新缓存的操作已经完成了,读请求可以获取到最新的缓存。
3.4 先删除缓存,后更新DB
1 并发写场景
所有线程都是先删除缓存再更新DB
,无论哪个线程先删除缓存再更新DB
,缓存都会被删除,不会导致缓存和DB
数据不一致。
具体步骤:
- 线程1删除缓存
- 线程2删除缓存
- 线程2更新
DB
- 线程1更新
DB
总结:
无论哪个线程先删除缓存再更新DB
,缓存都会被删除,不会导致缓存和DB
数据不一致。
2 并发读写场景
在写线程删除缓存和更新DB
之间,读线程根据查询的DB
结果更新了缓存,导致缓存和DB
数据不一致。
具体步骤:
- 线程1删除缓存
- 线程2查询,未命中
- 线程2查询
DB
- 线程2根据查询的
DB
结果更新缓存 - 线程1更新
DB
总结:
线程1删除缓存和更新DB
之间,线程2根据查询的DB
结果更新了缓存,导致缓存和DB
数据不一致。
3.5 延迟双删
因为3.4 先删除缓存,再更新DB,在并发读写场景会导致数据不一致。
延迟双删是基于先删除缓存再更新DB
的基础上的改进,在更新DB
后延迟一定时间,再次删除缓存。
延迟是为了保证第二次删除缓存前能完成更新DB
操作,延迟时间根据系统的查询性能而定。
第二次删除缓存是为了保证后续请求查询DB
(此时数据库中的数据已是更新后的数据),重新写入缓存,保证数据一致性。
1 并发写场景
无论哪个线程都会删除缓存,所以不会导致缓存和DB
数据不一致。
具体步骤:
- 线程1删除缓存
- 线程2删除缓存
- 线程2更新
DB
- 线程1更新
DB
- 线程1延时删除缓存
- 线程2延时删除缓存
2 并发读写场景
具体步骤:
- 线程1删除缓存
- 线程2查询,未命中
- 线程2查询
DB
- 线程2根据查询的
DB
结果更新缓存 - 线程1更新
DB
- 线程1延时删除缓存
总结:
线程1第一次删除缓存之后,线程2根据查询的DB
结果更新缓存,此时查询得到的结果是旧数据,线程1延迟第二次删除缓存之后,后续查询DB
(此时数据库中的数据已是更新后的数据),重新写入缓存,不会导致缓存和DB
数据不一致。
3 延时双删的缺点:
- 需要延时,低延时场景不合适,如秒杀等需要低延时,需要强一致,高频繁修改数据场景。
- 不能保证强一致性,在更新
DB
之前,查询线程查询得到的结果是旧数据,可但可以减轻缓存和DB
数据不一致的问题。 - 延时的时间是一个不可评估的值,延时越久,能规避一致性的概率越大。
3.6 异步删除缓存
因为3.2 先更新DB,后删除缓存 在并发写场景不会导致数据不一致,但是在并发读写场景会短暂的导致数据不一致,但是由于删除缓存失败不会重试,并发写场景、并发读写场景都可能长时间导致数据不一致。
异步删除缓存是对先更新DB,后删除缓存的改进:更新DB
之后,基于消费队列异步删除缓存。
根据消费队列不同大致分为:消息队列、bin log
+消息队列。
3.6.1 基于消息队列的异步删除缓存
1 并发写场景
无论哪个线程先更新DB
再删除缓存,缓存都会被删除,不会导致缓存和DB
数据不一致。
具体步骤:
- 线程1更新
DB
- 线程2更新
DB
- 线程2把删除缓存放入消息队列
- 线程1把删除缓存放入消息队列
- 异步:消息队列消费删除缓存
总结:
无论哪个线程先更新DB
再删除缓存,缓存都会被删除,不会导致缓存和DB
数据不一致。
2 并发读写场景
异步删除缓存期间,读线程获取的缓存是旧数据,短暂出现数据不一致,异步删除缓存后最终会一致。
具体步骤:
- 线程1更新
DB
- 线程2查询缓存,命中返回
- 线程1把删除缓存放入消息队列
- 异步:消息队列消费删除缓存
总结:
异步删除缓存期间,读线程获取的缓存是旧数据,短暂出现数据不一致,异步删除缓存后最终会一致。
3.6.2 基于MySQL的bin log+消息队列删除缓存
1 并发写场景
具体步骤:
- 线程1更新
DB
- 线程2更新
DB
- 异步:
bin log
日志收集中间件定时收集DB
的bin log
日志 - 异步:
bin log
日志收集中间件发送日志消息到消息队列 - 异步:消息队列消费删除缓存
总结:
无论哪个线程先更新DB
再删除缓存,缓存都会被删除,不会导致缓存和DB
数据不一致。
–
2 并发读写场景
具体步骤:
- 线程1更新
DB
- 线程2查询缓存,命中返回
- 异步:
bin log
日志收集中间件定时收集DB
的bin log
日志 - 异步:
bin log
日志收集中间件发送日志消息到消息队列 - 异步:消息队列消费删除缓存
总结:
异步删除缓存期间,读线程获取的缓存是旧数据,短暂出现数据不一致,异步删除缓存后最终会一致。
3.6.3 异步删除缓存的优缺点
优点:
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
…(img-ZnpH8SLY-1715638651102)]
[外链图片转存中…(img-imUQ3bFx-1715638651103)]
[外链图片转存中…(img-zZgsmG1S-1715638651103)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新