数据库与缓存双写一致性问题

缓存是什么

  • 通常我们会使用更快的介质(比如内存)作为缓存,来解决较慢介质(比如磁盘)读取数据慢的问题,缓存是用来解决性能问题的一种结构模式,本质就是空间换时间。
  • 缓存系统一般设计简单,功能相对单一,所以诸如Redis这种缓存系统的整体吞吐量,能达到关系型数据库的几倍甚至几十倍,因此缓存特别适用于互联网应用的高并发场景。

使用缓存需要注意什么

  • 从客户端的角度来说,缓存数据的特点一定是有原始数据来源的,且允许丢失。当数据丢失后,我们需要从原始数据重新加载数据,不能认为缓存系统是绝对可靠的,更不能认为缓存系统不会删除没有过期的数据。
  • 从 Redis 服务端的角度来说,缓存系统可以保存的数据量一定是小于原始数据的,那么我们就应该限制 Redis 对内存的使用量,并且明确以怎么样的策略来维护数据,常用的算法有 LRU、TTL 和 LFU 三种。

缓存同步策略

  • 先更新数据库,再更新缓存
    如果更新数据库成功,但缓存更新失败,此时数据库中是最新值,但缓存中是旧值,后续的读请求会直接命中缓存,得到的是旧值,对业务产生影响。
  • 先更新缓存,再更新数据库
    如果更新缓存成功,但数据库更新失败,此时缓存中是最新值,数据库中是旧值,后续读请求会直接命中缓存,但得到的是最新值,短期对业务影响不大。但是,一旦缓存过期或者满容后被淘汰,读请求就会从数据库中重新加载旧值到缓存中,之后的读请求会从缓存中得到旧值,对业务产生影响。
  • 先删除缓存,再更新数据库
    如果线程A删除缓存成功,还没来得及更新数据库(比如网络延迟),有另一个线程B读取缓存,发现缓存不存在,就回去数据库读取数据,这时读到的值是还没更新的值。然后线程A更新完数据库之后就会出现线程B读到的值和数据库的值不一致的情况。
  • 先更新数据库,再删除缓存
    如果线程 A 更新了数据库中的值,但还没来得及删除缓存值,线程 B 就开始读取数据了,那么此时,线程 B 查询缓存时,发现缓存命中,就会直接从缓存中读取旧值。不过,在这种情况下,如果其他线程并发读缓存的请求不多,那么,就不会有很多请求读取到旧值。而且,线程 A 一般也会很快删除缓存值,这样一来,其他线程再次读取时,就会发生缓存缺失,进而从数据库中读取最新值。

综上,先更新数据库再删除缓存是较好的一种策略。

如何解决数据不一致问题

  • 缓存设置过期时间
    给缓存设置一个合理的过期时间,这样能保证在一定时间内,缓存可以和数据库的数据保持一致。
  • 重试机制
    把要删除的缓存值或者是要更新的数据库值暂存到消息队列中,当应用没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
    如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了。否则的话,我们还需要再次进行重试。如果重试超过的一定次数,还是没有成功,我们就需要向业务层发送报错信息了。
  • 分布式锁
    高并发的情况下,可以考虑加入分布式锁,保证同一时间只有一个线程可以对同一条数据进行操作。
  • 延时双删
    线程A先删除缓存,然后线程 A 更新完数据库值之后,我们可以让它先 sleep 一小段时间,再进行一次缓存删除操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值