场景:Redis面试
(图片来源于网络)html
面试官: 我看到你的简历上说你熟练使用Redis,那么你讲一下Redis是干吗用的?git
小明: (心中窃喜,Redis不就是缓存吗?)Redis主要用做缓存,经过内存高效地存储非持久化数据。github
面试官: Redis能够用做持久化的存储吗?面试
小明 :嗯...应该能够吧...数据库
面试官: 那Redis怎么进行持久化操做呢?后端
小明:嗯...不是太清楚。缓存
面试官: Redis的内存淘汰机制有哪些?网络
小明:嗯...没了解过数据结构
面试官:咱们还能够用Redis作哪些事情?分别利用了Redis的哪一个指令?多线程
小明:我只知道Redis还能够作分布式锁、消息队列...
面试官:好了,咱们进入下一个话题...
思考:很明显,小明同窗在面试过程当中关于Redis的表现和回答确定是比较失败的。Redis是咱们工做中天天都会使用到的东西,为何一到面试却变成了丢分项呢?
做为开发者,咱们习惯了使用大神们已经封装好的东西,以此保障咱们可以更专一于业务开发,殊不知道这些经常使用工具的底层实现是什么,所以尽管平时应用起来驾轻就熟,但一到面试仍是没法让面试官眼前一亮。
本文总结了一些Redis的知识点,有原理有应用,但愿能够帮助到你们。
1、Redis是什么
REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。
Redis是一个开源的使用ANSI 、C语言编写、遵照BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
这里我引用了Redis教程里对Redis的描述,很官方,可是很标准。 可基于内存亦可持久化的日志型、Key-Value数据库。 我认为这个描述很贴切很全面。
1.1 Redis的行业地位
Redis是互联网技术领域使用最为普遍的存储中间件,因超高的性能、完美的文档、多方面的应用能力以及丰富完善的客户端支持在存储方面独当一面,广受好评,尤为以其性能和读取速度而成为了领域中最受青睐的中间件。基本上每个软件公司都会使用Redis,其中包括不少大型互联网公司,好比京东、阿里、腾讯、github等。所以,Redis也成为了后端开发人员必不可少的技能。
1.2 知识图谱
在我看来,学习每一项技术,都须要有一个清晰的脉络和结构,否则你也不知道本身会了哪些、还有多少没学会。就像一本书,若是没有目录章节,也就失去了灵魂。
所以我试图总结出Redis的知识图谱,也称为脑图,以下图所示,可能知识点不是很全,后续会不断更新补充。
本系列文章的知识点也会和这个脑图基本一致,本文先介绍Redis的基本知识,后续文章会详细介绍Redis的数据结构、应用、持久化等多个方面。
2、Redis优势
2.1 速度快
做为缓存工具,Redis最广为人知的特色就是快,到底有多快呢?Redis单机qps(每秒的并发)能够达到110000次/s,写的速度是81000次/s。 那么,Redis为何这么快呢?
- 绝大部分请求是纯粹的内存操做,很是快速;
- 使用了不少查找操做都特别快的数据结构进行数据存储,Redis中的数据结构是专门设计的。如HashMap,查找、插入的时间复杂度都是O(1);
- 采用单线程,避免了没必要要的上下文切换和竞争条件,也不存在多进程或者多线程致使的切换而消耗CPU,不用去考虑各类锁的问题,不存在加锁、释放锁操做,没有由于可能出现死锁而致使的性能消耗;
- 用到了非阻塞I/O多路复用机制。
2.2 丰富的数据类型
Redis有5种经常使用的数据类型:String、List、Hash、set、zset,每种数据类型都有本身的用处。
2.3 原子性,支持事务
Redis支持事务,而且它的全部操做都是原子性的,同时Redis还支持对几个操做合并后的原子性执行。
2.4 丰富的特性
Redis具备丰富的特性,好比能够用做分布式锁;能够持久化数据;能够用做消息队列、排行榜、计数器;还支持publish/subscribe、通知、key过时等等。当咱们要用中间件来解决实际问题的时候,Redis总能发挥出本身的用处。
3、Redis和Memcache对比
Memcache和Redis都是优秀的、高性能的内存数据库,通常咱们说到Redis的时候,都会拿Memcache来和Redis作对比。(为何要作对比呢?固然是要陪衬出Redis有多好,没有对比,就没有伤害~)对比的方面包括:
3.1 存储方式
-
Memcache把数据所有存在内存之中,断电后会挂掉,没法作到数据的持久化,且数据不能超过内存大小。
-
Redis有一部分数据存在硬盘上,能够作到数据的持久性。
3.2 数据支持类型
-
Memcache对数据类型支持相对简单,只支持String类型的数据结构。
-
Redis有丰富的数据类型,包括:String、List、Hash、Set、Zset。
3.3 使用的底层模型
-
它们之间底层实现方式以及与客户端之间通讯的应用协议不同。
-
Redis直接本身构建了VM机制 ,由于通常的系统调用系统函数,会浪费必定的时间去移动和请求。
3.4 存储值大小
- Redis最大能够存储1GB,而memcache只有1MB。
看到这里,会不会以为Redis特别好,全是优势,天衣无缝?其实Redis仍是有不少缺点的,这些缺点日常咱们该如何克服呢?
4、Redis存在的问题及解决方案
4.1 缓存数据库的双写一致性的问题
问题:一致性的问题是分布式系统中很常见的问题。一致性通常分为两种:强一致性和最终一致性,当咱们要知足强一致性的时候,Redis也没法作到完美无瑕,由于数据库和缓存双写,确定会出现不一致的状况,Redis只能保证最终一致性。
解决:咱们如何保证最终一致性呢?
-
第一种方式是给缓存设置必定的过时时间,在缓存过时以后会自动查询数据库,保证数据库和缓存的一致性。
-
若是不设置过时时间的话,咱们首先要选取正确的更新策略:先更新数据库再删除缓存。但咱们删除缓存的时候也可能出现某些问题,因此须要将要删除的缓存的key放到消息队列中去,不断重试,直到删除成功为止。
4.2 缓存雪崩问题
问题: 咱们应该都在电影里看到过雪崩,开始很平静,而后一瞬间就开始崩塌,具备很强的毁灭性。这里也是同样的,咱们执行代码的时候将不少缓存的实效时间设定成同样,接着这些缓存在同一时间都会实效,而后都会从新访问数据库更新数据,这样会致使数据库链接数过多、压力过大而崩溃。
解决:
- 设置缓存过时时间的时候加一个随机值。
- 设置双缓存,缓存1设置缓存时间,缓存2不设置,1过时后直接返回缓存2,而且启动一个进程去更新缓存1和2。
4.3 缓存穿透问题
问题: 缓存穿透是指一些非正经常使用户(黑客)故意去请求缓存中不存在的数据,致使全部的请求都集中到到数据库上,从而致使数据库链接异常。
解决:
-
利用互斥锁。缓存失效的时候,不能直接访问数据库,而是要先获取到锁,才能去请求数据库。没获得锁,则休眠一段时间后重试。
-
采用异步更新策略。不管key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存若是过时,异步起一个线程去读数据库,更新缓存。须要作缓存预热(项目启动前,先加载缓存)操做。
-
提供一个能迅速判断请求是否有效的拦截机制。好比利用布隆过滤器,内部维护一系列合法有效的key,迅速判断出请求所携带的Key是否合法有效。若是不合法,则直接返回。
4.4 缓存的并发竞争问题
问题:
缓存并发竞争的问题,主要发生在多线程对某个key进行set的时候,这时会出现数据不一致的状况。
好比Redis中咱们存着一个key为amount的值,它的value是100,两个线程同时都对value加100而后更新,正确的结果应该是变为300。可是两个线程拿到这个值的时候都是100,最后结果也就是200,这就致使了缓存的并发竞争问题。
解决
- 若是多线程操做没有顺序要求的话,咱们能够设置一个分布式锁,而后多个线程去争夺锁,谁先抢到锁谁就能够先执行。这个分布式锁能够用zookeeper或者Redis自己去实现。
- 能够利用Redis的incr命令。
- 当咱们的多线程操做须要顺序的时候,咱们能够设置一个消息队列,把须要的操做加到消息队列中去,严格按照队列的前后执行命令。
5、Redis的过时策略
Redis随着数据的增多,内存占用率会持续变高,咱们觉得一些键到达设置的删除时间就会被删除,可是时间到了,内存的占用率仍是很高,这是为何呢?
Redis采用的是按期删除和惰性删除的内存淘汰机制。
5.1 按期删除
按期删除和定时删除是有区别的:
-
定时删除是必须严格按照设定的时间去删除缓存,这就须要咱们设置一个定时器去不断地轮询全部的key,判断是否须要进行删除。可是这样的话cpu的资源会被大幅度地占据,资源的利用率变低。因此咱们选择采用按期删除,。
-
按期删除是时间由咱们定,咱们能够每隔100ms进行检查,但仍是不能检查全部的缓存,Redis仍是会卡死,只能随机地去检查一部分缓存,可是这样会有一些缓存没法在规定时间内删除。这时惰性删除就派上用场了。
5.2 惰性删除
举个简单的例子:中学的时候,平时做业太多,根本作不完,老师说下节课要讲这个卷子,大家都作完了吧?其实有不少人没作完,因此须要在下节课以前赶忙补上。
惰性删除也是这个道理,咱们的这个值按理说应该没了,可是它还在,当你要获取这个key的时候,发现这个key应该过时了,赶忙删了,而后返回一个'没有这个值,已通过期了!'。
如今咱们有了按期删除 + 惰性删除的过时策略,就能够高枕无忧了吗?并非这样的,若是这个key一直不访问,那么它会一直滞留,也是不合理的,这就须要咱们的内存淘汰机制了。
5.3 Redis的内存淘汰机制
Redis的内存淘汰机制通常有6种,以下图所示:
那么咱们如何去配置Redis的内存淘汰机制呢?
在Redis.conf中咱们能够进行配置
# maxmemory-policy allkeys-lru
6、小结
本文初探Redis,大概整理出了Redis的知识图谱,对照之下能够发现Redis竟然有这么多的知识点须要学习;接着咱们分析了Redis的优缺点,知道了其基于内存的高效的读写速度和丰富的数据类型,也分析了Redis面对数据一致性、缓存穿透、缓存雪崩等问题时该如何处理;最后咱们了解了Redis的过时策略和缓存淘汰机制。
相信你们已经对Redis有了一些了解,下篇文章咱们将分析Redis的数据结构、每一种数据类型是如何实现的、对应的命令有哪些。