【Redis学习笔记】4:Redis缓存数据库双写不一致问题

1 问题描述

如果有两个线程都要给某个字段落盘(先写数据库再写缓存),按照下面的顺序执行不会有问题:
在这里插入图片描述
但是如果按照下面的顺序就会出现数据库中的数据和缓存中的数据不一致的情况:
在这里插入图片描述
这就是缓存数据库双写不一致问题。


在更多时候,真实场景下,写入数据库之后往往不会直接去写缓存(浪费资源),而是会去把缓存删除掉,而是当另一个线程读数据库之后顺便把读的数据写入到缓存里。在这种情况下,如果“查数据库”和“更新缓存”这两个操作之间有延时,那么也会出现缓存数据库双写不一致问题:
在这里插入图片描述
因此这个问题本质上是对数据库的操作和其后的对缓存的操作之间夹杂了其他操作引起的

2 延迟双删(很不靠谱的方案)

该策略是,在每次写数据库删除缓存之后,隔一段时间后再进行一次删除操作:
在这里插入图片描述
这样只要线程2第二次删除缓存的操作在线程3错误的更新缓存操作之后执行,就能解决双写不一致的问题。

延迟双删会让所有对数据库的更新都删两次缓存,而且两次删除之间还有一定的延迟时间。这个方法能在一定程度上缓解双写不一致的问题,但是也是治标不治本的方案,而且还会导致所有的更新操作有延迟。

3 内存队列(很不靠谱的方案)

指对同一个key的所有增删改查,放到一个队列里去串行执行,这样的话本质上是把并发执行的所有线程串行化了,这样确实不会有双写不一致问题了,但是性能不高(靠串行化解决并发安全问题),而且实现起来也不容易。

4 加普通分布式锁(不够靠谱的方案)

不同线程对同一个key的操作,都要去获取同一把分布式锁,在执行完成后再去释放锁,这样本质上也是能把对同一个key的操作串行化,而且实现起来方便了很多。但是因为也和内存队列一样是靠串行化来解决并发安全问题,所以性能还是比较差。

5 加分布式读写锁(适合读多写少场景)

大多数业务场景都是读多写少的场景,可以使用读写锁。读写锁分为一把读锁和一把写锁,读操作之前加读锁,写操作之前加写锁。

读写锁的特点:读锁和读锁是不互斥的,只有读和写、写和写之间才互斥。

也就是说如果多个线程都是进行读操作,那么在读之前都会加读锁,这些加锁操作是不会互斥的。因为大多流量都是读操作,所以相比普通的分布式锁,使用读写锁能极大的提高性能。

下面是在Redisson中的使用,读操作的业务实现中使用读锁:
在这里插入图片描述
写操作的业务实现中使用写锁:
在这里插入图片描述
代码里的读锁和写锁用的是同一个key,这样也就能控制同一个资源。看源码可以看到Redisson的读写锁底层也是用Lua脚本操作Redis来实现的,只是在设置key的时候还额外指定了mode,来标识加的是读锁(read模式)还是写锁(write模式)

也就是说,Redisson的读写锁底层在加锁时,如果发现之前加的是read模式的锁或者没加锁,那么就可以直接操作;如果发现之前加的是write模式的锁,那么就要等这把锁释放掉再继续操作。

6 为缓存设置超时时间(适合允许一段时间DB和缓存不一致的场景)

没有技术能适合所有的业务场景,在某些允许一定时间的不一致存在的场景,比如商品详情页显示库存,因为购物流程很长,只要在下订单的时候保证不会超卖就可以了,所以在这种场景下只要直接为缓存设置一个超时时间,过了超时时间之后缓存失效,查询时就到数据库里去查新值就可以了。
在这里插入图片描述
用这种简单的处理方式,系统的性能反倒比较高,而且能满足当前业务场景的需要。

7 使用分布式数据库(适合读多写多场景)

在读多写多,又要求强的双写一致的场景用缓存没有大意义,这种时候可以直接采用专门的分布式数据库(如TiDB)来抗住并发量。

另外也可以采用阿里巴巴的Canal等特殊的中间件来保证双写一致性,具体看下其他资料。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值