当执行写操作后,需要保证从缓存读取到的数据与数据库中持久化的数据是一致的,因此需要对缓存进行更新。

因为涉及到数据库和缓存两步操作,难以保证更新的原子性。

在设计更新策略时,我们需要考虑多个方面的问题:

  • 对系统吞吐量的影响:比如更新缓存策略产生的数据库负载小于删除缓存策略的负载
  • 并发安全性:并发读写时某些异常操作顺序可能造成数据不一致,如缓存中长期保存过时数据
  • 更新失败的影响:若某个操作失败,如何对业务影响降到最小
  • 检测和修复故障的难度: 操作失败导致的错误会在日志留下详细的记录容易检测和修复。并发问题导致的数据错误没有明显的痕迹难以发现,且在流量高峰期更容易产生并发错误产生的业务风险较大。

更新缓存有两种方式:

  • 删除失效缓存: 读取时会因为未命中缓存而从数据库中读取新的数据并更新到缓存中
  • 更新缓存: 直接将新的数据写入缓存覆盖过期数据

更新缓存和更新数据库有两种顺序:

  • 先数据库后缓存
  • 先缓存后数据库

两两组合共有四种更新策略,现在我们逐一进行分析。

并发问题通常由于后开始的线程却先完成操作导致,我们把这种现象称为“抢跑”。 下面我们逐一分析四种策略中“抢跑”带来的错误。

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

若数据库更新成功,删除缓存操作失败,则此后读到的都是缓存中过期的数据,造成不一致问题。

可能发生的并发错误:

时间 线程A 线程B 数据库 缓存
1 缓存失效 v1 null
2 从数据库读取v1 v1 null
3 更新数据库 v2 null
4 删除缓存 v2 null
5 写入缓存 v2 v1

先更新数据库,再更新缓存

同删除缓存策略一样,若数据库更新成功缓存更新失败则会造成数据不一致问题。

可能发生的并发错误:

时间 线程A 线程B 数据库 缓存
0 v0 v0
1 更新数据库为 v1 v1 v0
2 更新数据库为 v2 v2 v0
3 更新缓存为 v2 v2 v2
4 更新缓存为 v1 v2 v1

当两个写线程发生冲突时,可以通过比较数据版本方式避免线程A写入旧的数据。

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

可能发生的并发错误:

时间 线程A 线程B 数据库 缓存
1 删除缓存 v1 null
2 缓存失效 v1 null
3 从数据库读取v1 v1 null
4 更新数据库为v2 v2 null
5 将v1写入缓存 v2 v1

先更新缓存,再更新数据库

若缓存更新成功数据库更新失败, 则此后读到的都是未持久化的数据。因为缓存中的数据是易失的,这种状态非常危险。

因为数据库因为键约束导致写入失败的可能性较高,所以这种策略风险较大。

可能发生的并发错误:

时间 线程A 线程B 数据库 缓存
0 v0 v0
1 更新缓存为 v1 v0 v1
2 更新缓存为 v2 v0 v2
3 更新数据库为 v2 v2 v2
4 更新数据库为 v1 v1 v2

异步更新

双写更新的逻辑复杂,一致性问题较多。现在我们可以采用订阅数据库更新的方式来更新缓存。

阿里巴巴开源了mysql数据库binlog的增量订阅和消费组件 - canal

我们可以采用API服务器只写入数据库,而另一个线程订阅数据库 binlog 增量进行缓存更新的策略。

这种策略存在和先更新数据库后删除缓存类似的并发问题:

时间 读线程 写线程 异步线程 数据库 缓存
1 缓存失效 v1 null
2 从数据库读取v1 v1 null
3 更新数据库为v2 v2 null
4 删除缓存/更新缓存 v2 null
5 写入缓存 v2 v1

这个问题同样可以采用异步线程更新缓存,且写入缓存时比较数据版本的方法来解决。

Redis 缓存更新一致性的更多相关文章

  1. (redis缓存更新策略)postgres 9.4.1 && redis 3.7.0 && redis_fdw_REL9_4_STABLE

    首先下载redis_fdw,这里要注意下载的版本.(https://github.com/pg-redis-fdw/redis_fdw) 一开始,我下载了REL9_4_STABLE_pre2.8版本, ...

  2. SpringBoot缓存管理(二) 整合Redis缓存实现

    SpringBoot支持的缓存组件 在SpringBoot中,数据的缓存管理存储依赖于Spring框架中cache相关的org.springframework.cache.Cache和org.spri ...

  3. Redis双写一致性与缓存更新策略

    一.双写一致性 双写一致性,也就是说 Redis 和 mysql 数据同步 双写一致性数据同步的方案有: 1.先更新数据库,再更新缓存 这个方案一般不用: 因为当有两个请求AB先后更新数据库后,A应该 ...

  4. Redis缓存和数据库一致性问题

    工作中,经常会遇到缓存和数据库数据一致性问题.从理论上设置过期时间,是保证最终一致性的解决方案.这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可 ...

  5. Redis缓存如何保证一致性

    为什么使用Redis做缓存 MySQL缺点 单机连接数目有限 对数据进行写速度慢 Redis优点 内存操作数据速度快 IO复用,速度快 单线程模型,避免线程切换带来的开销,速度快 一致性问题 读数据的 ...

  6. Redis缓存穿透、击穿、雪崩,数据库与缓存一致性

    Redis作为高性能非关系型(NoSQL)的键值对数据库,受到了广大用户的喜爱和使用,大家在项目中都用到了Redis来做数据缓存,但有些问题我们在使用中不得不考虑,其中典型的问题就是:缓存穿透.缓存雪 ...

  7. Redis系列十:缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级

    一.缓存雪崩 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而 ...

  8. redis缓存与数据库一致性问题

    一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去. ...

  9. Redis缓存雪崩、缓存穿透、缓存击穿、缓存降级、缓存预热、缓存更新

    Redis缓存能够有效地加速应用的读写速度,就DB来说,Redis成绩已经很惊人了,且不说memcachedb和Tokyo Cabinet之流,就说原版的memcached,速度似乎也只能达到这个级别 ...

随机推荐

  1. nexus7入手

    平板一直关注了很久了,关键是不知道平板对我来说,拿它来做什么用.所以,一直也就是关注,也没有决心买了. 终于这次出手了,N7,到货了! 照片是原生的android系统,不习惯,不习惯,直接用刷机精灵, ...

  2. js操作复选框

    js操作复选框 JavaScript 代码: //复选框全选 $(function () { $("#select_all").click(function () { $(&quo ...

  3. SpringBoot入门系列(二)如何返回统一的数据格式

    前面介绍了Spring Boot的优点,然后介绍了如何快速创建Spring Boot 项目.不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/zhangweizhong/ ...

  4. MVC05

    1. 添加搜索功能 如何实现url添加查询字符串实现查询指定项目的功能? 来到MovisController,修改Index方法如下 public ActionResult Index(string ...

  5. 用table类型布局一个新闻网页

    <html><head><meta http-equiv="Content-Type" content="text/html; charse ...

  6. 续python学习(一)

    接上面没写完的知识点继写. 当然,这些知识点都很简单,可能没必要花费太多时间去记忆,多写写代码就会了. 5.字符串的使用.索引和切片是字符串主要的两个应用.索引:顾名思义就是找出某个字符在一个字符串中 ...

  7. java调用DLL,打印二维码标签

    package com.ian.das.controller; import java.util.List; import org.xvolks.jnative.JNative; import org ...

  8. iview的render函数使用

    render渲染函数详解 https://www.cnblogs.com/weichen913/p/9676210.html iview表格的render函数作用是自定义渲染当前列,权限高于key,所 ...

  9. OpenWrt tcpdump 抓包

    路由器用的是 网件的 4300 刷的是石像鬼双固件 1, 安装支持库opkg updateopkg install libpcapopkg install tcpdump 2, 设置条件开始捕获tcp ...

  10. python虚拟环境安装使用

    # 安装 pip install virtualenv pip install virtualenvwrapper-win # win环境下 liunx下不需要-win #创建一个文件夹并cd进去mk ...