一文搞懂redis(其实很简单)

概述:现在redis已经成为必问的面试题了,搞懂redis非常重要,以下内容是个人对redis的理解和总结。

文章目录

  • 以前淘宝和12306以及京东崩溃的现象
  • Nosql简介
  • 为什么要用redis?
  • HashMap原理
  • redis是什么?
  • redis单线程机制
  • redis持久化
  • redis的5种数据类型
  • 单指令数据操作和多指令数据操作的选择
  • redis事务
  • redis删除策略
  • redis高级数据类型
  • redis主从复制
  • redis哨兵
  • redis集群
  • redis缓存预热
  • redis缓存雪崩
  • redis缓存击穿
  • redis缓存穿透
  • redis性能监控
  • redis常见应用场景

以前淘宝和12306以及京东崩溃的现象

问题现象:
这些大网站都有海量用户,并且在双十一和六一八和春运的时候出现高并发的现象,海量用户同时访问网站再进行数据访问的操作,网站就扛不住了。

罪魁祸首:
关系型数据库导致的,原因有如下几点:

1.学过计算机组成原理的同学应该都知道cpu访问的速度排序是
寄存器>高速缓存(cache)>内存>硬盘(不能直接访问硬盘,需要将硬盘的内容复制到内存中才能够访问),而关系型数据库的数据是存在硬盘里的,所以我们不难知道如果数据访问是去关系型数据库里面的话,速度就是最慢的

2.关系型数据库不光是存储数据,它更多的还要表示数据关系,数据关系复杂,扩展性差,不便于大规模集群

解决思路:
1.降低磁盘IO次数,越少越好 --------------内存存储

2.尽量去除数据之间的关系,越简单越好 -------------不存储关系,仅存储数据。

而这两点就是Nosql

Nosql简介

NoSQL:Not-Only-SQL(泛指菲关系型的数据库),是对关系型数据库的补充(记住只是补充,去解决它不能解决的场景)

作用:应对基于海量用户和海量数据前提下的数据处理问题

特征:
1.可扩容,可伸缩
2.大数据量下保持高性能
3.灵活的数据模型
4.高可用

常见的Nosql数据库:
1.Redis
2.memcache
3.HBase
4.MongoDB

为什么要用redis?

上面的内容大家已经看到了cpu访问的速度排序是
寄存器>高速缓存(cache)>内存>硬盘,而redis是基于内存存储的数据库,所以显然cpu对它的访问速度比对关系型数据库(例如mysql等等)的快多了,
并且它还不表述数据关系,它使用键值对的形式来表示数据, 类似HashMap,而Mysql是用b+tree的数据结构来存储数据的。

HashMap查找的时间复杂度为O(1)级别 (下面马上解释原理)

理论上,B+树的查找时间复杂度为O(log(1.44)n) (b+树,博主还未了解原理,日后再更)

所以两者的比较redis性能的优点就很明显了!

但是这里大家应该也能推导出来redis肯定存储空间比不上关系型数据库,所以我们应该尽量只把高频热点信息存放在redis中,根据具体的应用场景来决策!

HashMap原理

HashMap原理与字典
在展开HashMap原理的讲解之前,首先回忆一下大家初中和高中使用的汉英字典。
比如要找一个单词对应的中文意思,假设单词是Lengendary,首先在目录找到Lengendary在第 555页。
然后,翻到第555页,这页不只一个单词,但是量已经很少了,逐一比较,很快就定位目标单词Lengendary。
555相当于就是Lengendary对应的hashcode

HashMap性能卓越的原因
-----hashcode概念-----
所有的对象,都有一个对应的hashcode(散列值)
比如字符串“gareen”对应的是1001 (实际上不是,这里是方便理解,假设的值)
比如字符串“temoo”对应的是1004
比如字符串“db”对应的是1008
比如字符串“annie”对应的也是1008
-----保存数据-----
准备一个数组,其长度是2000,并且设定特殊的hashcode算法,使得所有字符串对应的hashcode,都会落在0-1999之间
要存放名字是"gareen"的英雄,就把该英雄和名称组成一个键值对,存放在数组的1001这个位置上
要存放名字是"temoo"的英雄,就把该英雄存放在数组的1004这个位置上
要存放名字是"db"的英雄,就把该英雄存放在数组的1008这个位置上
要存放名字是"annie"的英雄,然而 "annie"的hashcode 1008对应的位置已经有db英雄了,那么就在这里创建一个链表,接在db英雄后面存放annie
-----查找数据-----
比如要查找gareen,首先计算"gareen"的hashcode是1001,根据1001这个下标,到数组中进行定位,(根据数组下标进行定位,是非常快速的) 发现1001这个位置就只有一个英雄,那么该英雄就是gareen.
比如要查找annie,首先计算"annie"的hashcode是1008,根据1008这个下标,到数组中进行定位,发现1008这个位置有两个英雄,那么就对两个英雄的名字进行逐一比较(equals),因为此时需要比较的量就已经少很多了,很快也就可以找出目标英雄
这就是使用hashmap进行查询,非常快原理。
这是一种用空间换时间的思维方式

一文搞懂redis(其实很简单)_第1张图片

redis是什么?

经过刚才的介绍,大家应该已经对redis有个初步认识了,所以现在丢概念给大家,大家应该就不会懵了。redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、高性能键值对(Key-Value)数据库,并提供多种语言的API。是当前最热门的的的NoSql数据库之一,也被人们称为数据结构服务器。

redis单线程机制

Redis是单线程的多路复用IO,单线程避免了线程切换的开销,而多路复用IO避免了IO等待的开销,在多核处理器下提高处理器的使用效率可以对数据进行分区,然后每个处理器处理不同的数据。

redis持久化

redis是基于内存的,如果突然发生断电那么数据就会丢失,所以我们需要持久化,根据需要再把它的数据往硬盘存,这样即使突然发生断电,我们也可以从硬盘中取出数据进行灾难恢复。

redis的5种数据类型

1.字符串类型 string
2.列表类型 list
3.散列类型 hash
4.集合类型 set
5.有序集合类型 sorted_set

应用场景
hash:实现购物车,实现抢购
list:实现日志消息队列
set:实现权限校验,实现网站访问量统计,实现黑白名单,实现时效性任务管理,实现带有权重的任务管理
sorted_set:实现排行榜

单指令数据操作和多指令数据操作的选择

一共三个地方需要消耗时间:
1.set发送请求给redis
2.redis处理请求
3.result返回给客户端
例子:
如果是50条数据的话,选择多指令操作更好,因为它只用发一次请求和返回一次结果,而单指令则需要发50次和响应50次
如果是1亿条数据,选择单指令操作,因为数据量太大了,一次发送这么多再加上redis又是单线程,会导致这条指令执行很久,会让其他所有请求都在等这条请求完成
根据需求来选择!!!

redis事务

概念
redis事务就是一个命令执行的队列,将一系列预定义命令包装成一个整体(一个队列)。当执行时,一次性按照添加顺序依次执行,中间不会被打断或者干扰


场景:天猫双十一热卖过程中,对已经售罄的的货物追加补货,4个业务员都有权限进行补货,补货的操作可能是一系列操作,牵扯到多个连续的动作,如何保障不会重复操作?即一个业务员补了,另一个业务员不会多补

场景实现:多个客户端操作同一组数据时,该数据一旦被操作修改后,将不适用于继续操作。在操作之前锁定要操作的数据,一旦发生变化,终止当前操作(使用watch命令

分布式锁
场景:天猫双十一热卖过程中,对已经售罄的货物追加补货,且补货完成.客户购买热情高涨,3S内将所有商品购买完毕,本次补货已经将库存全部清空,如何避免最后一件商品不被多人同时购买?

场景实现:此时使用watch监控一个key有没有改变已经不能解决问题,此处要监控的是具体数据,虽然redis是单线程的,但是多个客户端对同一数据同时进行操作时,仍有风险,所以应该使用分布式锁来解决
setnx lock-key value:使用setnx设置一个公共锁,利用setnx命令的返回值特征,有值则返回设置失败,无值则返回设置成功,对于返回设置成功的,拥有控制权,进行业务操作,对于返回设置失败的,不具有控制权,排队或等待。操作完毕通过del操作释放锁。

死锁的解决方案
场景:依赖分布式锁的机制,某个用户操作时对应客户端宕机了,且此时已经获取到锁,如何解决?

解决方案:使用expire为锁key添加时间限定,到时不释放,会强制释放

redis删除策略

过期数据
Redis中的数据特征
Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态(3种如下)
XX:具有时效性的数据
-1:永久有效的数据
-2:已经过期的数据或被删除的数据或未定义的数据

过期的数据真的立刻删除了吗?
redis删除数据时会给cpu发消息,如果是发送的一组操作指令,cpu压力会增大,这时redis会让cpu先干正事例如get set,而不是删除数据如果当前内存充足的话,所以过期数据其实并未马上删除。

时效性数据的存储结构:
一文搞懂redis(其实很简单)_第2张图片
redis存放数据后,会开辟一块空间里面存储值的地址以及它的过期时间

删除策略的目标
在内存占用与CPU占用之间寻找一种平衡,顾此失彼都会造成整体redis性能的下降,甚至引发服务器宕机或内存泄漏
人话翻译:cpu忙的时候,就不要再去删除数据降低性能了,cpu不忙的时候再来维护内存

定时删除
创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作。
优点:节约内存,到时就删除,快速释放不必要的内存占用
缺点:cpu压力很大,无论cpu此时负载量多高,均占有cpu,会影响redis服务器响应时间和指令吞吐量
总结:用处理器性能换存储空间

惰性删除
数据到达过期时间,不作处理,等下次访问该数据时删除返回不存在。
优点:节约cpu性能,发现必须删除的时候才删除
缺点:内存压力很大,出现长期占用内存的数据
总结:用存储空间换取处理器性能

这两种方式都极端,有折中方案
定期删除
redis服务器启动初始化时,会去读取server.hz的值,默认为10
每秒钟执行server.hz次serverCron()(定时轮巡)→databasesCron(对每一个数据库轮巡)→activeExpireCycle()(这个操作是对每个数据库里的expires[]逐一进行检测,每次执行250ms/server.hz,对expires[]具体检测时,又是从里面随机挑选出W个key检测,如果key超时,删除key,如果一轮中删除的key的数量>W25%,循环该过程,如果一轮中删除的key的数量<=W25%,检查下一个expires[*],W取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值)

参数current_db用于记录activeExpireCycle()进入哪个expires[*]执行。
如果activeExpireCycle()执行时间到期,下次从current_db继续向下执行

周期性轮巡redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度
特点1:CPU性能占用设置有峰值,检测频度可自定义设置
特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理
总结:周期性抽查存储空间(随机抽查,重点抽查)

redis常用惰性删除和定期删除

逐出算法
如果新数据进入redis时,如果内存不足怎么办?
redis使用内存存储数据,在执行每一个命令前,会调用freeMemorylfNeeded()检测内存是否充足.如果内存不满足新加入数据的最低存储要求。redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为逐出算法。
注意:逐出数据的过程不是百分之百能够清理出足够的可使用的内存空间,如果不成功则反复执行。当对所有数据尝试完毕后,如果不能达到内存清理的要求,将会出现错误信息。

maxmemory:占用你计算机物理内存的比例,默认值为0,表示不限制.一般设置在百分之50以上。

相关配置:
检测易失数据:
volatile-lru:挑选最近最少使用的数据淘汰
volatile-lfu:挑选最近使用次数最少的数据淘汰
volatile-ttl:挑选将要过期的数据淘汰
volatile-random:任意选择数据淘汰
检测全库数据:
alllkeys-lru:挑选最近最少使用的数据淘汰
alllkeys-lfu:挑选最近使用次数最少的数据淘汰
alllkeys-random:任意选择数据淘汰

redis高级数据类型

redis高级数据模型是为解决单一的业务

Bitmaps:
用最小的存储单元bit来表示状态,例如:1001就表示第一个和第四个是1的状态,第二个和第三个是0的状态

HyperLogLog:
运用LogLog算法来统计基数(基数是数据集去重后的元素个数)
误差范围:基数估计的结果是一个带有0.81%标准错误的近似值。
消耗空间很小,每个hyperloglog key占用了12K的内存用于标记基数

GEO:
坐标类型,例如附近的人这种功能就能用它实现

redis主从复制

待更

redis哨兵

待更

redis集群

待更

redis缓存预热

现象:服务器启动后迅速宕机
问题排查:
1.请求数量较高
2.主从之间数据吞吐量较大,数据同步操作频度较高
解决方案:
待更

redis缓存雪崩

现象:在一个较短时间内,缓存中较多的key集中过期,此时间内请求访问过期的数据,redis未命中,redis向数据库获取数据,数据库同时接收到大量的请求无法及时处理,redis大量请求被积压,开始出现超时现象,数据库流量激增,数据库崩溃,重启后仍然面对缓存中无数据可用,redis服务器资源被严重占用,redis服务器崩溃,redis集群出现崩塌,集群瓦解,应用服务器无法及时得到数据响应请求,来自客户端的请求数量越来越多,应用服务器崩溃。

解决方案:
1.更多的页面静态化处理
2.构建多级缓存架构
Nginx缓存+redis缓存+ehcache缓存
3.检查mysql严重耗时业务进行优化
对数据库的瓶颈排查:例如超时查询,耗时较高事务等
4.灾难预警机制
监控redis服务器性能指标
cpu占用,cpu使用率
内存容量
查询平均响应时间
线程数
5.限流,降级:短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问

针对数据过期的解决方案:
1.LRU与LFU切换(删除策略)
2.数据有效期策略调整
根据业务数据有效期进行分类错峰,A类90分钟,B类80分钟,C类70分钟。
过期时间使用固定时间+随机值的形式,稀释集中到期的key数量
3.超热数据使用永久key
4.定期维护(自动+人工)
对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时
5.加锁!慎用!

redis缓存击穿

现象:redis某个key过期,该key访问量巨大,多个数据请求从服务器直接压到redis后,均未命中,redis在短时间内发起了大量对数据库中同一数据的访问。

解决方案:
1.预先设定:以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息key的过期时长,注意:购物节不仅仅指当天,以及后续若干天,访问峰值呈现逐渐降低的趋势。
2.现场调整:监控访问量,对自然流量激增的数据延长过期时间或设置为永久性key
3.后台刷新数据:启动定时任务,高峰期来临之前,刷新数据有效期,确保不丢失
4.二级缓存:设置不同的失效时间,保障不会被同时淘汰就行
5.加锁:分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重

redis缓存穿透

现象:redis中出现大面积的未命中,出现不正确的url,获取的数据在数据库中也不存在,数据库查询未得到对应数据,redis获取到null数据未进行持久化,直接返回,下次此类数据到达重复上述过程,出现黑客攻击服务器

解决方案:
1.缓存null:对查询结果为null的数据进行缓存(长期使用,定期清理)。设置定短时限,例如30-60秒,最高5分钟
2.白名单策略:
提前预热各种分类数据id对应的bitmaps,id作为bitmaps的offset,相当于设置了数据白名单。当加载正常数据时,放行,加载异常数据时直接拦截(效率偏低)
使用布隆过滤器
3.实施监控:实时监控redis命中率(业务正常范围时,通常会有一个波动值)与null数据的占比
非活动时段波动:通常检测3-5倍,超过5倍纳入重点排查对象
活动时段波动:通常检测10-50倍,超过50倍纳入重点排查对象
根据倍数不同,启动不同的排查流程。然后使用黑名单进行防控
4.key加密:问题出现后,临时启动防灾业务key,对key进行业务层传输加密服务,设定校验程序,过来的key校验,例如每天随机分配60个加密串,挑选2到3个,混淆到页面数据id中,发现访问key不满足规则,驳回数据访问

redis性能监控

待更

redis常见应用场景

1.为热点数据加速查询(主要场景),如热点商品,热点新闻,热点资讯,推广类等高访问量信息等
2.任务队列,如秒杀,抢购,购票排队等
3.即时信息查询,如排行榜,各类网站访问统计,公交到站信息,在线人数信息(聊天室,网站),设备信号等
4.时效性信息控制,如验证码控制,投票控制等
5.分布式数据共享,如分布式集群架构中的session分离
6.消息队列
7.分布式锁

你可能感兴趣的:(redis,redis,数据库,java,经验分享,面试)