一个应用中决定加缓存(Redis,memcached)以前,要考虑的第一个问题就是,引进了缓存以后,会带来哪些收益(利),付出哪些代价,引发哪些额外的问题(弊)?
任何新的中间件引进,收益和成本都是伴随的,只有当利大于弊的状况下,可以容忍其弊端(完全解决?没有额外代价又没有负面影响,是不可能的,那就是不用就好了),才值得引进。
以Redis做为缓存为例,引进以后,其利和弊也是伴随的。
带来的收益:加速读写,提升并发性,下降后端持久化层数据库的负载
付出的代价:增长代码复杂,缓存自己的运维,潜在的数据不一致形成的影响。
数据不一致的存在
引进Redis(或者其余缓存)以后,应用程序到持久化层多了一个中间层,部分数据存储由原来的单一持久化层,变为缓存层和持久化层两份。
这两部分数据在相互同步的过程当中,在某些时间点上的维度来看,可能会潜在不一致的状况。
其中,潜在的数据不一致,是任何一个引进缓存层以后最面临的最大的一个问题(缓存层和持久化层,最终的数据是要保持一致的,这一点是底线)。
首先须要衡量的就是,这种潜在的不一致,会引起什么样的问题,带来的问题是否能够接受范围以内,或者是否会对应用程序逻辑引发致命的问题。
缓存和持久化层存储可能会不一致,每每是缓存和持久化层未同步刷新引发的,
具体举例说明:
第一种状况,好比点赞次数,浏览次数等等(读多写少的场景,写MySQL,读Redis,写入了数据库可是还没有同步到缓存层这个间隙)。
不会对业务产生严重的逻辑错误,这种暂时性的数据不一致是能够忍受的,另外就是,经过刷新等手段,二者数据最终会达成一致。
第二种状况,好比银行卡取款取超,致使余额为负数,缓存和持久化层存储的不一致形成严重的逻辑错误,这种是没法忍受的。
就须要考虑这种缓存层自己的设计是否合理?
轻量级作法,代码逻辑实现
若是对于缓存的合理性没有问题,且业务逻辑上要求缓存和持久化层强一致,那么久要实现数据库的一致性操做。
对于缓存和持久化层数据的一致性实现,我的的话,思路有如下两种,
轻量级的作法以下:
对于引发数据变化的逻辑,通常都是“写操做”,好比对数据的update或者delete或者insert操做,
1,首先去delete缓存中对应的数据(而不是去对应的update、delete、insert,为何?由于只要delete成功,缓存被清理以后,就消除了不一致的可能性,而非delete就作不到)
2,若是1执行成功,再去操做持久化层的数据库,
3,最后将写入成功以后的持久化层数据回写缓存层(这一步可选,或者其它手段同步)
重量级分布式锁实现,双写实现强一致
双写的安全性通常要经过分布式锁来实现,分布式锁能够经过zookeeper或者redis实现。
一旦考虑使用分布式锁,又要考虑分布式锁的载体的安全性,也即不论是用zookeeper或者redis,要考虑zookeeper或者redis的安全性(集群)。
这样下去,问题会变得很是复杂,纯粹变为解决问题-->引入新的问题-->解决问题的死循环。
若是要保持一致,固然双写也是一种选择,不过经过双写来确保数据的绝对一致,不但会对总体效率产生负面的影响,实现也是比较困难的,暂时不讨论这种方案。redis
若是是分布式锁,任何写入性操做,好比update,delete等,以下:
1,直接锁定相关key值
2,依次操做缓存层和持久化层,同时作好每一层的回滚操做,一旦任何一步失败,都要回滚
3,最终无论成功或者失败,都释放Key
分布式锁这种方式的话,实现起来,原代码中业务侵入性较多,比较复杂