redis实现缓存一致性的原理深度解析(代码片段)

刘Java 刘Java     2023-01-12     596

关键词:

详细介绍了Redis实现缓存一致性的三种方式,以及他们的优缺点。


首先要明白,缓存和数据库数据之间没有绝对的一致性,如果要绝对一致,那就不能使用缓存,我们只能保证数据的最终一致性,以及尽量保证缓存不一致的时间最短。

另外,为了避免极端条件下造成的缓存与数据库之间的数据不一致,缓存需要设置一个失效时间。时间到了,缓存自动被清理,这样才能达到缓存和数据库数据的“最终一致性”。

如果不是很高的并发的情况下,无论选择先删缓存还是后删缓存的方式,都几乎很少能产生问题。

1 先更新数据库,然后再删除缓存

如果先更新了数据库成功,删除缓存的时候失败,那么数据库中是新数据,缓存中是老数据,此时就会出现数据不一致的情况。

如果在高并发的场景下,还有一种更加极端的数据库与缓存数据不一致的情况:

  1. 缓存刚好失效;
  2. 请求A查询数据库,得一个旧值;
  3. 请求B将新值写入数据库;
  4. 请求B删除缓存;
  5. 请求A将查到的旧值写入缓存;

上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据,除非下一次更新数据库数据。

2 先删除缓存,然后再更新数据库

先删除缓存,后更新数据库,即使后面更新数据库失败了,缓存是空的,读的时候会从数据库中重新读取,虽然都是旧数据,但数据是一致的。

如果在高并发的场景下,还有一种更加极端的数据库与缓存数据不一致的情况:

  1. 请求A进行写操作,删除缓存;
  2. 请求B查询发现缓不存在;
  3. 请求B查询数据库,得一个旧值;
  4. 请求B将查到的旧值写入缓存;
  5. 请求A将新值写入数据库;

3 采用延时双删策略

上面的两种方法,不管是先写库,再删除缓存;还是先删缓存,再写库,都有可能出现数据不一致的情况。相对来说,第二种更保险,因此,如果只用这两种简单的方法,建议使用第二种。

更好较好的办法是采用一种“延时双删”的策略!在写库前后都进行一次redis.del(key)操作,另外为了避免更新数据库的时候,其他线程从缓存中读取不到数据然后读取旧数据写入缓存,就在更新完数据库之后,再sleep一段时间,然后再次删除缓存。

这个sleep时间要考虑大于另一个请求读取数据库旧数据+写缓存的时间,以及如果有redis主从同步、数据库分库分表,还要考虑数据同步的耗时,在Sleep之后再次尝试删除缓存(无论新的还是旧的)。这样,虽然不能保证一定不会出现缓存一致性的问题,但是能够保证仅在sleep时间内缓存不一致,降低了缓存不一致的时间。

当然这种策略由于需要休眠一定时间,这样毫无疑问又增加了写请求的耗时,导致服务器请求的吞吐量降低,这也是一个问题。为此,可以将第二次删除作为异步的删除。这样,业务线程的请求就不用sleep一段时间之后再返回,这么做,可以加大吞吐量。

万一删除缓存失败怎么办呢?此时又需要重试机制,那么此时可以利用消息队列,将需要删除的key发送到消息队列中,异步的消费消息,获得需要删除的key的值对比数据库的值!不一致则删除,还是删除失败则从新消费直到成功,或者当失败一定次数的时候一般就是Redis服务器出现问题了!

实际上引入消息中间件之后,问题变得更复杂了,我们需要保证消息中间件不出现各种问题,比如生产者发送消息成功或者失败。因此,我们可以借助监听数据库binlog日志的消息队列来做删除缓存的操作,好处是不需要自己往消息队列放消息了,通过一个中间件监听数据库的binlog日志,然后自动向队列中放消息,我们不再需要编程生产者代码,只需要编写消费者的代码。这种监听数据库binlog+消息队列的方式也是目前比较流行的一种方式。

4 为什么是删除缓存

为什么上面的方法都是删除缓存而不是更新缓存呢?

假如先更新数据库,然后更新缓存,那么可能出现如下情况:如果数据库1小时内更新了1000次,那么缓存也要更新1000次,但是这个缓存可能在1小时内只被读取了1次。如果是删除的话,就算数据库更新了1000次,那么也只是做了1次有效的缓存删除,后续的删除操作会立即返回,只有当缓存真正被读取的时候才去数据库加载缓存。这样减轻了Redis的负担。

相关文章:

  1. https://redis.io

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

docker安装canalmysql进行简单测试与实现redis和mysql缓存一致性(代码片段)

一、简介canal[kənæl],译意为水道/管道/沟渠,主要用途是基于MySQL数据库增量日志解析,提供增量数据订阅和消费。早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务trigger获取增... 查看详情

redis缓存使用技巧(代码片段)

...的一些技巧。缓存更新策略LRU/LFU/FIFO算法剔除场景:数据一致性要求较低原理:缓存使用量超过了预设值,使用maxmemory-policy来选择何种剔除策略对现有数据进行删除问题:数据清理由算法决定,开发人员只能选择使用哪种算法... 查看详情

一致性hash算法java版实现(代码片段)

...字长文聊缓存(下)-应用级缓存》,谈到缓存不说一下一致性Hash算法那就是在耍流氓。分布式缓存集群的访问模型现在通常使用Redis来做分布式缓存,下面我们就以Redis为例:假如当前我们系统的业务发展很快,需要缓存的数据... 查看详情

缓存架构中分布式一致性hash应用解析(代码片段)

前言本篇文章会从什么是分布式一致性hash算法、hash算法在Memcached、Redis中的应用、以及Java本地缓存与分布式缓存绝佳组合、剖析从浏览器缓存到数据库缓存等;然后去解析一致性hash算法的应用。以及我们在项目应用中,... 查看详情

docker安装canalmysql进行简单测试与实现redis和mysql缓存一致性(代码片段)

一、简介canal[kə’næl],译意为水道/管道/沟渠,主要用途是基于MySQL数据库增量日志解析,提供增量数据订阅和消费。早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是... 查看详情

canal解决redis与mysql缓存一致性问题(代码片段)

目录1缓存一致性2缓存一致性解决方案3Canal介绍3.1Canal应用场景3.2MySQL主从复制原理3.3Canal工作原理3.4Canal配置5同步更新Redis缓存1缓存一致性用户每次抢完红包,要查看自己抢红包记录,此时需要查询数据库表money_log,如果... 查看详情

redispub/sub发布订阅模式的深度解析与实现消息队列(代码片段)

...细介绍了Redis的Pub/Sub的相关命令和优缺点,以及如何实现简单的消息队列。文章目录1Pub/Sub的概述2订阅3取消订阅4模式匹配5发布6Pub/Sub原理6.1pubsub_channels6.2pubsub_patterns7Pub/Sub缺点1Pub/Sub的概述我们可以利用Redis的List数据结构实... 查看详情

redis发布订阅和事务实现原理(代码片段)

...订发送消息事务事务队列执行事务WATCH命令实现ACID原子性一致性隔离性持久性发布订阅Redis的发布订阅由PUBLISH,SUBSCRIBE,PSUBSCRIBE等命令组成,例子如下:redis中我们还可以通过PSUBSCRIBE"user.*"命令完成频道的模式订阅,也... 查看详情

redis发布订阅和事务实现原理(代码片段)

...订发送消息事务事务队列执行事务WATCH命令实现ACID原子性一致性隔离性持久性发布订阅Redis的发布订阅由PUBLISH,SUBSCRIBE,PSUBSCRIBE等命令组成,例子如下:redis中我们还可以通过PSUBSCRIBE"user.*"命令完成频道的模式订阅,也... 查看详情

redis缓存一致性(代码片段)

文章目录缓存一致性读缓存**双检加锁**策略写缓存保障最终数据一致性解决方案先更新数据库,再更新缓存案例演示1->更新缓存异常案例演示2->并发导致先更新缓存,再更新数据库案例演示->并发导致先删除缓存&#... 查看详情

redis缓存简介以及缓存的更新策略(代码片段)

...1a; 2、数据库和缓存不一致采用什么方案3、CacheAsidePattern实现4、先操作数据库还是先操作缓存?六、实现商铺和缓存与数据库双写一致 1、加入超时时间 queryById()2、修改更新的逻辑一、什么是缓存就像 查看详情

redis缓存双写一致性(代码片段)

目录双写一致性Redis与Mysql双写一致性canal配置流程代码案例双写一致性理解缓存操作细分缓存一致性多种更新策略挂牌报错,凌晨升级先更新数据库,在更新缓存先删除缓存,在更新数据库先更新数据库,在删除缓存延迟双删策略总... 查看详情

redis缓存一致性讨论(代码片段)

...法笔记(超级全)、大厂面试、笔试题目录缓存一致性缓存类型只读缓存新增数据更新数据并发读写读写缓存新增数据更新删除数据并发读写总结Singleflightredis执行lua脚本实现多命令原子性操作redis事务上 查看详情

redis缓存一致性讨论(代码片段)

...法笔记(超级全)、大厂面试、笔试题目录缓存一致性缓存类型只读缓存新增数据更新数据并发读写读写缓存新增数据更新删除数据并发读写总结Singleflightredis执行lua脚本实现多命令原子性操作redis事务上 查看详情

vuerouter源码深度解析(代码片段)

...备面试。路由原理在解析源码前,先来了解下前端路由的实现原理。前端路由实现起来其实很简单,本质就是监听U 查看详情

浅析一致性哈希算法的原理及实现(代码片段)

1.分布式缓存问题以上是单节点环境下,但随着流量的增大,可能就演变为了如下情形:❓这个负载均衡算法该如何设计最为合理呢?首先能想到的最简单的方法可能就是随机或者轮询,这样会产生两个问题&#x... 查看详情

redisstream流的深度解析与实现高级消息队列一万字(代码片段)

...本新增加的数据结构Stream的使用方式以及原理,如何实现更加可靠的消息队列。文章目录Stream概述2Stream基本结构3存储数据3.1EntryID3.2数量限制4获取数据4.1范围查询4.2独立消费消息4.2.1非阻塞使用4.2.2阻塞的使用4.3消费者组4.3.1... 查看详情

redis主从集群搭建及主从复制原理解析(代码片段)

Redis框架除缓存之外的实用功能前言上篇文章主要介绍redis的实用功能,也就是数据类型。包括string、hash、set、zset、发布订阅、stream等类型;并解析了他们的应用场景;本篇文章会紧接着写redis主从集群的搭建,... 查看详情