概述

当我们在使用缓存时,如果发生数据变更,那么你需要同时操作缓存和数据库,而它们两个又分属不同的系统,因此无法做到同时操作成功或失败,因此在并发读写下很可能出现缓存与数据库数据不一致的情况

理论上可以通过分布式事务保证同时操作成功或失败,但这会影响系统性能,一般很少使用。虽然没办法做到缓存和数据库强一致,但我们可以让他们的数据尽可能在绝大部分时间内保持一致,并保证最终是一致的

缓存更新设计

一般来说都是采用删除缓存的方式更新缓存,这就涉及到先删除缓存还是先更新数据库的顺序问题了

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 数据一致性的更多相关文章

  1. redis缓存雪崩、缓存穿透、数据库和redis数据一致性

    一.缓存雪崩 回顾一下我们为什么要用缓存(Redis):减轻数据库压力或尽可能少的访问数据库. 在前面学习我们都知道Redis不可能把所有的数据都缓存起来(内存昂贵且有限),所以Redis需要对数据设 ...

  2. Redis分布式集群几点说道

    原文地址:http://www.cnblogs.com/verrion/p/redis_structure_type_selection.html  Redis分布式集群几点说道 Redis数据量日益 ...

  3. Azure Redis Cache (1) 入门

    <Windows Azure Platform 系列文章目录> Microsoft Azure Redis Cache基于流行的开源Redis Cache. 1.功能 Redis 是一种高 ...

  4. 【Redis】Redis分布式集群几点说道

    Redis数据量日益增大,使用的公司越来越多,不仅用于做缓存,同时趋向于存储这一块,这样必促使集群的发展,各个公司也在收集适合自己的集群方案,目前行业用的比较多的是下面几种集群架构,大部分都是采用分片 ...

  5. redis缓存介绍以及常见问题浅析

    # 没缓存的日子: 对于web来说,是用户量和访问量支持项目技术的更迭和前进.随着服务用户提升.可能会出现一下的一些状况: 页面并发量和访问量并不多,mysql足以支撑自己逻辑业务的发展.那么其实可以 ...

  6. 002 Redis使用及API

    Redis的使用及相关API 1.作用: 提高查询效率 一定程度上可以减轻数据库服务器的冲击压力,从而保护了数据库 //1.是否包含key redisTemplate.hasKey(key) //2. ...

  7. php架构之路

    鉴于最近跟小伙伴聊了很多PHP架构发展方向的问题,相关技术整理了一下,也顺便规划了一下自己的2019年. 一.常用的设计模式以及使用场景 以下是我用到过的   工厂,单例,策略,注册,适配,观察者,原 ...

  8. redis客户端、分布式锁及数据一致性

    Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...

  9. redis系列之数据库与缓存数据一致性解决方案

    redis系列之数据库与缓存数据一致性解决方案 数据库与缓存读写模式策略 写完数据库后是否需要马上更新缓存还是直接删除缓存? (1).如果写数据库的值与更新到缓存值是一样的,不需要经过任何的计算,可以 ...

  10. 高并发下Redis如何保持数据一致性(避免读后写)

    通常意义上我们说读后写是指针对同一个数据的先读后写,且写入的值依赖于读取的值. 关于这个定义要拆成两部分来看,一:同一个数据:二:写依赖于读.(记住这个拆分,后续会用到,记为定义一.定义二)只有当这两 ...

随机推荐

  1. 标子查询优化和改写SQL案例

    京华开发一哥们找我优化条报表SQL,反馈执行时间很慢需要 18s 才能出结果,安排. -- 原SQL SELECT 2 AS TYPE, to_char(a."create_time&quo ...

  2. command_execution

    前置知识 可以通过ping的TTL来判断系统的版本 判断了是Linux之后就使用Linux的连接命令来进行操作 这里直接全局搜索flag相关的文件 linux全局查询文件_linux全局查找某个文件- ...

  3. 【VMware vSAN】主机之间网络性能测试,提示“无法运行网络性能测试。请稍后重试。”的处理过程。

    vSAN集群监控,有一个主动测试功能,里面可以针对vSAN主机进行虚拟机创建测试.网络性能测试等. 官方解释: 虚拟机创建测试通常需要 20 至 40 秒时间,在超时情况下最长需要 180 秒时间.将 ...

  4. ElasticSearch 命令执行漏洞

    漏洞编号:CVE-2014-3120 漏洞详情 CVE编号 CVE-2014-3120 漏洞级别 中危6.8 标题 Elasticsearch默认配置允许动态脚本执行漏洞 披露时间 2014/07/2 ...

  5. selenium之下拉菜单列表定位

    下拉菜单列表定位>>使用Select类定位 from selenium.webdriver.support.ui import Select #导入Select类 select=Selec ...

  6. 一文带你深入理解K8s-Pod的意义和原理

    本文分享自华为云社区<深入理解K8s-Pod的意义和原理>,作者:breakDawn. 在Kubernetes概念中,有以下五种概念: 容器container:镜像管理的最小单位 生产任务 ...

  7. 一文读懂Spring框架中Bean的生命周期

    我们先来聊聊bean的生命周期: bean的生命周期图: AbstractAutowireCapableBeanFactory的docreateBean()方法(简单描述):  1.可以根据源码的时候 ...

  8. 获取yml自定义内容的方式

    yml内容 yml: login: name: zhangsan age: 18 pass: 123456 方式一: 创建实体类 @Configuration @ConfigurationProper ...

  9. Spark Core快速入门

    Spark-core快速入门 一.简介 Apache spark是专门为大规模数据处理而设计的快速通用的计算模型,是一种类似于Mapreduce通用并行计算框架,与mapreduce不同的是,spar ...

  10. Java 在Excel中添加筛选器并执行筛选

    以下内容介绍通过Java程序在Excel添加筛选器并执行筛选.程序需要使用Excel工具类库Free Spire.XLS for Java,本文中使用的是免费版,可在官网下载jar包,解压导入jar文 ...