您当前的位置:首页 > 电脑百科 > 数据库 > Redis

八张图了解Redis和MySQL数据一致性问题

时间:2020-11-10 10:50:27  来源:  作者:

前言

首发公众号:bigsai 头条号:程序员bigsai 还请关注、一键三连!

对于Web来说,用户量和访问量在一定程度上推动项目技术和架构的更迭和进步。可能会有以下的一些状况:

  1. 页面并发量和访问量并不多,MySQL足以支撑自己逻辑业务的发展。那么其实可以不加缓存。最多对静态页面进行缓存即可。
  2. 页面的并发量显著增多,数据库有些压力,并且有些数据更新频率较低反复被查询或者查询速度较慢。那么就可以考虑使用缓存技术优化。对高命中的对象存到key-value形式的redis中,那么,如果数据被命中,那么可以不经过效率很低的db。从高效的redis中查找到数据。
  3. 当然,可能还会遇到其他问题,你还通过静态页面缓存页面、cdn加速、甚至负载均衡这些方法提高系统并发量。这里就不做介绍。
「图解」八张图了解Redis和MySQL数据一致性问题

 

缓存思想无处不在

我们从一个算法问题开始了解缓存的意义。

问题1:

  • 输入一个数n(n<20),求n!;

分析1

  • 单单考虑算法,不考虑数值越界问题。 当然我们知道n!=n * (n-1) * (n-2) * ... * 1= n * (n-1)!; 那么我们可以用一个递归函数解决问题。
static long jiecheng(int n)
{
 if(n==1||n==0)return 1;
 else {
   return n*jiecheng(n-1);
 }
}

这样每输入求一次需要执行n次。 问题2:

  • 输入t组数据(可能成百上千),每组一个xi(xi<20),求xi!;

分析2

  • 如果使用递归,输入t组数据,每次输入为xi,那么每次都要执行次数为: 当每次输入的Xi过大或者t过大都会造成不小的负担!时间复杂度为O(n2)
  • 那么能否换个思想的。没错、是打表。打表常用于ACM算法中,常用于解决多组输入输出、图论搜索结果、路径储存问题。那么,对于这个求阶乘。我们只需要申请一个数组,按照编号从前往后将在需求的数存到数组中,后面再取得时候直接输出数组值就可以,思想很明确吧:
import JAVA.util.Scanner;
public class test {
public static void main(String[] args) {
 // TODO Auto-generated method stub
 Scanner sc=new Scanner(System.in);
 int t=sc.nextInt();
 long jiecheng[]=new long[21];
 jiecheng[0]=1;
 for(int i=1;i<21;i++)
 {
  jiecheng[i]=jiecheng[i-1]*i;
 }
   for(int i=0;i<t;i++) {
  int x=sc.nextInt();
  System.out.println(jiecheng[x]);
 }
}  
}
  • 时间复杂度才O(n)。这里的思想就和缓存思想差不多。先将数据在jiecheng[21]数组中储存。执行一次计算。当后面继续访问的时候就相当于访问静态数组值。每次都为O(1的操作)。

缓存的应用场景

缓存适用于高并发的场景,提升服务容量。主要是将从经常被访问的数据或者查询成本较高从慢的介质中存到比较快的介质中,比如从硬盘—>内存。我们知道大多数关系数据库是基于硬盘读写的,其效率和资源有限,而redis是基于内存的,其读写速度差别差别很大。当并发过高关系数据库性能达到瓶颈时候,就可以策略性将常访问数据放到Redis提高系统吞吐和并发量。

对于常用网站和场景,关系数据库主要可能放在两个地方:

  • 读写IO性能较差
  • 一个数据可能通过较大量计算得到

所以使用缓存能够减少磁盘IO次数和关系数据库的计算次数。读取上速度快也从两个方面体现:

  • 基于内存,读写较快
  • 使用哈希算法直接定位结果不需要计算

所以对于像样的,有点规模的网站,缓存是很 necessary的,而Redis无疑是最好的选择之一。

「图解」八张图了解Redis和MySQL数据一致性问题

 

需要注意的问题

缓存使用不当会带来很多问题。所以需要对一些细节进行认真考量和设计。当然最难得数据一致性在下面单独分析。

是否用缓存

项目不能为了用缓存而用缓存,缓存并一定适合所有场景,如果对数据一致性要求极高,又或者数据频繁更改而查询并不多,又或者根本没并发量的、查询简单的不一定需要缓存,还可能浪费资源使得项目变得臃肿难维护,并且使用redis缓存多多少少可能会遇到数据一致性问题需要考虑。

缓存合理设计

在设计缓存的时候,很可能会遇到多表查询,如果遇到多表查询缓存的键值对就需要合理考虑,是拆分还是合在一起?当然如果组合种类多但常出现的不多也可以直接缓存,具体的设计要根据项目业务需求来看,并没有一个非常绝对的标准。

过期策略选择

  • 缓存装的是相对热点和常用的数据,Redis资源也是有限,需要选择一个合理的策略让缓存过期删除。我们学过操作系统也知道在计算机的缓存实现中有先进先出的算法(FIFO);最近最少使用算法(LRU);最佳淘汰算法(OPT);最少访问页面算法(LFR)等磁盘调度算法。设计Redis缓存时候也可以借鉴。根据时间来的FIFO是最好实现的。且Redis在全局key支持过期策略。
  • 并且过期时间也要根据系统情况合理设置,如果硬件好点当前可以稍微久一点,但是过期时间过久或者过短可能都不太好,过短可能缓存命中率不高,而过久很可能造成很多冷门数据存储在Redis中不释放。

数据一致性问题★

上面其实提到数据一致性问题。如果对一致性要求极高那么不建议使用缓存。下面稍微梳理一下缓存的数据。 在Redis缓存中经常会遇到数据一致性问题。对于一个缓存,下面罗列几种情况:

read:从Redis中读取,如果Redis中没有,那么就从MySQL中获取更新Redis缓存。 下面流程图描述常规场景,没啥争议:

「图解」八张图了解Redis和MySQL数据一致性问题

 

写1:先更新数据库,再更新缓存(普通低并发)

「图解」八张图了解Redis和MySQL数据一致性问题

 

更新数据库信息,再更新Redis缓存。这是常规做法,缓存基于数据库,取自数据库。

但是其中可能遇到一些问题,例如上述如果更新缓存失败(宕机等其他状况),将会使得数据库和Redis数据不一致。造成DB新数据,缓存旧数据

写2:先删除缓存,再写入数据库(低并发优化)

「图解」八张图了解Redis和MySQL数据一致性问题

 

解决的问题

这种情况能够有效避免写1中防止写入Redis失败的问题。将缓存删除进行更新。理想是让下次访问Redis为空去MySQL取得最新值到缓存中。但是这种情况仅限于低并发的场景中而不适用高并发场景。

存在的问题

写2虽然能够看似写入Redis异常的问题。看似较为好的解决方案但是在高并发的方案中其实还是有问题的。我们在写1讨论过如果更新库成功,缓存更新失败会导致脏数据。我们理想是删除缓存让下一个线程访问适合更新缓存。问题是:如果这下一个线程来的太早、太巧了呢?

「图解」八张图了解Redis和MySQL数据一致性问题

 

因为多线程你也不知道谁先谁后,谁快谁慢。如上图所示情况,将会出现Redis缓存数据和MySQL不一致。当然你可以对key进行上锁。但是锁这种重量级的东西对并发功能影响太大,能不用锁就别用!上述情况就高并发下依然会造成缓存是旧数据,DB是新数据。并且如果缓存没有过期这个问题会一直存在。

写3:延时双删策略

「图解」八张图了解Redis和MySQL数据一致性问题

 

这个就是延时双删策略,能过缓解在写2中在更新MySQL过程中有读的线程进入造成Redis缓存与MySQL数据不一致。方法就是删除缓存->更新缓存->延时(几百ms)(可异步)再次删除缓存。即使在更新缓存途中发生写2的问题。造成数据不一致,但是延时(具体时间根据业务来,一般几百ms)再次删除也能很快的解决不一致。

但是就写的方案其实还是有漏洞的,比如第二次删除错误、多写多读高并发情况下对MySQL访问的压力等等。当然你可以选择用MQ等消息队列异步解决。其实实际的解决很难顾及到万无一失,所以不少大佬在设计这一环节可能会因为一些纰漏会被喷。作为菜菜的笔者在这里就更不献丑了,各位大佬欢迎贡献你们的方案。

写4:直接操作缓存,定期写入sql(适合高并发)

当有一堆并发(写)扔过来的后,前面几个方案即使使用消息队列异步通信但也很难给用户一个舒适的体验。并且对大规模操作sql对系统也会造成不小的压力。所以还有一种方案就是直接操作缓存,将缓存定期写入sql。因为Redis这种非关系数据库又基于内存操作KV相比传统关系型要快很多。

「图解」八张图了解Redis和MySQL数据一致性问题

 

上面适用于高并发情况下业务设计,这个时候以Redis数据为主,MySQL数据为辅助。定期插入(好像数据备份库一样)。当然,这种高并发往往会因为业务对读、写得顺序等等可能有不同要求,可能还要借助消息队列以及锁完成针对业务上对数据和顺序可能会因为高并发、多线程带来的不确定性和不稳定性,提高业务可靠性。

总之,越是高并发、越是对数据一致性要求高的方案在数据一致性的设计方案需要考虑和顾及的越复杂、越多。上述也是笔者针对Redis数据一致性问题的学习和自我发散(胡扯)学习。如果有解释理解不合理或者还请各位大佬指正!



Tags:数据一致性   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
作为一名程序员,你是不是经常在很多场景,例如看博客、聊天吹水等等时候听到这样一个词"系统数据一致性",是不是有时候感觉到了迷糊,不知道这个"系统数据一致性"到底是在说什么?其...【详细内容】
2021-02-18  Tags: 数据一致性  点击:(198)  评论:(0)  加入收藏
前言首发公众号:bigsai 头条号:程序员bigsai 还请关注、一键三连!对于Web来说,用户量和访问量在一定程度上推动项目技术和架构的更迭和进步。可能会有以下的一些状况: 页面并发量...【详细内容】
2020-11-10  Tags: 数据一致性  点击:(142)  评论:(0)  加入收藏
以下文章来源于波波微课 ,作者架构师杨波介绍系统架构微服务化以后,根据微服务独立数据源的思想,每个微服务一般具有各自独立的数据源,但是不同微服务之间难免需要通过数据分发...【详细内容】
2020-08-04  Tags: 数据一致性  点击:(141)  评论:(0)  加入收藏
转载本文需注明出处:微信公众号EAWorld,违者必究。针对分布式架构下的数据一致性,大家也许会问这样的问题:跨系统间分布式事务如何解决?系统内多个服务的分布式事务如何解决?一个...【详细内容】
2020-05-09  Tags: 数据一致性  点击:(49)  评论:(0)  加入收藏
一、数据库架构原则 高可用 高性能 一致性 扩展性二、常见的数据库架构方案方案一:主备架构,只有主库提供读写服务,备库冗余作故障转移用 jdbc:mysql://vip:3306/xxdb1、 高...【详细内容】
2019-09-16  Tags: 数据一致性  点击:(195)  评论:(0)  加入收藏
在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节。所以,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问MySQL等数据库。 这个业务场景...【详细内容】
2019-08-26  Tags: 数据一致性  点击:(425)  评论:(0)  加入收藏
▌简易百科推荐
来源: my.oschina.net/xiaomu0082/blog/2990388首先说下问题现象:内网sandbox环境API持续1周出现应用卡死,所有api无响应现象刚开始当测试抱怨环境响应慢的时候 ,我们重启一下应...【详细内容】
2021-12-08  Java识堂    Tags:Redis   点击:(16)  评论:(0)  加入收藏
我不知道为什么你会选择对特定数量的“错误”(或警告)如此具体。听起来您正在寻找将要发布到 Yahoo! 的某些文章的内容。 Insider (N Foos to Blah for the BlahBlah)。那说:...【详细内容】
2021-12-07  富集云科技有限公司    Tags:Redis   点击:(14)  评论:(0)  加入收藏
目录 一、背景 二、步骤 0.理论支持 1、获取数据 2、结果 3、分析数据并评估大小 三、关于repl-backlog-size 一、背景 repl-backlog-size控制这个环形缓冲区. ​ 主从断...【详细内容】
2021-11-05  弈秋的美好生活    Tags:redis   点击:(41)  评论:(0)  加入收藏
Redis 性能测试是通过同时执行多个命令实现的。1,Redis-benchmarkRedis性能命令:redis性能命令格式: redis-benchmark [option] [option value] redis 性能测试工具可选参数如...【详细内容】
2021-11-02  川石信息    Tags:Redis   点击:(41)  评论:(0)  加入收藏
1 概述数据结构和内部编码 无传统关系型数据库的 Table 模型schema 所对应的db仅以编号区分。同一 db 内,key 作为顶层模型,它的值是扁平化的。即 db 就是key的命名空间。 key...【详细内容】
2021-11-01  JavaEdge    Tags:Redis   点击:(28)  评论:(0)  加入收藏
普通java中使用引用Java redis 驱动,即可连接:import redis.clients.jedis.Jedis; public class RedisTestJava { public static void main(String[] args) { //连...【详细内容】
2021-10-13  faesuite    Tags:Redis   点击:(34)  评论:(0)  加入收藏
Redis常用的数据结构有 string list set zset hashstringstring 是 Redis 的基本的数据类型,一个 key 对应一个 value。string 类型是二进制安全的,Redis的string可以包含任...【详细内容】
2021-10-12  语霖    Tags:Redis   点击:(36)  评论:(0)  加入收藏
列表类型可以存储一组按插入顺序排序的字符串,它非常灵活,支持在两端插入、弹出数据,可以充当栈和队列的角色。> LPUSH fruit apple(integer) 1> RPUSH fruit banana(integer)...【详细内容】
2021-09-17  深夜敲代码    Tags:Redis   点击:(54)  评论:(0)  加入收藏
Redis持久化意义 是做灾难恢复,数据恢复,也可以归类到高可用的一个环节里面去,比如你的redis整个挂了,然后redis就不可用了,你要做的事情是让redis变得可用,尽快变得可用 大量的请...【详细内容】
2021-08-12  小李说IT    Tags:Redis   点击:(77)  评论:(0)  加入收藏
当查询Redis中没有的数据时,该查询会下沉到数据库层,同时数据库层也没有该数据,当这种情况大量出现或被恶意攻击时,接口的访问全部透过Redis访问数据库,而数据库中也没有这些数据...【详细内容】
2021-07-30  随便t    Tags:缓存穿透   点击:(90)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条