Redis 数据一致性
概述
当我们在使用缓存时,如果发生数据变更,那么你需要同时操作缓存和数据库,而它们两个又分属不同的系统,因此无法做到同时操作成功或失败,因此在并发读写下很可能出现缓存与数据库数据不一致的情况
理论上可以通过分布式事务保证同时操作成功或失败,但这会影响系统性能,一般很少使用。虽然没办法做到缓存和数据库强一致,但我们可以让他们的数据尽可能在绝大部分时间内保持一致,并保证最终是一致的
缓存更新设计
一般来说都是采用删除缓存的方式更新缓存,这就涉及到先删除缓存还是先更新数据库的顺序问题了
1. 先删除缓存,后更新数据库
先删除缓存,后更新数据库,如果数据库没有更新成功,下次读缓存发现不存在,则从数据库读取,并重建缓存,此时数据库和缓存依旧保持一致,但还是旧值
高并发下,假设有两个线程并发读写数据,可能会发生以下场景:
- 线程 A 要更新 X = 2(原值 X = 1)
- 线程 A 先删除缓存
- 线程 B 读缓存,发现不存在,从数据库中读取到旧值(X = 1)
- 线程 A 将新值写入数据库(X = 2)
- 线程 B 将旧值写入缓存(X = 1)
- 最终 X 的值在缓存中是 1(旧值),在数据库中是 2(新值),发生不一致
可见,在高并发下这种方式容易出现长时间的脏数据,一般不建议使用
2. 先更新数据库,后删除缓存
先更新数据库,后删除缓存,如果缓存没有删除成功,数据库是最新值,缓存中是旧值,会发生不一致
再看两个线程并发读写数据:
- 某一时刻缓存中 X 失效不存在(数据库 X = 1)
- 线程 A 读取数据库,得到旧值(X = 1)
- 线程 B 更新数据库(X = 2)
- 线程 B 删除缓存
- 线程 A 将旧值写入缓存(X = 1)
- 最终 X 的值在缓存中是 1(旧值),在数据库中是 2(新值),发生不一致
这种方式依旧会出现数据不一致,但概率很低,所以普遍采用这种方式
更多优化
通过前面分析,我们采用了先更新数据库,再删除缓存的方式,还可以进一步优化
1. 保证两步都执行成功
前面提到,无论采用哪种方式,只要第二步失败都会有问题,所以我们需要保证第二步成功执行
一种简单的办法是失败就重试,但这会占用资源,并且立即重试大概率还是失败,所以可以采用异步重试,就是把重试请求写到消息队列,由专门的消费者来重试,直到成功
或者更直接的做法,为了避免第二步执行失败,我们可以把操作缓存这一步,直接放到消息队列中,由消费者来操作缓存,这样做的好处是即使系统重启了,消息也不会丢失
也可以通过订阅数据库变更日志,再操作缓存的方式,以 MySQL 举例,当一条数据发生修改时,MySQL 就会产生一条变更日志(Binlog),我们可以订阅这个日志,拿到具体操作的数据,然后再根据这条数据,去删除对应的缓存。订阅变更日志,目前也有了比较成熟的开源中间件,例如阿里的 canal
2. 延迟双删
一般数据库会使用【主从复制 + 读写分离】提高性能,这种情况下也有可能出现数据不一致:
- 线程 A 更新主库 X = 2(原值 X = 1)
- 线程 A 删除缓存
- 线程 B 查询缓存,没有命中,查询「从库」得到旧值(从库 X = 1)
- 从库「同步」完成(主从库 X = 2)
- 线程 B 将「旧值」写入缓存(X = 1)
- 最终 X 的值在缓存中是 1(旧值),在主从库中是 2(新值),也发生不一致
解决办法就是延时双删,比如线程 A 在更新数据库并删除缓存后,延迟一段时间再删除一次,延迟时间取决于主从复制的延迟时间,一般凭经验估算 1s - 5s 左右
Redis 数据一致性的更多相关文章
- redis缓存雪崩、缓存穿透、数据库和redis数据一致性
一.缓存雪崩 回顾一下我们为什么要用缓存(Redis):减轻数据库压力或尽可能少的访问数据库. 在前面学习我们都知道Redis不可能把所有的数据都缓存起来(内存昂贵且有限),所以Redis需要对数据设 ...
- Redis分布式集群几点说道
原文地址:http://www.cnblogs.com/verrion/p/redis_structure_type_selection.html Redis分布式集群几点说道 Redis数据量日益 ...
- Azure Redis Cache (1) 入门
<Windows Azure Platform 系列文章目录> Microsoft Azure Redis Cache基于流行的开源Redis Cache. 1.功能 Redis 是一种高 ...
- 【Redis】Redis分布式集群几点说道
Redis数据量日益增大,使用的公司越来越多,不仅用于做缓存,同时趋向于存储这一块,这样必促使集群的发展,各个公司也在收集适合自己的集群方案,目前行业用的比较多的是下面几种集群架构,大部分都是采用分片 ...
- redis缓存介绍以及常见问题浅析
# 没缓存的日子: 对于web来说,是用户量和访问量支持项目技术的更迭和前进.随着服务用户提升.可能会出现一下的一些状况: 页面并发量和访问量并不多,mysql足以支撑自己逻辑业务的发展.那么其实可以 ...
- 002 Redis使用及API
Redis的使用及相关API 1.作用: 提高查询效率 一定程度上可以减轻数据库服务器的冲击压力,从而保护了数据库 //1.是否包含key redisTemplate.hasKey(key) //2. ...
- php架构之路
鉴于最近跟小伙伴聊了很多PHP架构发展方向的问题,相关技术整理了一下,也顺便规划了一下自己的2019年. 一.常用的设计模式以及使用场景 以下是我用到过的 工厂,单例,策略,注册,适配,观察者,原 ...
- redis客户端、分布式锁及数据一致性
Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...
- redis系列之数据库与缓存数据一致性解决方案
redis系列之数据库与缓存数据一致性解决方案 数据库与缓存读写模式策略 写完数据库后是否需要马上更新缓存还是直接删除缓存? (1).如果写数据库的值与更新到缓存值是一样的,不需要经过任何的计算,可以 ...
- 高并发下Redis如何保持数据一致性(避免读后写)
通常意义上我们说读后写是指针对同一个数据的先读后写,且写入的值依赖于读取的值. 关于这个定义要拆成两部分来看,一:同一个数据:二:写依赖于读.(记住这个拆分,后续会用到,记为定义一.定义二)只有当这两 ...
随机推荐
- 标子查询优化和改写SQL案例
京华开发一哥们找我优化条报表SQL,反馈执行时间很慢需要 18s 才能出结果,安排. -- 原SQL SELECT 2 AS TYPE, to_char(a."create_time&quo ...
- command_execution
前置知识 可以通过ping的TTL来判断系统的版本 判断了是Linux之后就使用Linux的连接命令来进行操作 这里直接全局搜索flag相关的文件 linux全局查询文件_linux全局查找某个文件- ...
- 【VMware vSAN】主机之间网络性能测试,提示“无法运行网络性能测试。请稍后重试。”的处理过程。
vSAN集群监控,有一个主动测试功能,里面可以针对vSAN主机进行虚拟机创建测试.网络性能测试等. 官方解释: 虚拟机创建测试通常需要 20 至 40 秒时间,在超时情况下最长需要 180 秒时间.将 ...
- ElasticSearch 命令执行漏洞
漏洞编号:CVE-2014-3120 漏洞详情 CVE编号 CVE-2014-3120 漏洞级别 中危6.8 标题 Elasticsearch默认配置允许动态脚本执行漏洞 披露时间 2014/07/2 ...
- selenium之下拉菜单列表定位
下拉菜单列表定位>>使用Select类定位 from selenium.webdriver.support.ui import Select #导入Select类 select=Selec ...
- 一文带你深入理解K8s-Pod的意义和原理
本文分享自华为云社区<深入理解K8s-Pod的意义和原理>,作者:breakDawn. 在Kubernetes概念中,有以下五种概念: 容器container:镜像管理的最小单位 生产任务 ...
- 一文读懂Spring框架中Bean的生命周期
我们先来聊聊bean的生命周期: bean的生命周期图: AbstractAutowireCapableBeanFactory的docreateBean()方法(简单描述): 1.可以根据源码的时候 ...
- 获取yml自定义内容的方式
yml内容 yml: login: name: zhangsan age: 18 pass: 123456 方式一: 创建实体类 @Configuration @ConfigurationProper ...
- Spark Core快速入门
Spark-core快速入门 一.简介 Apache spark是专门为大规模数据处理而设计的快速通用的计算模型,是一种类似于Mapreduce通用并行计算框架,与mapreduce不同的是,spar ...
- Java 在Excel中添加筛选器并执行筛选
以下内容介绍通过Java程序在Excel添加筛选器并执行筛选.程序需要使用Excel工具类库Free Spire.XLS for Java,本文中使用的是免费版,可在官网下载jar包,解压导入jar文 ...