缓存一致性问题

Cache Aside策略

比如电商系统中有一个用户表,表中有ID和年龄两个字段,缓存中以ID为key存储年龄信息。当我们把ID为1的用户的年龄从19更新到20,怎么做?可以先更新数据库,再更新缓存。但是这样会导致缓存和数据库中数据不一致,如下图所示:
在这里插入图片描述

A 请求将数据库中 ID 为 1 的用户年龄从 19 变更为 20,与此同时,请求 B 也开始更新 ID 为 1 的用户数据,它把数据库中记录的年龄变更为 21,然后变更缓存中的用户年龄为 21。紧接着,A 请求开始更新缓存数据,它会把缓存中的年龄变更为 20。此时,数据库中用户年龄是 21,而缓存中的用户年龄却是 20。

**如何解决这个问题?**我们可以再更新数据时不更新缓存,而是删除缓存中的数据,在读取数据时,发现缓存中没了数据之后,再从数据库中读取数据,更新到缓存中。
在这里插入图片描述

这就是Cache Aside 策略。它的问题在于当写入比较频繁时,缓存中的数据会被频繁地清理,这样会对缓存的命中率有一些影响。如果业务中对缓存命中有严格要求,可以考虑以下两种解决方案:

  1. 更新数据时也更新缓存,只是在更新缓存前先加一个分布式锁,因为这样在同一时间只允许一个服务更新缓存,就不会产生并发问题了。当然这么做对于写入的性能会有一些影响。可以满足强一致性。
  2. 在更新数据时也更新缓存,只是给缓存加一个较短的过期时间,这样即使出现缓存不一致的情况,缓存的数据也会很快地过期,对业务的影响也是可以接受的。

Cache Aside 策略也并非一定能保证数据一致性,比如请求A读数据时,先读取缓存,发生Cache Miss进而读DB(数据版本V1),同时请求B修改数据,更新DB数据(数据版本V2),然后立马把以前的key删掉(发现key为空,请求A还没回填完),而后请求A才将V1版本数据回填到了缓存,产生了数据不一致的情况。
在这里插入图片描述

目前公司的解决办法是用一个消息队列异步补偿缓存,即用消费者消费Mysql的bin-log日志,再回填一次缓存,满足数据最终一致性。这也是有风险的,毕竟这个消费者服务和刚刚说到的请求A,无法满足“Happens Before”。因为也可能发生消费者服务在消费完成V2版本数据的回填之后,请求A才V1版本的数据覆盖写入到了缓存中,又造成不一致。这里得用一点小技巧,异步Job操作的时候用SetEX(优先级高,异步补偿直接写缓存),而请求A这种类型的操作用SetNX(优先级低,缓存中有数据,则不写入),这样就能保证数据最终一致性啦。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IaxeuUki-1626598371450)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1626597578273.png)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值