Redis教程

Redis缓存介绍以及常见问题浅析

本文主要是介绍Redis缓存介绍以及常见问题浅析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

对于web来说,是用户量和访问量支持项目技术的更迭和前进。随着服务用户提升。可能会出现一下的一些状况:

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

问题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组数据(可能成百上千),每组一个x(n<20),求 x! ;

分析2:

  • 如果使用 递归 ,输入t组数据,每个位x,那么每次都要执行 X~i~
  • 当X~i~过大或者n过大都会造成不小的负担! 时间复杂度 为 O(n^2^)
  • 那么能否换个思想的。没错、是 打表 (也可以理解位 动态规划 )。打表常用于ACM算法中,常用于解决多组输入输出、图论搜索结果、路径储存问题。那么,对于这个求阶乘。我们只需要申请一个数组。每个数据为 前一个数据 * 当前index 。那么思想很明确啦!
import java.util.Scanner;
public class test3 {
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等非关系型就是基于内存存储。其效率差别很大。当然,缓存也分为本地缓存和服务端缓存,这里只讲redis的服务端缓存。
  • 举个例子。例如如果一个接口sql查询 需要2s 。你每次查询都会2s并且加载的时候都会等在,这个长期等待给用户的体验是 非常糟糕 的。而用户能够接受的往往是 第一次 的等待。如果你用了缓存技术。你第一次查询放到redis里面。然后数据再从redis返回给你。后面当你继续访问这个数据的时候。查询到redis中有备份,那么 不需要通过db 直接能 从redis 中获取数据。那么,你想想,从一个key value的Nosql中取一个value能要多久呢!
  • 所以对于像样的,有点规模的网站,缓存is necessary 的.redis也是必不可少的。并且服务端的缓存设计也是要根据业务有所区别的。也要防止占用内存过大,redis雪崩等问题。

需要注意的问题

  • 缓存使用不当会带来很多问题。所以需要对一些细节进行认真考量和设计。笔者对于分布式的经验并不是很丰富,就相对于笔者的眼中谈谈缓存设计不好会带来那些问题。

是否用缓存

  • 现在不少项目,为了缓存而缓存,然而缓存并不是适合所有场景,比如如果对 数据一致性 要求极高,又或者 数据频繁 更改而查询并不多。有的可以不需要缓存。因为如果使用redis缓存多多少少可能会遇到数据一致性问题。那你可以考虑使用redis做成分布式锁去锁sql的数据。同样如果频繁更新数据,那么redis能起到的作用就仅仅是多了一层中转站。反而 浪费资源 。使得传输过程臃肿。

过期策略选择

  • 大部分场景 不适合缓存一致存在 ,首先,你的sql数据库的内容可能很多就不说了,另外,返回给你的对象如果是完整的pojo对象还好,但是如果是使用不同参数各种关联查询出来的结果那么redis中会储存太多冷数据。占用资源而得不到销毁。我们学过 操作系统 也知道在计算机的 缓存实现 中有)先进先出的算法( FIFO );最近最少使用算法( LRU );最佳淘汰算法( OPT );最少访问页面算法( LFR )等磁盘调度算法。对于web开发也可以借鉴。根据时间来的FIFO是最好实现的。因为redis在 全局key 支持过期策略。
  • 而开发中可能还会遇到 其他问题 。比如过期时间的选择上,如果过久会导致数据聚集。而过少可能导致频繁查询数据库甚至可能会导致缓存雪崩等问题。
  • 所以,过期策略一定要设置。并且对于 关键key 一定要 小心谨慎设计 。

数据一致性问题★

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

read :从redis中读取,如果redis中没有,那么就从mysql中获取更新redis缓存。 该 流程图描述常规场景。一般没啥争议。

《Redis缓存介绍以及常见问题浅析》


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

《Redis缓存介绍以及常见问题浅析》


  • 更新数据库信息,再更新redis缓存。这是常规做法,缓存基于数据库,取自数据库。但是其中可能遇到一些问题。例如上述如果更新缓存失败(宕机等其他状况),将会使得数据库和redis 数据不一致 。 造成DB新数据,缓存旧数据 。

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

《Redis缓存介绍以及常见问题浅析》


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

存在的问题

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

《Redis缓存介绍以及常见问题浅析》



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

写3:延时双删策略

《Redis缓存介绍以及常见问题浅析》


  • 这个就是延时双删策略,能过缓解在 写2 中在更新mysql过程中有读的线程进入造成redis缓存与mysql数据不一致。方法就是 删除缓存 -> 更新缓存 -> 延时(几百ms)(可异步)再次删除缓存。即使在更新缓存途中发生 写2 的问题。造成数据不一致,但是延时(具体实间根据业务来,一般几百ms)再次删除也能很快的解决不一致。
  • 但是就写的方案其实还是有漏洞的,比如 第二次删除错误 、 多写多读高并发 情况下对mysql访问的压力等等。当然你可以选择用mq等消息队列异步解决。其实实际的解决很难顾及到万无一失,所以 不少大佬在设计 这一环节可能会因为 一些纰漏 会 被喷 。作为菜菜的笔者在这里就更不献丑了,策略只是提供大纲,具体设计还是需要自己团队实践和摸索。并且也对一致性的要求级别有所区别。

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

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

《Redis缓存介绍以及常见问题浅析》


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

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

缓存穿透、缓存雪崩和缓存击穿

如果不了解,可能对这几个概念都不了解,听着感觉太高大上,至少笔者刚开始是这么觉得,本文并不是详细介绍如何解决和完美解决,更主要的是认识和认知吧。

《Redis缓存介绍以及常见问题浅析》


《Redis缓存介绍以及常见问题浅析》


redis缓存穿透

《Redis缓存介绍以及常见问题浅析》


  • 理解重在 穿透 吧,也就是访问透过redis直接经过mysql,通常是一个不存在的 key ,在数据库查询为 null 。每次请求落在数据库、并且高并发。数据库扛不住会挂掉。

解决方案

验证

redis缓存雪崩

理解

  • 雪崩,就是某 东西蜂拥而至 的意思,像雪崩一样。在这里,就是redis缓存集体 大规模集体失效 ,在高并发情况下突然使得key大规模访问mysql,使得数据库崩掉。可以想象下国家 人口老年化 。以后那天人集中在70-80岁,就没人干活了。国家劳动力就造成压力。

《Redis缓存介绍以及常见问题浅析》



解决方案

随机数

redis缓存击穿

理解

《Redis缓存介绍以及常见问题浅析》


  • 击穿和穿透不同,穿透的意思是想法 绕过 redis去使得数据库崩掉。而击穿你可以理解为 正面刚 击穿,这种通常为大量并发对一个key进行大规模的读写操作。这个key在缓存失效期间大量请求数据库,对数据库造成太大压力使得数据库崩掉。就 比如 在秒杀场景下10000块钱的mac和100块的mac这个100块的那个订单肯定会被抢到爆。所以缓存击穿就是针对某个常用key大量请求导致数据库崩溃。

解决方案

  • 能够达到这种场景的公司其实不多,我也不清楚他们的具体处理方法,但是一个锁拦截请求总是能防止数据库崩掉吧。


这篇关于Redis缓存介绍以及常见问题浅析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!