一、Redis基础

简介,常用命令,数据类型,数据结构,客户端

1、Redis有几种数据类型,底层分别是怎么存储的?

(1)字符串String
①概述:进制安全,可以包含任何类型数据,可以存储图片或者序列化的对象,值能存储的最大容量为512M。
②应用场景:共享session、分布式锁,计数器、限流。
③内部编码:int(8字节长整型)、embstr(小于等于39字节字符串)、raw(大于39个字节字符串)
(2)哈希Hash
①概述:键值对集合,String类型的field和value的映射表,适合用于存储对象
②应用场景:缓存用户信息等
③内部编码:ziplist(压缩列表) 、hashtable(哈希表)
(3)列表List
①概述:存储多个有序的字符串,一个列表最多可以存储2^32-1个元素。
②应用场景:消息队列,文章列表
③内部编码:ziplist(压缩列表)、linkedlist(链表)
(4)集合Set
①概述:String类型的无序集合,保存多个的字符串元素,但是不允许重复元素
②应用场景:用户标签、生成随机数抽奖、社交需求等
③内部编码:intset(整数集合)、hashtable(哈希表)
(5)有序集合Sorted Set
①概述:String类型元素的集合,元素不允许重复,每个元素都会关联一个double类型的分数(通过分数来为集合中的成员进行从小到大的排序,分数是可以重复的)
②应用场景:排行榜,社交需求(如用户点赞)
③底层内部编码:ziplist(压缩列表)、skiplist(跳跃表)
(6)三种特殊数据类型
① Geo:Redis3.2推出的,地理位置定位,用于存储地理位置信息,并对存储的信息进行操作。
② HyperLogLog:用来做基数统计算法的数据结构,如统计网站的UV。
③ Bitmaps :用一个比特位来映射某个元素的状态,在Redis中,它的底层是基于字符串类型实现的,可以把bitmaps成作一个以比特位为单位的数组

2、能说一下Redis每种数据结构的使用场景吗?

(1)String
①记录每一个用户的访问次数,或者记录每一个商品的浏览次数
②缓存频繁读取不常修改的信息
③限定某个ip特定时间内的访问次数
④分布式session
(2)Hash
当对象的某个属性需要频繁修改,比如商品的价格、销量、关注数、评价数等可能经常发生变化的属性,适合存储在Hash中
(3)List
定时排行榜
(4)Set
收藏夹
(5)Sorted Set
实时排行榜

3、Memcache与Redis的区别都有哪些?

(1)存储方式
Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
Redis有部分存在硬盘上,这样能保证数据的持久性。
(2)数据支持类型
Memcache对数据类型支持相对简单。
Redis有丰富的数据类型。
(3)底层模型不同
底层实现方式以及与客户端之间通信的应用协议不一样。

4、Redis常用的客户端有哪些?

(1)Jedis
①Redis的Java实现客户端,提供了比较全面的Redis命令的支持
②优点:比较全面的提供了Redis的操作特性
(2)Redisson
①实现了分布式和可扩展的Java数据结构
②优点:提供很多分布式相关操作服务
(3)Lettuce
①高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器
②优点:方法调用是异步,API是线程安全的,可以操作单个Lettuce连接来完成各种操作

5、说说Redis为什么快

https://mp.weixin.qq.com/s/wf08G3PHpfbJKiU7ZVO9Lw
(1)基于内存存储实现
内存读写是比在磁盘快很多的,Redis基于内存存储实现的数据库,减少了一些不必要的 I/O 操作。
(2)高效的数据结构
底层多种数据结构支持不同的数据类型,支持Redis存储不同的数据;
不同数据结构的设计,使得数据存储时间复杂度降到最低。
image.png
(3)合理的数据编码
根据字符串的长度及元素的个数适配不同的编码格式。
image.png
(4)合理的线程模型
I/O多路复用模型同时监听客户端连接;
单线程在执行过程中不需要进行上下文切换,减少了耗时。
(5)虚拟内存机制
暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。

6、聊聊Redis的zset,它是怎么实现的?

(1)底层内部编码(底层实现)
ziplist(压缩列表)、skiplist(跳跃表)
(2)压缩列表ziplist

zset满足以下条件时使用压缩列表:当成员的数量小于128 个;每个成员的字符串长度都小于64个字节。

image.png
(3)跳跃表skiplist
在链表的基础上,增加了多级索引,通过索引位置的几个跳转,实现数据的快速定位,其插入、删除、查找的时间复杂度均为 O(logN)
image.png

7、Hash冲突怎么办?

Redis通过链式哈希解决冲突,即同一个桶里面的元素使用链表保存。
当链表过长就会导致查找性能变差可能,所以 Redis 为了追求快,使用了两个全局哈希表,用于 rehash 操作,增加现有的哈希桶数量,减少哈希冲突。
开始默认使用 「hash 表 1 」保存键值对数据,「hash 表 2」 此刻没有分配空间。当数据越来越多触发 rehash 操作,则执行以下操作:

  • 给 「hash 表 2 」分配更大的空间;
  • 将 「hash 表 1 」的数据重新映射拷贝到 「hash 表 2」 中;
  • 释放 「hash 表 1」 的空间。

    将 hash表1 的数据重新映射到 hash表2 的过程中并不是一次性的,这样会造成 Redis 阻塞,无法提供服务。 采用了渐进式 rehash,每次处理客户端请求的时候,先从「 hash 表 1」 中第一个索引开始,将这个位置的 所有数据拷贝到 「hash 表 2」 中,就这样将 rehash 分散到多次请求过程中,避免耗时阻塞。

8、什么是Redis?它主要用来什么的?

Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
被广泛应用于缓存,另外,Redis也经常用来做分布式锁。除此之外,Redis支持事务、持久化、LUA 脚本、LRU 驱动事件、多种集群方案。


二、Redis缓存

1、缓存雪崩、缓存穿透、缓存预热、缓存击穿、缓存降级的区别是什么?

(1)缓存穿透
①定义
指用户请求的数据在缓存中不存在即没有命中,同时在数据库中也不存在,导致用户每次请求该数据都要去数据库中查询一遍,然后返回空(绕过Redis使得数据库崩掉)
②解决方案
布隆过滤器(推荐)

  • 定义:Bloom Filter,简称BF,是一种空间效率高的概率型数据结构
  • 作用:专门用来检测集合中是否存在特定的元素
  • 设计思想:由一个长度为m比特的位数组(初始化为0)与k个哈希函数组成的数据结构。当要向布隆过滤器中插入一个元素时,该元素经过k个哈希函数计算产生k个哈希值,以哈希值作为位数组中的下标,将所有k个对应的比特值由0置为1。当要查询一个元素时,同样将其经过哈希函数计算产生哈希值,然后检查对应的k个比特值:如果有任意一个比特为0,表明该元素一定不在集合中;如果所有比特均为1,表明该集合有可能性在集合中。

    image.png

  • 优点:节省空间(不需要存储数据本身,只需要存储数据对应hash比特位);时间复杂度低

  • 缺点:存在假阳性(布隆过滤器判断存在,可能出现元素不在集合中,判断准确率取决于哈希函数的个数);不能删除元素
  • 适用场景:爬虫系统url去重、垃圾邮件过滤、黑名单

返回空对象

  • 可以将返回的空对象写到缓存中,这样下次请求该key时直接从缓存中查询返回空对象,请求不会落到持久层数据库。
  • 避免存储过多空对象,通常会给空对象设置一个过期时间
  • 存在问题:a. 如果有大量的key穿透,缓存空对象会占用宝贵的内存空间。b. 空对象的key设置了过期时间,在这段时间可能会存在缓存和持久层数据不一致的场景。

(2)缓存击穿
①定义
指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库(正面刚)
②危害
数据库瞬时压力骤增,造成大量请求阻塞。
③解决方案

  • 使用互斥锁:让一个线程回写缓存,其他线程等待回写缓存线程执行完,重新读缓存即可
  • 设置热点数据永不过期:针对热点key不设置过期时间,把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建

(3)缓存雪崩
①定义
指缓存中数据大批量到过期时间,而查询数据量巨大,请求直接落到数据库上,引起数据库压力过大甚至宕机。
②与缓存击穿的比较
缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
③解决方案

  • 均匀过期:将key的过期时间后面加上一个随机数,设置不同的过期时间,让缓存失效的时间点尽量均匀。
  • 加互斥锁:同一时间只让一个线程构建缓存,其他线程阻塞排队。
  • 缓存永不过期:缓存在物理上永远不过期,用一个异步的线程更新缓存(热点数据考虑不失效)
  • 双层缓存策略:使用主备两层缓存,主缓存的有效期按照经验值设置,设置为主读取的缓存,主缓存失效后从数据库加载最新值;备份缓存的有效期长,获取锁失败时读取的缓存,主缓存更新时需要同步更新备份缓存。

(4)缓存预热
①定义
系统上线后,将相关的缓存数据直接加载到缓存系统,这样就可以避免在用户请求的时候,先查询数据库,然后再将数据回写到缓存。
②不使用缓存预热
Redis 初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中, 对数据库造成流量的压力。
③操作方法

  • 数据量不大的时候,工程启动的时候进行加载缓存动作
  • 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新
  • 数据量太大的时候,优先保证热点数据进行提前加载到缓存

(5)缓存降级
①定义
指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。
②说明

  • 在项目实战中通常会将部分热点数据缓存到服务的内存中
  • 降级一般是有损的操作,所以要尽量减少

    2、Redis过期策略

    set key的时候,可以给它设置一个过期时间

    Redis中同时使用了惰性过期定期过期两种过期策略

(1)定时过期
每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即对key进行清除。
①优点:立即清除过期的数据,对内存很友好
②缺点:占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
(2)惰性过期
只有当访问一个key时,才会判断该key是否已过期,过期则清除。
①优点:最大化地节省CPU资源
②缺点:对内存非常不友好,极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
(3)定期过期
每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键)。
特点:前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

3、布隆过滤器是什么

(1)定义
布隆过滤器是一种占用空间很小的数据结构,它由一个很长的二进制向量和一组Hash映射函数组成。
(2)作用
用于检索一个元素是否在一个集合中,可以解决缓存穿透问题
image.png
(3)特点
空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
(4)原理
https://mp.weixin.qq.com/s/BfAHWsnQkre4iwOuT-DVsA
假设我们有个集合A,A中有n个元素。利用k个哈希散列函数,将A中的每个元素映射到一个长度为a位的数组B中的不同位置上,这些位置上的二进制数均设置为1。如果待检查的元素,经过这k个哈希散列函数的映射后,发现其k个位置上的二进制数全部为1,这个元素很可能属于集合A,反之,一定不属于集合A。


三、Redis锁机制


四、Redis持久化

1、Redis如何做持久化的?能说一下RDB和AOF的实现原理吗?

(1)什么是持久化?
持久化就是把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)
(2)Redis为什么要持久化?
Redis中的数据都存储在内存中,当你重启系统或者关闭系统,之前缓存在内存中的数据都会丢失再也不能找回。为了上述情况,Redis需要实现持久化将内存中的数据存储起来
(3)Redis如何实现持久化?
①RDB持久化:指在指定的时间间隔内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中(Redis默认的持久化方式)——执行完操作后,在指定目录下会生成一个dump.rdb文件,Redis 重启的时候,通过加载dump.rdb文件来恢复数据。
②AOF持久化:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾。
③不使用持久化:数据在服务器运行的时候存在
④同时开启RDB和AOF:当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
(4)RDB持久化
①分类

  • 手动触发:对应save命令,会阻塞当前Redis服务器,直到RDB过程完成为止(对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用)
  • 自动触发:对应bgsave命令,Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束(阻塞只发生在fork阶段,一般时间很短)

②配置

  1. #在redis.conf配置,表示xx秒内数据修改xx次时自动触发bgsave
  2. save <seconds> <changes>
  3. #关闭自动触发
  4. save ""

③触发bgsave

  • 如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点
  • 默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则 自动执行bgsave

④bgsave工作机制
image.png

  • 执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如果存在,bgsave命令直接返回
  • 父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞(可以通过info stats命令查看latest_fork_usec选项,可以获取最近一个fork操作的耗时,单位为微秒)
  • 父进程fork完成后,bgsave命令返回“Background saving started”信息并不再阻塞父进程,可以继续响应其他命令
  • 子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换(执行lastsave命令可以获取最后一次生成RDB的时间,对应info 统计的rdb_last_save_time选项)
  • 进程发送信号给父进程表示完成,父进程更新统计信息

(5)AOF持久化
①定义
以独立日志的方式记录每次写命令, 重启时再重新执行AOF文件中的命令来恢复数据
②作用
解决了数据持久化的实时性
③工作机制

开启AOF功能需要配置:appendonly yes AOF文件名通过appendfilename配置设置,默认文件名是appendonly.aof

  • 命令写入(append):所有的写入命令会追加到缓冲区(aof_buf)中
  • 文件同步(sync):AOF缓冲区根据对应的策略向硬盘做同步操作
  • 文件重写(rewrite):随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的
  • 重启加载 (load):当Redis服务器重启时,可以加载AOF文件进行数据恢复

(6)RDB的优缺点
①优点

  • 保存了某个时间点的数据集,适用于数据集的备份
  • RDB是一个紧凑的单一文件,方便传送,适用于灾难恢复
  • 最大化 Redis 的性能(RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他 IO 操作)
  • 与AOF相比,在恢复大的数据集的时候,RDB 方式会更快一些

②缺点

  • 保存整个数据集任务繁重,有数据丢失的风险
  • RDB 需要经常 fork 子进程来保存数据集到硬盘上,当数据集比较大的时候,fork 的过程是非常耗时的,可能会导致 Redis 在一些毫秒级内不能响应客户端的请求

(7)AOF的优缺点
①优点

  • 可以使用不同的 fsync 策略,Redis性能较好
  • 一旦出现故障,你最多丢失1秒的数据,可以更好地保护数据不丢失
  • appen-only 模式写入性能比较高
  • 适合做灾难性的误删除紧急恢复

②缺点

  • 对于同一份文件,AOF 文件要比 RDB 快照大;
  • AOF 开启后,会对写的 QPS 有所影响,相对于 RDB 来说写 QPS 要下降;
  • 数据库恢复比较慢, 不合适做冷备。

    2、Redis挂了怎么办?

    如果Redis服务器挂了,数据就会丢失。为了避免数据丢失了,Redis提供了持久化(RDB和AOF),即把数据保存到磁盘。

    3、为什么AOF是执行完命令后才记录日志的?

    因为Redis在向AOF记录日志时,不会先对这些命令进行语法检查,如果先记录日志再执行命令,日志中可能记录了错误的命令,Redis使用日志回复数据时,可能会出错。
    (1)存在的风险
    ①执行完命令还没记录日志时,宕机了会导致数据丢失
    ②AOF不会阻塞当前命令,但是可能会阻塞下一个操作
    (2)风险解决方案
    ——AOF机制的三种写回策略appendfsync
    always:同步写回,每个子命令执行完,都立即将日志写回磁盘。
    everysec:每个命令执行完,只是先把日志写到AOF内存缓冲区,每隔一秒同步到磁盘。
    no:只是先把日志写到AOF内存缓冲区,有操作系统去决定何时写入磁盘。

    always同步写回,可以基本保证数据不丢失,no策略则性能高但是数据可能会丢失,一般可以考虑折中选择everysec。

4、RDB持久化过程中数据能修改吗?

借助操作系统的写时复制技术(copy-on-write,COW),在执行快照的同时,正常处理写操作。

5、如何选择RDB和AOF

(1)如果数据不能丢失,RDB和AOF混用
(2)如果只作为缓存使用,可以承受几分钟的数据丢失的话,可以只使用RDB。
(3)如果只使用AOF,优先使用everysec的写回策略。

6、Redis主从同步是怎样的过程?

image.png
(1)第一阶段:主从库间建立连接、协商同步
①从库向主库发送psync命令,告诉它要进行数据同步。
②主库收到psync命令后,响应FULLRESYNC命令(表示第一次复制采用的是全量复制),并带上主库runID和主库目前的复制进度offset
(2)第二阶段:主库把数据同步到从库,从库收到数据后,完成本地加载
①主库执行bgsave命令,生成RDB文件,接着将文件发给从库。从库接收到RDB文件后,会先清空当前数据库,然后加载RDB文件。
②主库把数据同步到从库的过程中,新来的写操作,会记录到replication buffer
(3)第三阶段,主库把新写的命令,发送到从库
主库完成RDB发送后,会把replication buffer中的修改操作发给从库,从库再重新执行这些操作。这样主从库就实现同步。

7、在生成 RDB 期间,Redis 可以同时处理写请求么?

可以的,Redis 使用操作系统的多进程写时复制技术(Copy On Write)来实现快照持久化,保证数据一致性。
image.png
Redis 在持久化时会调用 glibc 的函数fork产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。当主线程执行写指令修改数据的时候,这个数据就会复制一份副本, bgsave 子进程读取这个副本数据写到 RDB 文件。
这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。


五、Redis集群

1、什么是Redis主从复制?

(1)概念
指将一台Redis服务器的数据,复制到其他的Redis服务器(前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点)
(2)主从复制的作用
① 数据冗余:实现了数据的热备份
② 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复
③ 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载
④ 高可用基础:主从复制是哨兵和集群能够实施的基础
(3)说明
① Redis主从模式,就是部署多台Redis服务器,有主库和从库,它们之间通过主从复制,以保证数据副本的一致。
② 主从库之间采用的是读写分离的方式,其中主库负责读操作和写操作,从库则负责读操作。
③ 如果Redis主库挂了,切换其中的从库成为主库。

2、Sentinel(哨兵模式)的原理你能讲一下吗?

(1)解决的问题
Redis 的主从复制模式下,一旦主节点由于故障不能提供服务,需要手动将从节点晋升为主节点,同时还要通知客户端更新主节点地址,这种故障处理方式不太能被接受。
(2)定义
哨兵模式是Redis高可用的实现方案。哨兵模式是一个管理多个Redis实例的工具,它可以实现对 Redis的监控、通知、自动故障转移。哨兵模式通常由一组 Sentinel 节点和一组(或多组)主从复制节点组成。
(3)主要作用
能够自动完成故障发现和故障转移,并通知客户端,从而实现高可用。
(4)原理
①心跳机制
每个 Sentinel 节点会向主节点、从节点、以及其余 Sentinel 节点定时发送 ping 命令作为心跳检测,来确认这些节点是否可达。

  • 在哨兵模式创建时,需要通过配置指定 Sentinel 与 Redis Master Node 之间的关系,然后 Sentinel 会从主节点上获取所有从节点的信息,之后 Sentinel 会定时向主节点和从节点发送 info 命令获取其拓扑结构和状态信息。
  • 基于 Redis 的订阅发布功能, 每个 Sentinel 节点会向主节点的 sentinel:hello 频道上发送该 Sentinel 节点对于主节点的判断以及当前 Sentinel 节点的信息 ,同时每个 Sentinel 节点也会订阅该频道, 来获取其他 Sentinel 节点的信息以及它们对主节点的判断。

②故障转移

  • 只需要一个 Sentinel 节点来完成,基于 Raft 算法在 Sentinel 节点之间选出一个 Sentinel 领导者来进行故障转移的工作。
  • 具体步骤:在从节点列表中选出一个节点作为新的主节点——Sentinel领导者节点会对选出来的从节点执行slaveof no one命令让其成为主节点——Sentinel领导者节点会向剩余的从节点发送命令,让他们从新的主节点上复制数据——Sentinel领导者会将原来的主节点更新为从节点, 并对其进行监控, 当其恢复后命令它去复制新的主节点

    3、引入Cluster集群的原因

    不管是主从模式还是哨兵模式都只能由一个master在写数据,在海量数据高并发场景,一个节点写数据容易出现瓶颈,引入Cluster模式可以实现多个节点同时写数据,解决了大数据量存储导致的各种慢问题,同时也便于横向拓展。

    4、主从之间数据如何保证一致性?

    主从架构采用了读写分离:
    (1)读操作:主、从库都可以执行;
    (2)写操作:主库先执行,之后将写操作同步到从库
    image.png

    5、主从复制如何实现的?

    分为三种情况:
    第一次主从库全量复制;
    主从正常运行期间的同步;
    主从库间网络断开重连同步。

    6、第一次主从复制怎么实现?

    image.png
    (1)建立连接:从库会和主库建立连接,从库执行replicaof并发送psync命令并告诉主库即将进行同步,主库确认回复后,主从库间就开始同步了。
    (2)主库同步数据给从库:master 执行bgsave命令生成 RDB 文件,并将文件发送给从库,同时主库为每一个 slave 开辟一块replication buffer缓冲区记录从生成 RDB 文件开始收到的所有写命令。从库保存 RDB 并清空内存数据再加载 RDB 数据到内存中。
    (3)发送 RDB 之后接收到的新的写命令到从库:在生成 RDB 文件之后的写操作并没有记录到刚刚的 RDB 文件中,为了保证主从库数据的一致性,所以主库会在内存中使用一个叫 replication buffer 记录 RDB 文件生成后的所有写操作。并将里面的数据发送到 slave。

    7、主从库间的网络断了咋办?断开后要重新全量复制么?

    (1)在 Redis 2.8 之前,如果主从库在命令传播时出现了网络闪断,那么,从库就会和主库重新进行一次全量复制,开销非常大。从 Redis 2.8 开始,网络断了之后,主从库会采用增量复制的方式继续同步。
    (2)增量复制
    ①定义:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点
    ②实现原理:

  • 不管在什么时候master都会将写指令操作记录在repl_backlog_buffer中,因为内存有限, repl_backlog_buffer 是一个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容。

  • master 使用master_repl_offset记录自己写到的位置偏移量,slave 则使用slave_repl_offset记录已经读取到的偏移量。

image.png

  • 当主从断开重连后,slave 会先发送psync命令给 master,同时将自己的 runIDslave_repl_offset发送给 master。master 只需要把master_repl_offsetslave_repl_offset之间的命令同步给从库即可。

    8、那完成全量同步后,正常运行过程中如何同步数据呢?

    当主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,使用长连接的目的就是避免频繁建立连接导致的开销。

    9、你知道哨兵集群原理么?

    哨兵是 Redis 的一种运行模式,它专注于对 Redis 实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个 Redis 系统的可用性。
    Redis - 图13
    作用:

  • 监控:持续监控 master 、slave 是否处于预期工作状态。

  • 自动切换主库:当 master 运行故障,哨兵启动自动故障恢复流程,从 slave 中选择一台作为新 master。
  • 通知:让 slave 执行 replicaof ,与新的 master 同步;并且通知客户端与新 master 建立连接。

    10、哨兵之间是如何知道彼此的?

    哨兵与 master 建立通信,利用 master 提供发布/订阅机制发布自己的信息。master 有一个 sentinel 的专用通道,用于哨兵之间发布和订阅消息。

    11、哨兵之间虽然建立连接了,但是还需要和 slave 建立连接,不然没法监控他们呀,如何知道 slave 并监控他们的?

    哨兵向 master 发送 INFO 命令,master 接收到命令后,便将 slave 列表告诉哨兵。哨兵根据 master 响应的 slave 名单信息与每一个 salve 建立连接,并且根据这个连接持续监控哨兵。

    12、什么是 Cluster 集群?

    Redis 集群是一种分布式数据库方案,集群通过分片(sharding)来进行数据管理,并提供复制和故障转移功能。
    将数据划分为 16384 的 slots,每个节点负责一部分槽位,槽位的信息存储于每个节点中。
    image.png
    三个节点相互连接组成一个对等的集群,它们之间通过 Gossip 协议相互交互集群信息,最后每个节点都保存着其他节点的 slots 分配情况。

    13、哈希槽如何映射到 Redis 实例上呢?

    image.png
    根据键值对的 key,使用 CRC16 算法,计算出一个 16 bit 的值;
    将 16 bit 的值对 16384 执行取模,得到 0 ~ 16383 的数表示 key 对应的哈希槽;
    根据该槽信息定位到对应的实例。

    14、Cluster如何实现故障转移?

    Redis 集群节点采用 Gossip 协议来广播自己的状态以及自己对整个集群认知的改变。比如一个节点发现某个节点失联了 (PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息。
    如果一个节点收到了某个节点失联的数量 (PFail Count) 已经达到了集群的大多数,就可以标记该节点为确定下线状态 (Fail),然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换。

    15、客户端怎么确定访问的数据分布在哪个实例上呢?

    Redis 实例会将自己的哈希槽信息通过 Gossip 协议发送给集群中其他的实例,实现了哈希槽分配信息的扩散。这样,集群中的每个实例都有所有哈希槽与实例之间的映射关系信息。
    当客户端连接任何一个实例,实例就将哈希槽与实例的映射关系响应给客户端,客户端就会将哈希槽与实例映射信息缓存在本地。
    当客户端请求时,会计算出键所对应的哈希槽,再通过本地缓存的哈希槽实例映射信息定位到数据所在实例上,再将请求发送给对应的实例。
    image.png

    16、说说Redis高可用?

    https://mp.weixin.qq.com/s/s6tQcaBdTYGC2l86CRAXHg
    主从复制、哨兵模式、Cluster集群

    17、Redis主从复制(同步)过程

    image.png
    (1)第一阶段:主从库间建立连接、协商同步
    ①从库向主库发送psync命令,告诉它要进行数据同步。
    ②主库收到psync命令后,响应FULLRESYNC命令(表示第一次复制采用的是全量复制),并带上主库runID和主库目前的复制进度offset
    (2)第二阶段:主库把数据同步到从库,从库收到数据后,完成本地加载
    ①主库执行bgsave命令,生成RDB文件,接着将文件发给从库。从库接收到RDB文件后,会先清空当前数据库,然后加载RDB文件。
    ②主库把数据同步到从库的过程中,新来的写操作,会记录到replication buffer
    (3)主库把新写的命令,发送到从库
    主库完成RDB发送后,会把replication buffer中的修改操作发给从库,从库再重新执行这些操作,这样主从库就实现同步啦。

    18、一主多从,全量复制时主库压力问题

    (1)问题
    如果是一主多从模式,从库很多的时候,如果每个从库都要和主库进行全量复制的话,主库的压力是很大的。
    (2)原因
    因为主库fork进程生成RDB,这个fork的过程是会阻塞主线程处理正常请求的。同时,传输大的RDB文件也会占用主库的网络宽带。
    (3)解决
    部署主从集群时,选择硬件网络配置比较好的一个从库,让它跟部分从库再建立主从关系。
    image.png

六、Redis线程模型

1、讲解一下Redis的线程模型?

Redis内部使用文件事件处理器file event handler,是单线程的,采用 IO 多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器进行处理。

2、为什么Redis单线程模型也能效率这么高?

(1)纯内存操作
(2)基于非阻塞的 IO 多路复用机制
(3)单线程避免了多线程的频繁上下文切换问题

3、Redis单线程有什么好处?

(1)不会因为线程创建导致的性能消耗;
(2)避免上下文切换引起的 CPU 消耗,没有多线程切换的开销;
(3)避免了线程之间的竞争问题,比如添加锁、释放锁、死锁等,不需要考虑各种锁问题。
(4)代码更清晰,处理逻辑简单。


七、Redis通信协议


八、内存淘汰策略

1、Redis的内存淘汰机制

(1)定义
指当缓存内存不足时,通过淘汰旧数据处理新加入数据选择的策略。
(2)淘汰策略分类
① noeviction
默认策略,对于写请求直接返回错误,不进行淘汰。
② allkeys-lru
从所有的key中使用近似LRU算法进行淘汰。
③ volatile-lru
从设置了过期时间的key中使用近似LRU算法进行淘汰。
④ allkeys-random
从所有的key中随机淘汰。
⑤ volatile-random
从设置了过期时间的key中随机淘汰。
⑥ volatile-ttl
在设置了过期时间的key中根据key的过期时间进行淘汰,越早过期的越优先被淘汰。
⑦ allkeys-lfu
从所有的key中使用近似LFU算法进行淘汰。
⑧ volatile-lfu
从设置了过期时间的key中使用近似LFU算法进行淘汰。

2、LRU算法

(1)定义
LRU(Least Recently Used),即最近最少使用,是一种缓存置换算法。
(2)核心思想
如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉。
(3)在Redis中的实现
①Redis使用的是近似LRU算法,跟常规的LRU算法不太一样。近似LRU算法通过随机采样法淘汰数据,每次随机出5个(默认)key,从里面淘汰掉最近最少使用的key。
②可以通过maxmemory-samples参数修改采样数量,比如maxmemory-samples 10。配置的采样数量越大,淘汰的结果越接近于严格的LRU算法,但因此耗费的CPU也很高。
③实现:给每个key增加了一个额外增加了一个24bit的字段,用来存储该key最后一次被访问的时间。

3、LFU算法

(1)概述
LFU(Least Frequently Used),Redis4.0新加的一种淘汰策略
(2)核心思想
根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来。
(3)说明
LFU算法能更好的表示一个key被访问的热度。

4、Redis内存淘汰策略

(1)volatile-lru:当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LRU(最近最少使用)算法进行淘汰;
(2)allkeys-lru:当内存不足以容纳新写入数据时,从所有key中使用LRU(最近最少使用)算法进行淘汰。
(3)volatile-lfu:Redis4.0版本新增,当内存不足以容纳新写入数据时,在过期的key中,使用LFU(最少访问算法)进行删除key。
(4)allkeys-lfu:Redis4.0版本新增,当内存不足以容纳新写入数据时,从所有key中使用LFU算法进行淘汰。
(5)volatile-random:当内存不足以容纳新写入数据时,从设置了过期时间的key中,随机淘汰数据。
(6)allkeys-random:当内存不足以容纳新写入数据时,从所有key中随机淘汰数据。
(7)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的key中,根据过期时间进行淘汰,越早过期的优先被淘汰。
(8)noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。


九、其它知识点

1、Redis有事务机制吗?

有。
Redis事务生命周期:
(1)开启事务:使用MULTI开启一个事务
(2)命令入队列:每次操作的命令都会加入到一个队列中,但命令此时不会真正被执行
(3)提交事务:使用EXEC命令提交事务,开始顺序执行队列中的命令

2、Redis事务到底是不是原子性的?

官方认为Redis事务是一个原子操作,这是站在执行与否的角度考虑的。但是从ACID原子性定义来看,严格意义上讲Redis事务是非原子型的,因为在命令顺序执行过程中,一旦发生命令执行错误Redis是不会停止执行然后回滚数据。
(1)官方文档对事务的定义
①事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
②事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
(2)关系型数据对原子性的定义
一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

3、Redis为什么不支持回滚?

只有当被调用的Redis命令有语法错误时或者对某个键执行不符合其数据类型的操作,这条命令才会执行失败。实际上,这就意味着只有程序错误才会导致Redis命令执行失败,这种错误很有可能在程序开发期间发现,一般很少在生产环境发现。支持事务回滚能力会导致设计复杂,这与Redis的初衷相违背,Redis的设计目标是功能简化及确保更快的运行速度。

4、Redis事务相关的命令有哪几个?

(1)WATCH
被WATCH的键会被监视,并会发觉这些键是否被改动过了。如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回nil-reply来表示事务已经失败。
(2)MULTI
用于开启一个事务。MULTI执行之后,客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行,而是被放到一个队列中,当 EXEC命令被调用时, 所有队列中的命令才会被执行。
(3)UNWATCH
取消 WATCH 命令对所有 key 的监视,一般用于DISCARD和EXEC命令之前。
(4)DISCARD
作用是事务会被放弃, 事务队列会被清空,并且客户端会从事务状态中退出。
(5)EXEC
负责触发并执行事务中的所有命令。如果客户端成功开启事务后执行EXEC,那么事务中的所有命令都会被执行。即使事务中有某条/某些命令执行失败了,事务队列中的其他命令仍然会继续执行,Redis不会停止执行事务中的命令。

5、假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用keys指令可以扫出指定模式的key列表:keys pre*

6、如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

Redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。
这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

7、如果有大量的key需要设置同一时间过期,一般需要注意什么?

到过期的那个时间点,Redis可能会出现短暂的卡顿现象,严重的话可能会导致服务器雪崩。
一般在过期时间上加一个随机值,让过期时间尽量分散。