1:Redis有哪些数据结构,底层编码有哪几类?其中有序链表采用了哪些不同的编码?
回答技巧
①大部分面试者都能够回答出五种类型,我们可以适当扩展一下,将Redis另外的4种高级类型的用法提一下,展示出自己知识的全面性。
②对于底层编码可以挑选其中一种讲解其类型的不同点和转换的条件,如果只能指出来在Redis配置文件中能够更改转换条件更好
③重点将有序表中的跳跃表编码进行解释
1:数据结构
从上图我们可以看出,Redis基本的五大数据类型对应的底层数据结构。同时可以发现后面的四种集合类型都是有两种的实现方式。
2:编码方式
①String的编码方式分别为int、embstr和raw
int:当存储的字符串全是数字时,此时使用int方式来存储;
embstr:当存储的字符串长度小于44个字符时,此时使用embstr方式来存储;
raw:当存储的字符串长度大于44个字符时,此时使用raw方式来存储;
②Hash的编码方式分别为ziplist和hashtable
ziplist 编码的哈希对象使用压缩列表作为底层实现
hashtable 编码的哈希对象使用字典作为底层实现
③List的编码方式分别为 linkedList、zipList和quickList
linkedList是一个双向链表
ziplist为压缩列表
quickList为linkedList与zipList的混合体,综合了两者的优点
④Set的编码方式分别为 intset和hashtable
intset 编码的集合对象使用整数集合作为底层实现
hashtable 编码的集合对象使用字典作为底层实现
⑤ZSet的编码方式分别为 zipList和skipList
ziplist 编码的有序集合对象使用压缩列表作为底层实现
skiplist 编码的有序集合对象使用 zset 结构作为底层实现
3:有序链表的编码
上面对有序链表ZSet已经做了解释,采用了zipList和skipList
zipList编码格式
skipList编码格式
2:Redis单线程为什么那么快?请描述Redis的IO多路复用机制?
回答技巧
①回答此问题,可以考虑从3点来回答即可,依次是,单线程,基于内存,IO多路复用,大多数面试者只能够回答出前2种,因此我们可以前两点稍微解释一下,而把重点放在第三点对IO多路复用的解释。
②对于多路复用大部分面试者只知道其概念,将其原理不见得可以说清楚,包括一些面试者也不见得可以说清楚,我们回答的时候可以采用类比的方式解释就更好了。例如下面的方式
1:单线程
采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗。
2:基于内存
完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。
3:IO多路复用
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
4:类比解释多路复用
我们类比一个实例:在课堂上让全班30个人同时做作业,做完后老师检查,30个学生的作业都检查完成才能下课。如何在有限的资源下,以最快的速度下课呢?
第一种:安排一个老师,按顺序逐个检查。先检查A,然后是B,之后是C、D。。。这中间如果有一个学生卡住,全班都会被耽误。这种模式就好比,你用循环挨个处理socket,根本不具有并发能力。这种方式只需要一个老师,但是耗时时间会比较长。
第二种:安排30个老师,每个老师检查一个学生的作业。 这种类似于为每一个socket创建一个进程或者线程处理连接。这种方式需要30个老师(最消耗资源),但是速度最快。
第三种:安排一个老师,站在讲台上,谁解答完谁举手。这时C、D举手,表示他们作业做完了,老师下去依次检查C、D的答案,然后继续回到讲台上等。此时E、A又举手,然后去处理E和A。这种方式可以在最小的资源消耗的情况下,最快的处理完任务。
3:请描述Redis拉链法解决全局哈希冲突的实现
回答技巧
①先以总结的方式回答解决的方法,例如:增加哈希桶的容量,减少哈希的冲突的概率,缩短链条的长度。
②再依次对总结的三句话进行拆解
1:全局哈希表
如上图所示,Redis是采用一个全局哈希表来存储键值对,全局哈希表又是由多个哈希桶组成,哈希桶中保存的是键值对的对应的指针。
因此在操作Redis的时候是通过哈希桶找到其对应的值得指针,进而进行下一步的增,删,查等操作。
2:数据量的增加
如上图所示,当随着Redis中存储的数据越来越多,则会出现同一个哈希桶对应多个entry,并且在数据结构上在每个entry后面都加上*next的指针,这样就形式了一个链表的方式存储数据。
这种方式的弊端会随着数据的增多,越来越明显,查询效率会越来越低。
3:解决方式
Redis此时就会对哈希表进行rehash操作,即重新hash,增加哈希桶的容量,减少哈希的冲突的概率,缩短链条的长度,从而达到redis的快速响应。
每次拷贝都是小部分的进行,避免了一次全量的拷贝造成线程的堵塞。
4:什么叫Redis的索引?
回答技巧
①相信大家听的最多都是数据库索引,Mysql。此处提出一个Redis索引其实是有一种偷换概念的味道在里面,用这种方法来考察面试者对即熟悉又陌生的知识点如何面对
②对于此类问题,如果确实是不知道,可以很明确的说,没有接触过此类问题,但是可以尝试用自己的方法来实现该功能。然后结合自己熟悉的知识,我们也来一个偷换概念的方法,进而说出自己的思路,其实说的思路可以完全照帮熟悉知识中的方法,稍微做调整。
解答
Redis并不直接支持索引,实际上就是基于ZSet有序集合,由于其中有数值,数值就是索引,因此可以根据它找到其他值。
5:如何使用Redis解决缓存穿透?
回答技巧
①以总结的方式回答,面对这个问题,可以提供两种解决方案
②再依次对每种方法进行解释
1:缓存穿透的定义
key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
2:解决方案
1:布隆过滤器
将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
关于布隆过滤器的原理,相信大家很熟悉,现在提供相关的文档,可以选择性进行阅读。
2:查询全部缓存
真实查询的数据缓存这个是必不可少的,如果一个查询返回的数据为空,无论是数据不存在,还是系统故障,仍然可以把这个空结果进行缓存,可以考虑将其过期时间设置的很短,超过五分钟之内,有效的避免缓存穿透。