Redis 缓存实战——缓存、数据库一致性问题分析与解决方案

引言

缓存与数据库一致性的问题自从出现了缓存概念后就一直被提及,它是一个缓存方案的先天缺陷,只要存在缓存,就势必会讨论缓存与数据库一致性的问题。

一致性问题还广泛存在于各种分布式存储场景中,如主从一致性等等。

本篇博客讨论和整理了缓存、数据库一致性问题的一些思路,在实际的缓存业务场景中,可以对技术实现的起到一定指导作用。

一、为什么会出现一致性问题

缓存作为应用程序与数据库之间的数据存储池,主要作用就是热数据备份的作用,它的主要目的就是提高热数据的查询效率。因此,在读取缓存方面,普遍的做法是没有疑问的,基本都是按照如下流程执行:

在写数据方面,却存在很多问题,其中一点就是——如何保证数据库的数据与缓存的数据保持一致。

二、强一致性、弱一致性、最终一致性

在一致性问题上,分为事务一致性和数据一致性,而缓存、数据库一致性很明显就是数据一致性。

其实只有两类数据一致性,强一致性和弱一致性。除此之外,所有其他的一致性都是弱一致性的特殊情况。

2.1 强一致性与弱一致性

强一致性又叫线性一致性,顾名思义,就是在多个数据源之间以同步的方式复制数据。

弱一致性,即复制是异步的。在时间中,我们通常使一个从库是同步的,而其他的则是异步的。如果这个的从库出现问题,则使另一个异步从库同步。

这样可以确保永远有两个节点拥有完整数据。主库和同步从库,这种配置成为半同步。

2.2 最终一致性

最终一致性表示不一致的状态可能会出现,但这也只是暂时的,不同的数据源会在未来某个时点保持同步。

而为缓存设置过期时间可以有效的保证最终一致性。

三、缓存更新策略

解决缓存、数据库一致性问题的方法,无非就是在更新数据库时尽可能的减少不一致的情况出现。

但只要存在缓存,就无法保证数据的强一致性,选择不同的缓存更新策略,也只是在弱一致性的前提下,尽可能保证数据的同步,或达到最终一致性。

从理论上来讲,共有四种更新策略:

  1. 先更新数据库,再更新缓存
  2. 先更新数据库,再删除缓存
  3. 先更新缓存,再更新数据库
  4. 先删除缓存,再更新数据库

一般情况下,为了保证数据的安全,都会第一时间将数据更新到数据库中,所以先操作缓存的方式往往不被采纳。

对于先更新数据库的情况,到底是选择更新缓存还是删除缓存呢?

3.1 先更库,更缓存

结论是,这种方案普遍不被采纳。

第一点原因,线程安全性,如果同时有请求A 和 请求 B 都要更新数据,那么会出现如下情况:

  1. 线程 A 更新了数据库
  2. 线程 B 更新了数据库
  3. 线程 B 更新了缓存
  4. 线程 A 更新了缓存

本来按照先后顺序,A的更新操作在 B 之前,最终也应该是缓存中存放 B 更新的数据,但是由于一些特殊原因,导致后更新数据的 B 先于 A 更新了缓存,就导致了脏数据。

第二点原因,如果是一个写多读少的场景,采用这种方案由于每次更新数据都要去更新缓存,但从缓存中查询的次数却寥寥无几,不仅没有起到提高查询性能的目的,反而还极大的浪费性能。

3.2 先更库,删缓存

该方案同样会导致数据不一致。例如,同时有一个请求 A 查询数据,另一个请求 B 更新数据,会产生如下情形:

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

上面的情况是有可能发生的,它产生的原因是由于 A 将取得的旧数据(在 B 更新完数据并执行了删除缓存操作之后)又放入到了缓存,即缓存中的数据是 A 之前取得的旧数据。

但是这种情况发生的概率很低,这是因为写库操作的速度要远低于读库的速度。正因为如此,请求 B 在将新数据写入到数据库后才会大概率在请求 A 将旧数据写入缓存之后执行删除缓存操作。

即大概率发生的情形应该是:

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

由于写库操作要更加缓慢,因此,大概率会在最后,在其他读操作的写入缓存执行后再将缓存删除,从而保证数据的一致性。

另外,更新缓存实际上可以看做是先删除再添加的两步操作,那么对于删缓存的操作,无非就是将添加缓存安排到了下次查询的时候,这也在查询情况较少的时候避免了频繁更新缓存的尴尬。

因此,先更库,再删除缓存是一种较为可行的解决一致性问题的方案。

四、先更库再删缓存的其他问题

如果缓存删除失败了怎么办?

的确,如果更新了数据库之后,由于特殊原因,导致缓存删除失败,缓存中依然保留了旧数据,那么同样存在不一致的问题。

这个时候,我们需要为业务系统增加全局的缓存失效时间,这样可以保证不一致也只是暂时的,即达到最终一致性的效果。

或者使用下面的解决方案,使用消息队列保存数据库更新后失败的缓存删除操作,再某一时点进行重试,其中 binlog 订阅程序可以选择现成的MySQL binlog订阅中间件 “canal”。

鸣谢:

《缓存与数据库的一致性问题解析》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558594.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 多线程 —— AQS 详解

引言 AQS 是AbstractQuenedSynchronizer 的缩写,抽象的队列式同步器,它是除了java自带的synchronized关键字之外的锁机制。是 JUC 下的重要组件。 相关产物有:ReentrantLock、CountDownLatch、Semaphore、ReadWriteLock等。 一、AQS的设计…

的主机名_如何在Mac 上更改电脑的名称或本地局域网主机名?

我们知道,一台电脑有其设定的具体名称,电脑的名称和本地主机名用于在本地网络上识别您的电脑。当我们需要自定义电脑名称或本地局域网主机名时,则需要对其进行更改。那我们该如何更改呢?有需要的小伙伴们快和小编一起来看看吧~更改…

dev控件swiftplot图滚动方法_无限轮播图使用Scroller就这么简单

前言这几天又拾起老本行,复习复习Android,才发现忘的差不多了,上午做了一个小Demo,配合Scroller做了一个轮播图,效果如下,但是不知为何,录制的GIF成这样,凑乎一下看看。原理是继承Vi…

JVM——CPU缓存架构与Java 内存模型

导航一、CPU缓存架构与一致性协议1.1 CPU缓存架构1.2 缓存行与伪共享问题1.3 MESI 缓存一致性协议1.4 伪共享的解决办法二、JMM Java 内存模型2.1 JMM 简介2.2 原子性、可见性、有序性2.3 八大内存交互操作2.4 happens-before 原则一、CPU缓存架构与一致性协议 1.1 CPU缓存架构…

白噪声检测_科学家尝试用智能扬声器的白噪声来监测婴儿的呼吸运动

华盛顿大学的一支研究团队,刚刚介绍了他们开发的一种新型智能扬声器技术。这种设备能够借助白噪声来安抚熟睡的婴儿,并监测他们的呼吸和运动。具体说来是,通过智能扬声器发出的白噪声,原型设备能够将之与生命体征监测仪的数据相匹…

最大值_285期 博最大值2路,已经箭在弦上!

往期数据P-5掉码 跨度 和尾 012断路 余数和 位数86072 1 8 4 200 断1路2 5 对214对 双双双79703 0 2 3 101 断2路2 4 对215对 单单单62386 0 4 1 020 断1路2 4 错216对 双双单71903 0 8 7 110 断2路2 5 错217对 单单单64838 0 4 8 012 来3路3 4 错218对 双双双02052 0 2 2 020 …

商品领域ddd_为 Gopher 打造 DDD 系列:领域模型-资源库

前言: 作为领域模型中最重要的环节之一的Repository,其通过对外暴露接口屏蔽了内部的复杂性,又有其隐式写时复制的巧妙代码设计,完美的将DDD中的Repository的概念与代码相结合!Repository资源库通常标识一个存储的区域…

mysql5.7主从全备恢复_Mysql5.7—运维常用备份方式(超全)

小生博客:http://xsboke.blog.51cto.com小生 Q Q:1770058260-------谢谢您的参考,如有疑问,欢迎交流一、 Mysqldump备份结合binlog日志恢复使用mysqldump进行全库备份,并使用binlog日志备份,还原时&#xf…

docker 运行容器_Docker之运行 Django 容器

首先此篇笔记默认你已经安装好了 Docker,并了解 Docker 的基础概念,诸如镜像、容器、以及他们之间的关系等。如果不太了解,等我回头了解清楚以后,可以再写一篇文章阐述一下。(狗头当然,对于这篇文章&#x…

mysql8.0与mysql7.0_MySQL 5.7 vs 8.0,哪个性能更牛?

测试mysql5.7和mysql8.0分别在读写,选定,只写模式下不同并发时的性能(tps,qps)最早测试使用版本为mysql5.7.22和mysql8.0.15sysbench测试前先重启mysql服务,并清除os的缓存(避免多次测试时命中缓存)每次进行测试都是新生成测试数据…

springmvc使用requestmapping无法访问控制类_研究人员称人类使用的新烟碱类杀虫剂让蜜蜂无法入睡...

来自布里斯托尔大学的科学家进行了研究,显示常见的杀虫剂可以阻止蜜蜂和苍蝇睡个好觉。就像人类一样,许多昆虫也需要睡眠才能正常工作。然而,如果它们接触过新烟碱类杀虫剂,它们的睡眠就会受到影响,新烟碱类杀虫剂是一…

linux 监控mysql脚本_Linux系统MySQL主从同步监控shell脚本

操作系统:CentOS系统目的:定时监控MySQL数据库主从是否同步,如果不同步,记录故障时间,并执行命令使主从恢复同步状态1、创建脚本文件vi /home/crontab/check_mysql_slave.sh #编辑,添加下面代码#!/bin/sh…

python协成_Python协程(上)

几个概念:event_loop 事件循环:程序开启一个无限的循环,程序员会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。coroutine 协程:协程对象,指一个使用async关键字定义的函数&#…

js父元素获取子元素img_css,前端_父标签div中包含一个子元素img标签,子元素div标签,为什么img要加上浮动,子元素div才会处于正常位置?,css,前端 - phpStudy...

父标签div中包含一个子元素img标签,子元素div标签,为什么img要加上浮动,子元素div才会处于正常位置?dom结构如图img加上float 子元素div显示正常。不加float div显示错位。附上我写的一个dome测试用的,大家可本地看下究…

android运营商获取本机号码_一键登录已成大势所趋,Android端操作指南来啦!

根据极光(Aurora Mobile)发布的《2019年Q2移动互联网行业数据研究报告》,2019年第二季度,移动网民人均安装APP总量已达56款。面对如此繁多的APP,想在用户的手机中占据一席之地,移动开发者们就不得不努力提升用户体验。而现实却是&…

spring批量写入mysql数据库_MyBatis-spring和spring JDBC批量插入Mysql的效率比较

工具框架用spring-batch,数据库是mysql(未做特殊优化)。比较数据框架mybatis和spring jdbc的插入效率。Mybatis三种实现:1、mybatis的官方写法Java代码publicvoidbatchInsert1(List poilist)throwsException {SqlSession sqlSession sqlSessionFactory.…

金额转换java_java金额转换

像商品价格,订单,结算都会涉及到一些金额的问题,为了避免精度丢失通常会做一些处理,常规的系统中金额一般精确到小数点后两位,也就是分;这样数据库在设计的时候金额就直接存储整型数据类型,前端…

java bloomfilter_爬虫技术之——bloom filter(含java代码)

在爬虫系统中,在内存中维护着两个关于URL的队列,ToDo队列和Visited队列,ToDo队列存放的是爬虫从已经爬取的网页中解析出来的即将爬取的URL,但是网页是互联的,很可能解析出来的URL是已经爬取到的,因此需要VI…

java php js_【javascript/PHP】当一个JavaScripter初次进入PHP的世界,他将看到这样的风景...

本文将从以下11点介绍javascript和PHP在基础语法和基本操作上的异同:1.数据类型的异同2.常量和变量的定义的不同,字符串连接运算符不同3.对象的创建方法的不同4.PHP与JS在变量声明提升和函数声明提升的差异5.var在JS和PHP中使用的差异6.PHP和JS在访问对象…

从零开始学java 框架_从零开始学 Java - 搭建 Spring MVC 框架

如果创建一个 Spring 项目Spring MVC 框架在 Java 的 Web 项目中应该是无人不知的吧,你不会搭建一个 Spring 框架?作为身为一个刚刚学习Java的我都会,如果你不会的话,那可真令人忧伤。1.在 MyEclipse 创建项目后,可以以…