Redis 缓存更新一致性
当执行写操作后,需要保证从缓存读取到的数据与数据库中持久化的数据是一致的,因此需要对缓存进行更新。
因为涉及到数据库和缓存两步操作,难以保证更新的原子性。
在设计更新策略时,我们需要考虑多个方面的问题:
- 对系统吞吐量的影响:比如更新缓存策略产生的数据库负载小于删除缓存策略的负载
- 并发安全性:并发读写时某些异常操作顺序可能造成数据不一致,如缓存中长期保存过时数据
- 更新失败的影响:若某个操作失败,如何对业务影响降到最小
- 检测和修复故障的难度: 操作失败导致的错误会在日志留下详细的记录容易检测和修复。并发问题导致的数据错误没有明显的痕迹难以发现,且在流量高峰期更容易产生并发错误产生的业务风险较大。
更新缓存有两种方式:
- 删除失效缓存: 读取时会因为未命中缓存而从数据库中读取新的数据并更新到缓存中
- 更新缓存: 直接将新的数据写入缓存覆盖过期数据
更新缓存和更新数据库有两种顺序:
- 先数据库后缓存
- 先缓存后数据库
两两组合共有四种更新策略,现在我们逐一进行分析。
并发问题通常由于后开始的线程却先完成操作导致,我们把这种现象称为“抢跑”。 下面我们逐一分析四种策略中“抢跑”带来的错误。
先更新数据库,再删除缓存
若数据库更新成功,删除缓存操作失败,则此后读到的都是缓存中过期的数据,造成不一致问题。
可能发生的并发错误:
| 时间 | 线程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 缓存更新一致性的更多相关文章
- (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版本, ...
- SpringBoot缓存管理(二) 整合Redis缓存实现
SpringBoot支持的缓存组件 在SpringBoot中,数据的缓存管理存储依赖于Spring框架中cache相关的org.springframework.cache.Cache和org.spri ...
- Redis双写一致性与缓存更新策略
一.双写一致性 双写一致性,也就是说 Redis 和 mysql 数据同步 双写一致性数据同步的方案有: 1.先更新数据库,再更新缓存 这个方案一般不用: 因为当有两个请求AB先后更新数据库后,A应该 ...
- Redis缓存和数据库一致性问题
工作中,经常会遇到缓存和数据库数据一致性问题.从理论上设置过期时间,是保证最终一致性的解决方案.这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可 ...
- Redis缓存如何保证一致性
为什么使用Redis做缓存 MySQL缺点 单机连接数目有限 对数据进行写速度慢 Redis优点 内存操作数据速度快 IO复用,速度快 单线程模型,避免线程切换带来的开销,速度快 一致性问题 读数据的 ...
- Redis缓存穿透、击穿、雪崩,数据库与缓存一致性
Redis作为高性能非关系型(NoSQL)的键值对数据库,受到了广大用户的喜爱和使用,大家在项目中都用到了Redis来做数据缓存,但有些问题我们在使用中不得不考虑,其中典型的问题就是:缓存穿透.缓存雪 ...
- Redis系列十:缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
一.缓存雪崩 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而 ...
- redis缓存与数据库一致性问题
一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去. ...
- Redis缓存雪崩、缓存穿透、缓存击穿、缓存降级、缓存预热、缓存更新
Redis缓存能够有效地加速应用的读写速度,就DB来说,Redis成绩已经很惊人了,且不说memcachedb和Tokyo Cabinet之流,就说原版的memcached,速度似乎也只能达到这个级别 ...
随机推荐
- 何用Java8 Stream API进行数据抽取与收集
上一篇中我们通过一个实例看到了Java8 Stream API 相较于传统的的Java 集合操作的简洁与优势,本篇我们依然借助于一个实际的例子来看看Java8 Stream API 如何抽取及收集数据 ...
- Javascript Event事件中IE与标准DOM的区别
1.事件流的区别 <body> <div> <button>点击这里</button> </div> </body> IE采用冒 ...
- 第十章、Vue项目的联调上线
抓包 Fiddler 一.解决跨域 proxyTable(查看博客总结) 二.解决用本机ip地址不能访问 在dev中加上 --host 0.0.0.0就可以用本机ip访问,这样的话可以用手机在内网(局 ...
- 05 JPAUtil工具类
public final class JPAUtil { // JPA的实体管理器工厂:相当于Hibernate的SessionFactory private static EntityManager ...
- elasticjob学习二:封装elasticjob-spring-boot-starter
之前已经简单的学习了es-job.但是如果实际应用都如同第一篇进行编写,会有很多重复代码,不方便.这篇主要是进行封装.我还会用一个demo使用下封装好的组件. elasticjob-spring-bo ...
- django验证码框架captcha
1.安装 2.在settings.py 安装app中添加 3.添加url 4.运行makemigrations和migrate 5.运用 在form表单中定义 view中返回form表单 在前端htm ...
- 编译putty 源码去掉 Are you sure you want to close this session? 提示
0, 为什么要编译 putty ?在关闭窗口的时候,会弹出一个 Are you sure you want to close this session?要把这个去掉.当然也可以用 OD 之类的来修改. ...
- LSP原则—关于正方形不是长方形
长方形有二个属性长和宽.并有一个设置长的方法和设置宽的方法,还有一个求面积的方法. 像下面 private int length; private int width; public void set ...
- scrapy中间件中使用selenium切换ip
scrapy抓取一些需要js加载页面时一般要么是通过接口直接获取数据,要么是js加载,但是我通过selenium也可以获取动态页面 但是有个问题,容易给反爬,因为在scrapy中间件mid中使用sel ...
- Protocol buffers编写风格指南
原文链接:https://developers.google.com/protocol-buffers/docs/style Style Guide 本文说明了.proto文件的编写风格指南.遵循这些 ...