前言
因为缓存的高并发和高性能已经在各类项目中被普遍使用,在读取缓存这方面基本都是一致的,大概都是按照下图的流程进行操做:html
可是在更新缓存方面,是更新完数据库再更新缓存仍是直接删除缓存呢?又或者是先删除缓存再更新数据库?在这一点上就值得探讨了。python
一致性方案
在实际项目开发中须要保证数据库和缓存中的数据一致,不然人家充值了100块,不断刷新却仍是显示0.01元,岂不是尴尬?从理论上来讲,为缓存设置过时时间是最终保证数据一致性的解决方案,采用这种方案的话,全部的写操做都是以数据库为准,若是数据库写入成功可是缓存更新失败,只要缓存到过时时间以后后面读缓存时天然会在数据库中读取新的值而后更新缓存。接下来探讨的思路主要的方向是在不依赖为缓存设置过时时间的前提下如何保证数据一致性。这里主要探讨三种方案:mysql
①先更新数据库,再更新缓存git
②先删除缓存,再更新数据库github
③先更新数据库,再删除缓存redis
先更新数据库再更新缓存
这种方案是广泛被反对的(在个人认知范围中~),为啥呢?为啥这种方案就被反对呢?缘由主要有两方面,请听我细细道来:sql
首先从数据安全方面考虑,若是同时有请求A和请求B同时进行操做,A先更新了数据库的一条数据,随后B立刻有更新了该条数据,可是可能由于网络延迟等缘由,B却比A先更新了缓存,就会出现一种什么状况呢?缓存中的数据并不最新的B更新过的数据,就致使了数据不一致的状况。数据库
其次从业务场景方面考虑,若是是一个写数据库较多而读数据库较少的业务,若是采用这种方案就会致使数据还没读缓存就会被频繁更新,白白浪费性能。缓存
综合以上两方面的考虑,这种方案果断pass。下面的两种方案就是争议较大的两种方案了,究竟是先删缓存再更新数据库仍是先更新数据库再删除缓存?安全
先删缓存再更新数据库
若是同时有一个请求A进行更新操做,请求B进行查询操做,就可能会出现A请求进行写操做前会删除缓存,B请求恰好此时进来发现缓存是空的,B请求就会查询数据库,若是此时A请求的写操做还未完成,B请求查询到的就仍是旧的值,仍是会将旧的值写入缓存,A请求将新的值写入数据库,此时就会致使数据不一致的问题,若是不采用给缓存设置过时时间的策略,该数据永远都是脏数据。
解决这种状况能够采用延时双删的策略,就是在更新数据库以前先删除缓存,而后对数据库进行写入操做,数据库更新完成以后再次进行删除缓存的操做,目的是删除读请求可能形成的缓存脏数据,第二次删除缓存以前能够休眠几秒,具体时间开发者能够评估一下本身项目读数据业务逻辑的耗时,而后在该耗时基础上加几百ms便可,这么作的目的就是确保读请求结束写请求能够删除读请求形成的脏数据。若是MySQL采用的是读写分离的架构,可能因为主从延时的缘由形成数据不一致,能够在写操做完成以后根据主从延时时间休眠一下而后再进行删除缓存的操做。延时双删的伪代码以下:
# 伪代码 def delay_delete(): redis.delete('name') # 更新数据库以前先删除缓存 sql = 'update info set name='lili' where id=1;' # 更新数据库 cursor.execute(sql) time.sleep(1) # 若是mysql是主从架构则休眠主从延时的时间再多几百ms redis.delete('name') # 再次删除缓存 复制代码
那会不会存在第二次删除缓存失败的状况呢?若是第二次删除失败,仍是会形成缓存和数据库不一致的问题,又如何解决呢?且看下一种方案。
先更新数据库再删除缓存
老外提出了一个缓存更新方案Cache−AsidepatternCache-Aside patternCache−Asidepattern,文章中提到**应用程序应该从cache中获取数据,若是获取成功直接返回,若是没有获取成功,则从数据库中获取,成功后放到缓存中,更新数据时应该先把数据存到数据库中成功后再让缓存失效。**原文以下
If an application updates information, it can follow the write-through strategy by making the modification to the data store, and by invalidating the corresponding item in the cache.
When the item is next required, using the cache-aside strategy will cause the updated data to be retrieved from the data store and added back into the cache.
这种方案会不会产生数据不一致的状况呢?好比下述这种状况:
有两个请求A和B,A进行查询同时B进行更新,假设发生下述状况:
①此时缓存恰好失效
②请求A 就会去查询数据库获得一个旧的值
③请求B将新的值写入数据库
④请求B写入成功后删除缓存
⑤请求A将查到的机制写入缓存,产生脏数据...
若是发声上述状况,确实会产生数据不一致的状况,可是XDM想想,发生这种状况的几率是多少呢?若是先要产生这种结果,就必须有一个条件,就是请求B的操做时间很是短,短到什么程度呢,就是请求B写入数据库的操做要比请求A从数据库中读取数据的速度要快(由于redis很是快,所以操做redis的时间能够暂且忽略),只有这种状况下④才可能比⑤先发声,可是数据库的读操做要远比写操做快的多,否则作读写分离干吗呢?因此这种状况发生的几率是很是很是很是的低,可是若是强迫症患者出现必需要解决怎么办呢?就能够采用给缓存设置过时时间或者采用第二种方案的延时双删策略,保证读请求完成以后在进行删除操做。
最后的问题
还有问题呀,就是最终解决方案三可能 出现的极低几率的数据不一致的方案是采用方案二的延时双删策略,但是在方案二中也说了,若是出现缓存删除失败的状况咋办?那不是还会出现数据不一致的问题吗?这个问题到底如何解决呢?这里提供一个重试机制,删除失败就重试一次呗,这里提供一种重试的方案。
①更新数据库
②因为各类缘由缓存删除失败
③将删除失败的缓存放入消息队列中
④业务代码从消息队列中获取须要删除的key
⑤继续尝试删除操做,直到成功
结语
创做不易,若是对你们有所帮助,但愿你们点赞支持,有什么问题也能够在评论区里讨论😄~