这两天正在做公司缓存方面的设计,然后就把自己的思考过程整理一下。
网上对于这块的内容讲解也非常的多,有些说的也都非常的在理,关于缓存一致性的方案也就那么几种,如:先更新、再删,先删、在更新,先更新再更新等,然后围绕这些方案产生的问题有衍生出了+rocketmq方案或者+binlog&rocketmq方案,有兴趣的大家可以自己搜索。
本篇内容主要是我在整理更新、再删这种方案时的思考,欢迎一起讨论。
读写不分离场景
期望结果
关于先更新再缓存这种缓存方案设计的思考 - 程序员古德
这种方式在网上的推荐比较多,原因是出错概率小,实现起来比较简单,对于一般规模不大的系统足矣。深度思考,会发现以下几个问题:
1、如果服务B更新频率高,服务C读取频率低,对整个方案会有什么影响?
频繁的更新Mysql(RW)会导致Mysql(RW)的压力变大,但只对Redis做缓存删除,因此对Redis的影响微乎其微。
因为Redis的缓存总是被删除,因此服务C在每次读取缓存时都会先读取数据库然后更新缓存,这种情况下服务C中缓存的机制就失去了意义,也会对服务C性能造成一定的影响。
小概率会出现缓存击穿、缓存雪崩、缓存穿透等问题。
2、如果服务B更新频率低,服务C读取频率搞,对整个方案会有什么影响?
Mysql(RW)的更新频率低,也就是意味着缓存的更新频率低,对于服务C来说也就不用频繁的到Mysql(RW)中去查询数据,服务C少了和Mysql(RW)的交互,在性能上也能大幅提升。
但是服务C读取频率高,大概率会出现缓存击穿、缓存雪崩、缓存穿透等问题。尤其是缓存击穿问题,当高并发情况下缓存突然失效,这时就会因为缓存不命中,导致大量的请求开始读取Mysql(RW)以及更新Redis,从而给Mysql(RW)和Redis造成性能影响。
极端情况一
关于先更新再缓存这种缓存方案设计的思考 - 程序员古德
如果Mysql(RW)更新失败,但是Redis删除成功会有什么影响?
存在一定影响,因为在服务B中更新Mysql(RW)失败了,但是删除Redis却成功了,从业务上来说,在这一步就产生了缓存不一致问题。但是又因为删除Mysql(RW)失败,数据库的值没有变化,服务C读取的数据也就没有变化,从服务C来说缓存也就没有改变,不存在缓存一致性问题。
极端情况二
关于先更新再缓存这种缓存方案设计的思考 - 程序员古德
如果服务B缓存删除失败,对整个方案会有什么影响?
在服务B中如果Redis删除失败,则会导致Redis中的缓存值在一定时间之内和Mysql(RW)中不一致,如果没有其它方式介入,只能等到缓存失效,下一次请求进来时重新更新缓存,这种情况产生的风险非常大。如果是商详页或者商城首页,出现这问题直接就GG了。
读写分离场景
期望结果
关于先更新再缓存这种缓存方案设计的思考 - 程序员古德
当下项目规模都很大,公司Mysql数据库多数采用主从库、读写分离的部署方式,主库和从库之间通过binlog机制进行同步,主库数据的变动可以实时同步到从库。
主从同步机制自身也存在很大的问题,比如,当主库写入量非常多,或者主从库网络不通畅,这就使binlog同步存在数据延迟,导致主库和从库数据存在不一致情况,读写分离场景下,主库和从库之间同步延迟的时间,也是导致缓存不一致的主要原因。
深度思考以下几个问题:
1、如果服务B更新频率高,服务C读取频率低,对整个方案会有什么影响?
频繁的更新Mysql(W)会导致Mysql(W)的压力变大,但只对Redis做缓存删除,因此对Redis的影响微乎其微。
但是,如果服务B产生大量的数据变动,这就会使binlog同步延迟,同样的,网络延迟也会导致binlog同步延迟,这就导致了当Redis缓存过期时,服务C从Mysql(R)中读取的数据可能不是最新的,最终导致缓存不一致问题。
小概率会出现缓存击穿、缓存雪崩、缓存穿透等问题。
2、如果服务B更新频率低,服务C读取频率搞,对整个方案会有什么影响?
服务B上Mysql(W)的更新频率低,也就是意味着缓存的删除频率低,产生的变动数据少,对于binlog同步来说产生延迟也就越小(假设不考虑网络原因),对于Mysql(R)来说,binlog同步延迟越低,数据一致性就越高。同样的,网络的延迟问题也是需要考虑的。
总结一下,在读写分离场景下,不仅binlog自身同步会影响缓存一致性,网络问题也会影响缓存一致性,这就使得在读写分离场景下实现缓存一致性会有更高的挑战。
极端情况
关于先更新再缓存这种缓存方案设计的思考 - 程序员古德
当服务B在主库更新完Mysql(W)数据库,并且从Redis删除缓存,从库通过binlog进行数据同步,由于数据积压、网络延迟,从库一直未能和主库数据保持一致。
但是在这时服务C有新的请求进入,并且由于缓存过期,需要从从库获得数据,但是由于从库未和主库数据保持一致,因此服务C从从库获得数据并不是最新的,这也就导致了缓存不一致问题。
核心总结
比较适合读写不分离的场景,如果是读写分离场景,一定要充分考虑binlong同步延迟和网络延迟带来的缓存不一致问题。
比较适合写少读多的的业务场景,频繁的写入同时也会频繁的删除缓存,对于读取频率低的服务C来说,缓存也就失去了意义,因为每次都得要重新获得数据库数据。
缓存一定要设置有效期,非常重要。如果不设置会导致当缓存不一致时,永远不能和数据库保持一致。
要充分考虑更新失败和删除失败的策略
其它???
领取专属 10元无门槛券
私享最新 技术干货