你知道的越多,你不知道的越多node
点赞再看,养成习惯git
GitHub上已经开源github.com/JavaFamily,有一线大厂面试点脑图,欢迎Star和完善github
Redis在互联网技术存储方面使用如此普遍,几乎全部的后端技术面试官都要在Redis的使用和原理方面对小伙伴们进行360°的刁难。面试
做为一个在互联网公司面一次拿一次Offer的面霸,战胜了无数竞争对手,每次都只能看到无数落寞的身影失望的离开,略感愧疚(请容许我使用一下夸张的修辞手法)。redis
因而在一个寂寞难耐的夜晚,我痛定思痛,决定开始写《吊打面试官》系列,但愿能帮助各位读者之后面试势如破竹,对面试官进行360°的反击,吊打问你的面试官,让一同面试的同僚瞠目结舌,疯狂收割大厂Offer!算法
上一期由于是在双十一一直在熬夜的大环境下完成的,因此我本身以为质量明显没以前的好,我这不一睡好就加班加点准备补偿你们,来点干货。(熬夜太容易感冒了,此次点个赞别白嫖了!)数据库
顺带提一嘴,我把我准备写啥画了一个思惟导图,之后总不能每篇都放个贼大的图吧,就开源到了个人GitHub,你们有兴趣能够去完善和Star。后端
这篇我就先放出来你们看看,感受仍是差点意思,等你们完善了。缓存
上一期吊打系列咱们提到了Redis相关的一些知识,还没看的小伙伴能够回顾一下 服务器
这期我就从缓存到一些常见的问题讲一下,有一些我是以前提到过的,不过可能大部分仔是第一次看,我就重复发一下。
缓存是高并发场景下提升热点数据访问性能的一个有效手段,在开发项目时会常用到。
缓存的类型分为:本地缓存、分布式缓存和多级缓存。
本地缓存就是在进程的内存中进行缓存,好比咱们的 JVM 堆中,能够用 LRUMap 来实现,也可使用 Ehcache 这样的工具来实现。
本地缓存是内存访问,没有远程交互开销,性能最好,可是受限于单机容量,通常缓存较小且没法扩展。
分布式缓存能够很好得解决这个问题。
分布式缓存通常都具备良好的水平扩展能力,对较大数据量的场景也能应付自如。缺点就是须要进行远程请求,性能不如本地缓存。
为了平衡这种状况,实际业务中通常采用多级缓存,本地缓存只保存访问频率最高的部分热点数据,其余的热点数据放在分布式缓存中。
在目前的一线大厂中,这也是最经常使用的缓存方案,单考单一的缓存方案每每难以撑住不少高并发的场景。
无论是本地缓存仍是分布式缓存,为了保证较高性能,都是使用内存来保存数据,因为成本和内存限制,当存储的数据超过缓存容量时,须要对缓存的数据进行剔除。
通常的剔除策略有 FIFO 淘汰最先数据、LRU 剔除最近最少使用、和 LFU 剔除最近使用频率最低的数据几种策略。
noeviction:返回错误当内存限制达到而且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过时集合的键,使得新添加的数据有空间存放。
allkeys-random: 回收随机的键使得新添加的数据有空间存放。
volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过时集合的键。
volatile-ttl: 回收在过时集合的键,而且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
若是没有键知足回收的前提条件的话,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction 差很少了。
其实在你们熟悉的LinkedHashMap中也实现了Lru算法的,实现以下:
当容量超过100时,开始执行LRU策略:将最近最少未使用的 TimeoutInfoHolder 对象 evict 掉。
真实面试中会让你写LUR算法,你可别搞原始的那个,那真TM多,写不完的,你要么怼上面这个,要么怼下面这个,找一个数据结构实现下Java版本的LRU仍是比较容易的,知道啥原理就行了。
注意后面会把 Memcache 简称为 MC。
先来看看 MC 的特色:
另外,使用 MC 有一些限制,这些限制在如今的互联网场景下很致命,成为你们选择Redis、MongoDB的重要缘由:
先简单说一下 Redis 的特色,方便和 MC 比较。
Redis 的知识点结构以下图所示。
来看 Redis 提供的功能有哪些吧!
String 类型是 Redis 中最常使用的类型,内部的实现是经过 SDS(Simple Dynamic String )来存储的。SDS 相似于 Java 中的 ArrayList,能够经过预分配冗余空间的方式来减小内存的频繁分配。
这是最简单的类型,就是普通的 set 和 get,作简单的 KV 缓存。
可是真实的开发环境中,不少仔可能会把不少比较复杂的结构也统一转成String去存储使用,好比有的仔他就喜欢把对象或者List转换为JSONString进行存储,拿出来再反序列话啥的。
我在这里就不讨论这样作的对错了,可是我仍是但愿你们能在最合适的场景使用最合适的数据结构,对象找不到最合适的可是类型能够选最合适的嘛,以后别人接手你的代码一看这么规范,诶这小伙子有点东西呀,看到你啥都是用的String,垃圾!
好了这些都是题外话了,道理仍是但愿你们记在内心,习惯成天然嘛,小习惯成就你。
String的实际应用场景比较普遍的有:
缓存功能:String字符串是最经常使用的数据类型,不只仅是Redis,各个语言都是最基本类型,所以,利用Redis做为缓存,配合其它数据库做为存储层,利用Redis支持高并发的特色,能够大大加快系统的读写速度、以及下降后端数据库的压力。
计数器:许多系统都会使用Redis做为系统的实时计数器,能够快速实现计数和查询的功能。并且最终的数据结果能够按照特定的时间落地到数据库或者其它存储介质当中进行永久保存。
共享用户Session:用户从新刷新一次界面,可能须要访问一下数据进行从新登陆,或者访问页面缓存Cookie,可是能够利用Redis将用户的Session集中管理,在这种模式只须要保证Redis的高可用,每次用户Session的更新和获取均可以快速完成。大大提升效率。
这个是相似 Map 的一种结构,这个通常就是能够将结构化的数据,好比一个对象(前提是这个对象没嵌套其余的对象)给缓存在 Redis 里,而后每次读写缓存的时候,能够就操做 Hash 里的某个字段。
可是这个的场景其实仍是多少单一了一些,由于如今不少对象都是比较复杂的,好比你的商品对象可能里面就包含了不少属性,其中也有对象。我本身使用的场景用得不是那么多。
List 是有序列表,这个仍是能够玩儿出不少花样的。
好比能够经过 List 存储一些列表型的数据结构,相似粉丝列表、文章的评论列表之类的东西。
好比能够经过 lrange 命令,读取某个闭区间内的元素,能够基于 List 实现分页查询,这个是很棒的一个功能,基于 Redis 实现简单的高性能分页,能够作相似微博那种下拉不断分页的东西,性能高,就一页一页走。
好比能够搞个简单的消息队列,从 List 头怼进去,从 List 屁股那里弄出来。
List自己就是咱们在开发过程当中比较经常使用的数据结构了,热点数据更不用说了。
消息队列:Redis的链表结构,能够轻松实现阻塞队列,可使用左进右出的命令组成来完成队列的设计。好比:数据的生产者能够经过Lpush命令从左边插入数据,多个数据消费者,可使用BRpop命令阻塞的“抢”列表尾部的数据。
文章列表或者数据分页展现的应用。
好比,咱们经常使用的博客网站的文章列表,当用户量愈来愈多时,并且每个用户都有本身的文章列表,并且当文章多时,都须要分页展现,这时能够考虑使用Redis的列表,列表不但有序同时还支持按照范围内获取元素,能够完美解决分页查询功能。大大提升查询效率。
Set 是无序集合,会自动去重的那种。
直接基于 Set 将系统里须要去重的数据扔进去,自动就给去重了,若是你须要对一些数据进行快速的全局去重,你固然也能够基于 JVM 内存里的 HashSet 进行去重,可是若是你的某个系统部署在多台机器上呢?得基于Redis进行全局的 Set 去重。
能够基于 Set 玩儿交集、并集、差集的操做,好比交集吧,咱们能够把两我的的好友列表整一个交集,看看俩人的共同好友是谁?对吧。
反正这些场景比较多,由于对比很快,操做也简单,两个查询一个Set搞定。
Sorted set 是排序的 Set,去重但能够排序,写进去的时候给一个分数,自动根据分数排序。
有序集合的使用场景与集合相似,可是set集合不是自动有序的,而Sorted set能够利用分数进行成员间的排序,并且是插入时就排序好。因此当你须要一个有序且不重复的集合列表时,就能够选择Sorted set数据结构做为选择方案。
排行榜:有序集合经典使用场景。例如视频网站须要对用户上传的视频作排行榜,榜单维护多是多方面:按照时间、按照播放量、按照得到的赞数等。
用Sorted Sets来作带权重的队列,好比普通消息的score为1,重要消息的score为2,而后工做线程能够选择按score的倒序来获取工做任务。让重要的任务优先执行。
微博热搜榜,就是有个后面的热度值,前面就是名称
位图是支持按 bit 位来存储信息,能够用来实现 布隆过滤器(BloomFilter);
供不精确的去重计数功能,比较适合用来作大规模数据的去重统计,例如统计 UV;
能够用来保存地理位置,并做位置距离计算或者根据半径计算位置等。有没有想过用Redis来实现附近的人?或者计算最优地图路径?
这三个其实也能够算做一种数据结构,不知道还有多少朋友记得,我在梦开始的地方,Redis基础中提到过,你若是只知道五种基础类型那只能拿60分,若是你能讲出高级用法,那就以为你有点东西。
功能是订阅发布功能,能够用做简单的消息队列。
能够批量执行一组指令,一次性返回所有结果,能够减小频繁的请求应答。
Redis 支持提交 Lua 脚原本执行一系列的功能。
我在前电商老东家的时候,秒杀场景常用这个东西,讲道理有点香,利用他的原子性。
话说大家想看秒杀的设计么?我记得我面试好像每次都问啊,想看的直接点赞后评论秒杀吧。
最后一个功能是事务,但 Redis 提供的不是严格的事务,Redis 只保证串行执行命令,而且能保证所有执行,可是执行命令失败时并不会回滚,而是会继续执行下去。
Redis 提供了 RDB 和 AOF 两种持久化方式,RDB 是把内存中的数据集以快照形式写入磁盘,实际操做是经过 fork 子进程执行,采用二进制压缩存储;AOF 是以文本日志的形式记录 Redis 处理的每个写入或删除操做。
RDB 把整个 Redis 的数据保存在单一文件中,比较适合用来作灾备,但缺点是快照保存完成以前若是宕机,这段时间的数据将会丢失,另外保存快照时可能致使服务短期不可用。
AOF 对日志文件的写入操做使用的追加模式,有灵活的同步策略,支持每秒同步、每次修改同步和不一样步,缺点就是相同规模的数据集,AOF 要大于 RDB,AOF 在运行效率上每每会慢于 RDB。
细节的点你们去高可用这章看,特别是二者的优缺点,以及怎么抉择。
《吊打面试官》系列-Redis哨兵、持久化、主从、手撕LRU
来看 Redis 的高可用。Redis 支持主从同步,提供 Cluster 集群部署模式,经过 Sentine l哨兵来监控 Redis 主服务器的状态。当主挂掉时,在从节点中根据必定策略选出新主,并调整其余从 slaveof 到新主。
选主的策略简单来讲有三个:
在 Redis 集群中,sentinel 也会进行多实例部署,sentinel 之间经过 Raft 协议来保证自身的高可用。
Redis Cluster 使用分片机制,在内部分为 16384 个 slot 插槽,分布在全部 master 节点上,每一个 master 节点负责一部分 slot。数据操做时按 key 作 CRC16 来计算在哪一个 slot,由哪一个 master 进行处理。数据的冗余是经过 slave 节点来保障。
哨兵必须用三个实例去保证本身的健壮性的,哨兵+主从并不能保证数据不丢失,可是能够保证集群的高可用。
为啥必需要三个实例呢?咱们先看看两个哨兵会咋样。
master宕机了 s1和s2两个哨兵只要有一个认为你宕机了就切换了,而且会选举出一个哨兵去执行故障,可是这个时候也须要大多数哨兵都是运行的。
那这样有啥问题呢?M1宕机了,S1没挂那实际上是OK的,可是整个机器都挂了呢?哨兵就只剩下S2个裸屌了,没有哨兵去容许故障转移了,虽然另一个机器上还有R1,可是故障转移就是不执行。
经典的哨兵集群是这样的:
M1所在的机器挂了,哨兵还有两个,两我的一看他不是挂了嘛,那咱们就选举一个出来执行故障转移不就行了。
暖男我,小的总结下哨兵组件的主要功能:
提到这个,就跟我前面提到的数据持久化的RDB和AOF有着比密切的关系了。
我先说下为啥要用主从这样的架构模式,前面提到了单机QPS是有上限的,并且Redis的特性就是必须支撑读高并发的,那你一台机器又读又写,这谁顶得住啊,不当人啊!可是你让这个master机器去写,数据同步给别的slave机器,他们都拿去读,分发掉大量的请求那是否是好不少,并且扩容的时候还能够轻松实现水平扩容。
你启动一台slave 的时候,他会发送一个psync命令给master ,若是是这个slave第一次链接到master,他会触发一个全量复制。master就会启动一个线程,生成RDB快照,还会把新的写请求都缓存在内存中,RDB文件生成后,master会将这个RDB发送给slave的,slave拿到以后作的第一件事情就是写进本地的磁盘,而后加载进内存,而后master会把内存里面缓存的那些新命名都发给slave。
我发出来以后来自CSDN的网友:Jian_Shen_Zer 问了个问题:
主从同步的时候,新的slaver进来的时候用RDB,那以后的数据呢?有新的数据进入master怎么同步到slaver啊
敖丙答:笨,AOF嘛,增量的就像MySQL的Binlog同样,把日志增量同步给从服务就行了
Redis 的 key 能够设置过时时间,过时后 Redis 采用主动和被动结合的失效机制,一个是和 MC 同样在访问时触发被动删除,另外一种是按期的主动删除。
按期+惰性+内存淘汰
这是决定在使用缓存时就该考虑的问题。
缓存的数据在数据源发生变动时须要对缓存进行更新,数据源多是 DB,也多是远程服务。更新的方式能够是主动更新。数据源是 DB 时,能够在更新完 DB 后就直接更新缓存。
当数据源不是 DB 而是其余远程服务,可能没法及时主动感知数据变动,这种状况下通常会选择对缓存数据设置失效期,也就是数据不一致的最大容忍时间。
这种场景下,能够选择失效更新,key 不存在或失效时先请求数据源获取最新数据,而后再次缓存,并更新失效期。
但这样作有个问题,若是依赖的远程服务在更新时出现异常,则会致使数据不可用。改进的办法是异步更新,就是当失效时先不清除数据,继续使用旧的数据,而后由异步线程去执行更新任务。这样就避免了失效瞬间的空窗期。另外还有一种纯异步更新方式,定时对数据进行分批更新。实际使用时能够根据业务场景选择更新方式。
第二个问题是数据不一致的问题,能够说只要使用缓存,就要考虑如何面对这个问题。缓存不一致产生的缘由通常是主动更新失败,例如更新 DB 后,更新 Redis 由于网络缘由请求超时;或者是异步更新失败致使。
解决的办法是,若是服务对耗时不是特别敏感能够增长重试;若是服务对耗时敏感能够经过异步补偿任务来处理失败的更新,或者短时间的数据不一致不会影响业务,那么只要下次更新时能够成功,能保证最终一致性就能够。
缓存穿透。产生这个问题的缘由多是外部的恶意攻击,例如,对用户信息进行了缓存,但恶意攻击者使用不存在的用户id频繁请求接口,致使查询缓存不命中,而后穿透 DB 查询依然不命中。这时会有大量请求穿透缓存访问到 DB。
解决的办法以下。
缓存击穿,就是某个热点数据失效时,大量针对这个数据的请求会穿透到数据源。
解决这个问题有以下办法。
缓存雪崩,产生的缘由是缓存挂掉,这时全部的请求都会穿透到 DB。
解决方法:
实际场景中,这两种方法会结合使用。
老朋友都知道为啥我没有大篇幅介绍这个几个点了吧,我在以前的文章实在是写得太详细了,忍不住点赞那种,我这里就不作重复拷贝了。
拿笔记一下!
面试的时候问你缓存,主要是考察缓存特性的理解,对 MC、Redis 的特色和使用方式的掌握。
- 对 DB 热点数据进行缓存减小 DB 压力;对依赖的服务进行缓存,提升并发性能;
- 单纯 K-V 缓存的场景可使用 MC,而须要缓存 list、set 等特殊数据格式,可使用 Redis;
- 须要缓存一个用户最近播放视频的列表可使用 Redis 的 list 来保存、须要计算排行榜数据时,可使用 Redis 的 zset 结构来保存。
要了解 MC 和 Redis 的经常使用命令,例如原子增减、对不一样数据结构进行操做的命令等。
了解 MC 和 Redis 在内存中的存储结构,这对评估使用容量会颇有帮助。
了解 MC 和 Redis 的数据失效方式和剔除策略,好比主动触发的按期剔除和被动触发延期剔除
要理解 Redis 的持久化、主从同步与 Cluster 部署的原理,好比 RDB 和 AOF 的实现方式与区别。
要知道缓存穿透、击穿、雪崩分别的异同点以及解决方案。
无论你有没有电商经验我以为你都应该知道秒杀的具体实现,以及细节点。
……..
欢迎去GitHub补充
若是想要在面试中得到更好的表现,还应了解下面这些加分项。
是要结合实际应用场景来介绍缓存的使用。例如调用后端服务接口获取信息时,可使用本地+远程的多级缓存;对于动态排行榜类的场景能够考虑经过 Redis 的 Sorted set 来实现等等。
最好你有过度布式缓存设计和使用经验,例如项目中在什么场景使用过 Redis,使用了什么数据结构,解决哪类的问题;使用 MC 时根据预估值大小调整 McSlab 分配参数等等。
最好能够了解缓存使用中可能产生的问题。好比 Redis 是单线程处理请求,应尽可能避免耗时较高的单个请求任务,防止相互影响;Redis 服务应避免和其余 CPU 密集型的进程部署在同一机器;或者禁用 Swap 内存交换,防止 Redis 的缓存数据交换到硬盘上,影响性能。再好比前面提到的 MC 钙化问题等等。
要了解 Redis 的典型应用场景,例如,使用 Redis 来实现分布式锁;使用 Bitmap 来实现 BloomFilter,使用 HyperLogLog 来进行 UV 统计等等。
知道 Redis4.0、5.0 中的新特性,例如支持多播的可持久化消息队列 Stream;经过 Module 系统来进行定制功能扩展等等。
……..
仍是那句话欢迎去GitHub补充。
此次是对我Redis系列的总结,这应该是Redis相关的最后一篇文章了,其实四篇看下来的小伙伴不少都从只知其一;不知其二到了一脸懵逼,哈哈开个玩笑。
我以为个人方式应该还好,大部分小伙伴仍是比较能理解的,这篇以后我就不会写Redis相关的文章了(秒杀看你们想看的热度吧),有啥问题能够微信找我,下个系列写啥?
你们不用急,下个系列前我会发个有意思的文章,是我在公司代码创意大赛拿奖的文章,我以为仍是有点东西,我忍不住分享一下,顺便就在那期发起投票吧哈哈。
我看到不少小伙伴都有评论说想看别的,大概搜集了一下,还没留言的这期赶忙哟:
愚辛 :想看计算机基础,网络和操做系统那些(FPX牛脾)
cherish君:讲讲dubbo常常遇到的面试题目,太多人喜欢问dubbo😃
Java架构养成记:真的很香啊,下一期讲Dubbbo(重点SPI)而后讲MQ好吗
小殿下:看完了全部的redis篇 但愿能够出ssm
程然:Dubbo Dubbo
linshi2019:这期明显是赶工之做啊
敖丙:这条我回一下,鞭策我,我很喜欢,不过说实话仍是但愿你们理解下,我双十一熬夜三天了,如今给大家写的时候也是值班回家2点左右了,我一天吃饭工做时间确定是固定的,想写点东西就只有挤出睡觉时间了,这种产出确定没周末全情投入写的来的质量高。
其实第一期看过来的小伙伴应该也知道,我在排版,还有不少文案,配图其实我一直都有在改进的,光是名词高亮我都要弄好久,由于怕你们看单一的黑白色调枯燥。
我是真的用心在搞,仍是但愿你们支持下理解下。
知乎、简书、思否、慕课手记没人看不知道为啥,懂行的老铁能够跟我说一下。
我只想说大家想看的确定都在我开头和GITHub那个图里吧,问题不大,后面都会写的。
最后感谢下,新浪微博的技术专家张雷。
他于2013年加入新浪微博,做为核心技术人员参与了微博服务化、混合云等多个重点项目,是微博开源的RPC框架Motan的技术负责人,同时也负责微博的Service Mesh方案的研发与推广,专一于高可用架构及服务中间件开发方向。
他负责的Motan框架天天承载着万亿级别的请求调用,是微博平台服务化的基石,每次的突发热点事件、每次的春晚流量高峰,都离不开Motan框架的支撑与保障。此外,他也屡次应邀在ArchSummit、WOT、GIAC技术峰会作技术分享。
感谢他对文章部分文案提供的支持和思路。
好了各位,以上就是这篇文章的所有内容了,能看到这里的人呀,都是人才。
我后面会每周都更新几篇《吊打面试官》系列和互联网经常使用技术栈相关的文章。若是你有什么想知道的,也能够留言给我,我一有时间就会写出来,咱们共同进步。
很是感谢人才们能看到这里,若是这个文章写得还不错,以为「敖丙」我有点东西的话 求点赞👍 求关注❤️ 求分享👥 求留言💬 对暖男我来讲 很是有用!!!
各位的支持和承认,就是我创做的最大动力,咱们下篇文章见!
敖丙 | 文 【原创】【转载请联系本人】
《吊打面试官》系列每周持续更新,能够关注个人公众号JavaFamily第一时间阅读和催更(公众号比博客早一到两天哟),里面也有我我的微信有什么问题也能够直接滴滴我,咱们一块儿进步。