Mysql和Redis数据如何保持一致
先阐明一下Mysql和Redis的关系:Mysql是数据库,用来持久化数据,一定程度上保证数据的可靠性;Redis是用来当缓存,用来提升数据访问的性能。
关于如何保证Mysql和Redis中的数据一致(即缓存一致性问题),这是一个非常经典的问题。
使用过缓存的人都应该知道,在实际应用场景中,要想实时刻保证缓存和数据库中的数据一样,很难做到。
基本上都是尽可能让他们的数据在绝大部分时间内保持一致,并保证最终是一致的。
缓存不一致是如何产生的
如果数据一直没有变更,那么就不会出现缓存不一致的问题。
通常缓存不一致是发生在数据有变更的时候。 因为每次数据变更你需要同时操作数据库和缓存,而他们又属于不同的系统,无法做到同时操作成功或失败,总会有一个时间差。在并发读写的时候可能就会出现缓存不一致的问题(理论上通过分布式事务可以保证这一点,不过实际上基本上很少有人这么做)。
虽然没办法在数据有变更时,保证缓存和数据库强一致,但对缓存的更新还是有一定设计方法的,遵循这些设计方法,能够让这个不一致的影响时间和影响范围最小化。
缓存更新的几种设计
缓存更新的设计方法大概有以下四种:
- 先删除缓存,再更新数据库(这种方法在并发下最容易出现长时间的脏数据,不可取)
- 先更新数据库,删除缓存(Cache Aside Pattern)
- 只更新缓存,由缓存自己同步更新数据库(Read/Write Through Pattern)
- 只更新缓存,由缓存自己异步更新数据库(Write Behind Cache Pattern)
接下来详细介绍一些这四种设计方法
先删除缓存,再更新数据库
这种方法在并发读写的情况下容易出现缓存不一致的问题
如上图所示,其可能的执行流程顺序为:
- 客户端1 触发更新数据A的逻辑
- 客户端2 触发查询数据A的逻辑
- 客户端1 删除缓存中数据A
- 客户端2 查询缓存中数据A,未命中
- 客户端2 从数据库查询数据A,并更新到缓存中
- 客户端1 更新数据库中数据A
可见,最后缓存中的数据A跟数据库中的数据A是不一致的,缓存中的数据A是旧的脏数据。
因此一般不建议使用这种方式。
先更新数据库,再让缓存失效
这种方法在并发读写的情况下,也可能会出现短暂缓存不一致的问题
如上图所示,其可能执行的流程顺序为:
- 客户端1 触发更新数据A的逻辑
- 客户端2 触发查询数据A的逻辑
- 客户端3 触发查询数据A的逻辑
- 客户端1 更新数据库中数据A
- 客户端2 查询缓存中数据A,命中返回(旧数据)
- 客户端1 让缓存中数据A失效
- 客户端3 查询缓存中数据A,未命中
- 客户端3 查询数据库中数据A,并更新到缓存中
可见,最后缓存中的数据A和数据库中的数据A是一致的,理论上可能会出现一小段时间数据不一致,不过这种概率也比较低,大部分的业务也不会有太大的问题。
只更新缓存,由缓存自己同步更新数据库(Read/Write Through Pattern)
这种方法相当于是业务只更新缓存,再由缓存去同步更新数据库。 一个Write Through的 例子如下:
如上图所示,其可能执行的流程顺序为:
- 客户端1 触发更新数据A的逻辑
- 客户端2 触发查询数据A的逻辑
- 客户端1 更新缓存中数据A,缓存同步更新数据库中数据A,再返回结果
- 客户端2 查询缓存中数据A,命中返回
Read Through 和 WriteThrough 的流程类似,只是在客户端查询数据A时,如果缓存中数据A失效了(过期或被驱逐淘汰),则缓存会同步去数据库中查询数据A,并缓存起来,再返回给客户端
这种方式缓存不一致的概率极低,只不过需要对缓存进行专门的改造。
只更新缓存,由缓存自己异步更新数据库(Write Behind Cache Pattern)
这种方式性详单于是业务只操作更新缓存,再由缓存异步去更新数据库,例如:
如上图所示,其可能的执行流程顺序为:
- 客户端1 触发更新数据A的逻辑
- 客户端2 触发查询数据A的逻辑
- 客户端1 更新缓存中的数据A,返回
- 客户端2 查询缓存中的数据A,命中返回
- 缓存异步更新数据A到数据库中
这种方式的优势是读写的性能都非常好,基本上只要操作完内存后就返回给客户端了,但是其是非强一致性,存在丢失数据的情况。
如果在缓存异步将数据更新到数据库中时,缓存服务挂了,此时未更新到数据库中的数据就丢失了。
总结
上面讲到的几种缓存更新的设计方式,都是前人总结出来的经验,这些方式或多或少都有一些弊端,并不完美,实际上也很难有完美的设计。 大家在做系统设计的时候,也不要去追求完美,要有一些取舍,找到一种最适合自己业务场景的方式就行
作者:追光者
Mysql和Redis数据如何保持一致的更多相关文章
- 转载:MySQL和Redis 数据同步解决方案整理
from: http://blog.csdn.net/langzi7758521/article/details/52611910 最近在做一个Redis箱格信息数据同步到数据库Mysql的功能. 自 ...
- Mysql和Redis数据同步策略
为什么对缓存只删除不更新 不更新缓存是防止并发更新导致的数据不一致. 所以为了降低数据不一致的概率,不应该更新缓存,而是直接将其删除, 然后等待下次发生cache miss时再把数据库中的数据同步到缓 ...
- 采用lua脚本获取mysql、redis数据以及jwt的校验
一.安装配置Openresty 1,安装 wget https://openresty.org/download/ngx_openresty-1.9.7.1.tar.gz # 下载 tar xzvf ...
- 通过Gearman实现MySQL到Redis的数据同步
对于变化频率非常快的数据来说,如果还选择传统的静态缓存方式(Memocached.File System等)展示数据,可能在缓存的存取上会有很大的开销,并不能很好的满足需要,而Redis这样基于内存的 ...
- redis作为mysql的缓存服务器(读写分离,通过mysql触发器实现数据同步)
一.redis简介Redis是一个key-value存储系统.和Memcached类似,为了保证效率,数据都是缓存在内存中.区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录 ...
- JAVA通过Gearman实现MySQL到Redis的数据同步(异步复制)
MySQL到Redis数据复制方案 无论MySQL还是Redis,自身都带有数据同步的机制,像比较常用的 MySQL的Master/Slave模式 ,就是由Slave端分析Master的binlog来 ...
- Gearman + Nodejs + MySQL UDF异步实现 MySQL 到 Redis 的数据同步
[TOC] 1, 环境 CentOS, MySQL, Redis, Nodejs 2, Redis简介 Redis是一个开源的K-V内存数据库,它的key可以是string/set/hash/list ...
- 在这个应用中,我使用了 MQ 来处理异步流程、Redis 缓存热点数据、MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ、MySQL 持久化的数据也会存在于一个分布式文件系统中,他们之间的调用也是需要用 RPC 来完成数据交互的。
在这个应用中,我使用了 MQ 来处理异步流程.Redis 缓存热点数据.MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ.MySQ ...
- Docker-compose封装mysql并初始化数据以及redis
一.概述 现有一台服务器,需要部署mysql和redis.其中mysql容器,需要在第一次启动时,执行sql文件. redis保持空数据即可. 关于Docker-compose的安装,请参考连接: h ...
随机推荐
- 『忘了再学』Shell基础 — 25、扩展正则表达式
目录 1.扩展正则表达式说明 2.练习 (1)+和?练习 (2)|和()练习 3.注意(重点) 1.扩展正则表达式说明 熟悉正则表达式的童鞋应该很疑惑,在其他的语言中是没有扩展正则表达式说法的,在Sh ...
- 最短路之 Dijkstra 算法
普通的 Dijkstra 这是一种运用贪心的单源最短路算法,就是求从一个节点出发,到任意一个点的最短距离 首先我们要一个图 假设要求从 1 开始的单源最短路 dis[] 表示最短路数组, vis[] ...
- VTK 在WINDOWS上的安装使用
参考:http://www.vtk.org/Wiki/VTK/Building/Windows#Step_5_-_Open_the_Visual_Studio_project
- 李呈祥:bilibili在湖仓一体查询加速上的实践与探索
导读: 本文主要介绍哔哩哔哩在数据湖与数据仓库一体架构下,探索查询加速以及索引增强的一些实践.主要内容包括: 什么是湖仓一体架构 哔哩哔哩目前的湖仓一体架构 湖仓一体架构下,数据的排序组织优化 湖仓一 ...
- DevStream 成为 CNCF Sandbox 项目啦!- 锣鼓喧天、鞭炮齐鸣、红旗招展、忘词了。
开局两张图,内容全靠"编" 来,有图有真相! DevStream ️ CNCF DevStream joins CNCF Sandbox CNCF Cloud Native Int ...
- Python中的类变量和成员变量
类变量 定义 在类里面定义,且不位于构造或者成员函数里面,同时没有" self. " 的前缀:或者在类外定义,用 类名.类变量名 来新增加类变量. 使用 两种使用方法: (1)类名 ...
- Tapdata PDK 生态共建计划启动!Doris、OceanBase、PolarDB、SequoiaDB 等十余家厂商首批加入
2022年4月7日,Tapdata 正式启动 PDK 插件生态共建计划,致力于全面连接数据孤岛,加速构建更加开放的数据生态,以期让各行各业的使用者都能释放数据的价值,随时获取新鲜的数据.截至目前, ...
- loguru备忘
loguru是个非常好用的三方日志管理包,简单易用,奈何老是记不住,在这记录一下吧 #coding:utf-8 ''' @version: python3.8 @author: 'eric' @lic ...
- 使用xpath查找元素的子元素,找不到
List<WebElement> allUserList = driver.findElements(By.xpath("xxx")); for (WebElement ...
- 2022-07-13 第六组 润土 Java01学习笔记
1.数据类型: 基本数据类型: 整型: byte 字节型 -128-127 1个字节 short 短整型 2个字节 int 整型 4个字节 long 长整型 8个字节 浮点型: float 单精度 4 ...