面试常见问题:

  1. Redis的数据结构,是如何实现的?
  2. 集群
  3. 哨兵模式
  4. 主从复制
  5. 数据持久化
  6. 数据过期策略
  7. 缓存雪崩、缓存击穿
  8. Redis的事务

基础知识:

一、Redis实现高可用:

  • 数据持久化
  • 主从数据同步(主从复制)
  • Redis 哨兵模式(Sentinel)
  • Redis 集群(Cluster)

其中数据持久化保证了系统在发生宕机或者重启之后数据不会丢失,增加了系统的可靠性和减少了系统不可用的时间(省去了手动恢复数据的过程);而主从数据同步可以将数据存储至多台服务器,这样当遇到一台服务器宕机之后,可以很快地切换至另一台服务器以继续提供服务;哨兵模式用于发生故障之后自动切换服务器;而 Redis 集群提供了多主多从的 Redis 分布式集群环境,用于提供性能更好的 Redis 服务,并且它自身拥有故障自动切换的能力

二、数据持久化

  • RDB(Redis DataBase,快照方式)是将某一个时刻的内存数据,以二进制的方式写入磁盘。
  • AOF(Append Only File,文件追加方式)是指将所有的操作命令,以文本的形式追加到文件中。

RDB 默认的保存文件为 dump.rdb,优点是以二进制存储的,因此占用的空间更小、数据存储更紧凑,并且与 AOF 相比,RDB 具备更快的重启恢复能力。

  • 现在使用的技术是RDB + AOF混合

三、哨兵模式

Redis 哨兵模式就是用来监视 Redis 主从服务器的,当 Redis 的主从服务器发生故障之后,Redis 哨兵提供了自动容灾修复的功能,Redis 哨兵模块存储在 Redis 的 src/redis-sentinel 目录下。
image.png
哨兵的工作原理是每个哨兵会以每秒钟 1 次的频率,向已知的主服务器和从服务器,发送一个 PING 命令。如果最后一次有效回复 PING 命令的时间,超过了配置的最大下线时间(Down-After-Milliseconds)时,默认是 30s,那么这个实例会被哨兵标记为主观下线。

如果一个主服务器被标记为主观下线,那么正在监视这个主服务器的所有哨兵节点,要以每秒 1 次的频率确认主服务器是否进入了主观下线的状态。如果有足够数量(quorum 配置值)的哨兵证实该主服务器为主观下线,那么这个主服务器被标记为客观下线。此时所有的哨兵会按照规则(协商)自动选出新的主节点服务器,并自动完成主服务器的自动切换功能,而整个过程都是无须人工干预的

主从节点互相都会发送heartbeat信息。master默认每隔10秒发送一次heartbeat,slave node每隔1秒发送一个heartbeat。
通过sentinel模式启动redis后,自动监控master/slave的运行状态,基本原理是:心跳机制+投票裁决。每个sentinel会向其它sentinal、master、slave定时发送消息,以确认对方是否活着,如果发现对方在指定时间内未回应,则暂时认为对方宕机。若哨兵群中的多数sentinel都报告某一master没响应,系统才认为该master真正宕机,通过Raft投票算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。

四、Redis集群

五、主从复制

主从复制就是在Redis集群中,数据从主机复制到从机中。其中数据的复制是单向的,只能从主节点复制到从节点。

作用:

1、数据冗余:实现了数据的热备份。
2、故障恢复:如果是主节点发送故障,从节点可以成为主节点,继续服务。
3、负载均衡:主节点写、从节点读。可以分担大量的读操作。Redis就是读多写少的操作。从节点分担服务器的压力。
4、读写分离:主节点写、从节点读
5、高可用:是高可用的基础

主从复制的启用

主从复制的开始是从节点发起的,不需要主节点做任何事情。
开启的方式:

  1. 1、在服务器的配置文件中加入:
  2. slaveof <masterip> <masterport>
  3. 2、使用启动命令:
  4. redis-server start --slaveof <masterip> <masterport>
  5. 3、客户端命令:
  6. Redis服务器启动之后,直接通过客户端执行命令:
  7. slaveof <masterip> <masterport>

主从复制的流程:

主从复制过程大体可以分为3个阶段:连接建立阶段(即准备阶段)、数据同步阶段、命令传播阶段

连接建立阶段:
保存主节点信息 ==》 主从建立socket连接 ==》发送ping命令 ==》 权限验证 ==》发送从节点的端口信息
数据同步阶段:全量复制和局部复制
命令传播阶段:复制完成之后,主节点将自己接受到的命令发送给从节点,使得从节点与主节点的数据保持一致。

全量复制:
主节点接受到请求后,执行bgsave生成RDB。从节点接受RDB并且清除自己的存储,恢复RDB快照。主节点将缓存区域内的命令发送给从节点,从节点执行命令,主从节点的数据保持同步。

局部复制:
有3个重要的参数:复制偏移量、复制积压缓存、运行id(runid)

六、Redis的底层数据结构;

redis中的数据结构为:

  • String: int raw embstr
  • List:linkedList、ziplist、quicklist
  • hash:hashtable、ziplist
  • Set:hashtable、inset
  • ZSet:ziplist、skiplist

image.png

ziplist:

压缩列表(ziplist)是一组连续内存块组成的顺序的数据结构,压缩列表能够节省空间,压缩列表中使用多个节点来存储数据。
压缩列表是列表键和哈希键底层实现的原理之一,「压缩列表并不是以某种压缩算法进行压缩存储数据,而是它表示一组连续的内存空间的使用,节省空间」,压缩列表的内存结构图如下:
image.png
压缩列表中每一个节点表示的含义如下所示:

  1. zlbytes:4个字节的大小,记录压缩列表占用内存的字节数。
  2. zltail:4个字节大小,记录表尾节点距离起始地址的偏移量,用于快速定位到尾节点的地址。
  3. zllen:2个字节的大小,记录压缩列表中的节点数。
  4. entry:表示列表中的每一个节点。
  5. zlend:表示压缩列表的特殊结束符号’0xFF’。

再压缩列表中每一个entry节点又有三部分组成,包括previous_entry_ength、encoding、content。

  1. previous_entry_ength表示前一个节点entry的长度,可用于计算前一个节点的其实地址,因为他们的地址是连续的。
  2. encoding:这里保存的是content的内容类型和长度。
  3. content:content保存的是每一个节点的内容。

image.png

intset:整数列表

inset也叫做整数集合,用于保存整数值的数据结构类型,它可以保存int16_t、int32_t 或者int64_t 的整数值。
在整数集合中,有三个属性值encoding、length、contents[],分别表示编码方式、整数集合的长度、以及元素内容,length就是记录contents里面的大小。

在整数集合新增元素的时候,若是超出了原集合的长度大小,就会对集合进行升级,具体的升级过程如下:

  1. 首先扩展底层数组的大小,并且数组的类型为新元素的类型。
  2. 然后将原来的数组中的元素转为新元素的类型,并放到扩展后数组对应的位置。
  3. 整数集合升级后就不会再降级,编码会一直保持升级后的状态。

skiplist 跳跃表

skiplist也叫做「跳跃表」,跳跃表是一种有序的数据结构,它通过每一个节点维持多个指向其它节点的指针,从而达到快速访问的目的。

skiplist由如下几个特点:

  1. 有很多层组成,由上到下节点数逐渐密集,最上层的节点最稀疏,跨度也最大。
  2. 每一层都是一个有序链表,只包含两个节点,头节点和尾节点。
  3. 每一层的每一个每一个节点都含有指向同一层下一个节点和下一层同一个位置节点的指针。
  4. 如果一个节点在某一层出现,那么该以下的所有链表同一个位置都会出现该节点。

具体实现的结构图如下所示:
image.png
在跳跃表的结构中有head和tail表示指向头节点和尾节点的指针,能后快速的实现定位。level表示层数,len表示跳跃表的长度,BW表示后退指针,在从尾向前遍历的时候使用。
BW下面还有两个值分别表示分值(score)和成员对象(各个节点保存的成员对象)。
跳跃表的实现中,除了最底层的一层保存的是原始链表的完整数据,上层的节点数会越来越少,并且跨度会越来越大。
跳跃表的上面层就相当于索引层,都是为了找到最后的数据而服务的,数据量越大,条表所体现的查询的效率就越高,和平衡树的查询效率相差无几。

参考文章

  1. Redis专题:一文搞懂主从复制原理!
  2. 最详细的Redis五种数据结构详解(理论+实战)-知乎