一、Redis是单线程还是多线程

Redis6.0之前的单线程指的是其网络IO和键值对读写是由一个线程完成的Redis6.0引入多线程指的是网络请求采用多线程,而键值对读写命令依然是单线程处理的,所以redis依然是并发安全的

也就是只有网络请求和模块数据操作模块是单线程的,而其他的持久话、集群数据同步等,其实是由额外的线程执行的

二、Redis单线程为什么会这么快

  1. 命令执行基于内存操作的,一条命令在内存里操作的时间是几时纳秒
  2. 命令执行是单线程操作,没有线程切换开销
  3. 基于IO多路复用机制提升Redis的IO效率
  4. 高效的数据储存结构,全局hash表以及多种高效数据结构,比如:跳表,压缩列表,链表等等

三、Redis底层数据是如何跳表来储存的

和数据库索引差不多详情可见数据库索引

四、Redis Key过期了为什么内存没释放

你在使用Redis时,肯定经常使用SET命令

SET除了可以设置key- value以外,还可以设置key的过期时间,就像下面这样:

127.0.0.1:6379> SET hideyoshi hidesyohi EX 120
OK
127.0.0.1:6379> TTL hideyoshi
(integer) 117

此时如果想要修改key的值,但只是使用SET命令,而没有加上过期时间的参数,那么这个key的过期时间将会被擦除

127.0.0.1:6379> SET hideyoshi hidesyohi
OK
127.0.0.1:6379> TTL hideyoshi
(integer) -1

导致这个问题的原因在于:SET命令如果不设置过期时间,那么Redis会自动擦除这个key的过期时间

如果你发现Redis的内存持续增长,而且很多key原来设置了过期时间,后来发现过期时间丢失了,很有可能是因为这个原因导致的。

这是你的Redis中就会存在大量不过期的key,消耗过多的内存资源

所以,你在使用SET命令是,如果刚开始设置了过期时间,那么修改这个key,也务必要加上过期时间参数,避免过期时间丢失问题

Redis对过期key的处理一半有惰性删除和定时删除两种策略

  1. 惰性删除:当读/写一个已经过期的key时,会触发惰性删除策略,判断key是否过期,如果过期了直接删除这个key
  2. 定时删除:由于惰性删除无法保证冷数据被及时删除,所以Redis会定期(默认100ms)主动淘汰一批已经过期的key,这里的一批只是部分过期key吗所以可能会出现部分key已经过期但还没有被清理掉的情况,导致内存没有被释放

五、Redis Key没有设置过期时间为什么被Redis删除了

当redis已用内存超过maxmemory限定时,触发主动清理策略

主动清理策略在Redis4.0之前一共实现了6种内存淘汰策略,在4.0后,又增加了两种,总共8种:

  1. 针对设置了过期时间的key做处理:

    1. volatile-ttl:在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期越先被删除。
    2. volatile-random:在设置了过期时间的键值对中,进行随机删除。
    3. volatile-lru:会使用LRU算法筛选设置了过期时间的键值对删除。(最久未使用)
    4. volatile-lfu:会使用LFU算法筛选设置了过期时间的键值对删除。(最不常使用)
  2. 对所有key做处理:

    1. allkeys-random:从所有键值对中随机选择并删除数据
    2. allkeys-lru:所有key使用LRU算法删除(最久未使用)
    3. allkeys-lfu:所有key使用LFU算法删除(最不常使用)
  3. 不处理:

    1. noeviction:不会剔除任何数据,拒绝所有写入操作并返回客户端信息"(error)OOM command not allowed when used memory",此时Redis只响应读操作。

六、删除Redis Key的命令会阻塞Redis吗

删除命令的时间复杂读:

O(N), N为被删除的key的数量

删除单个字符串的key,时间复杂度为0(1).

删除单个列表、集合、有序集合或哈希表类型的的key,时间复杂度为0(M),M为以上数据结构内的元素数量。

结论:会阻塞Redis

七、Redis主从、哨兵、集群架构优缺点比较

哨兵模式

在Redis3.0以前的版本要实现集群一般是借助哨兵模式sentinel工具来监控master节点状态,如果master节点异常,则会做主从切换,将一台slave作为master,哨兵的配置略微复杂,并且性能和高可用等各方面表现一般,特别是在主从切换的瞬间存在访问瞬断的情况,而且哨兵模式只有一个主节点对外提供服务,没法支持很高的并发,且单个主节点内存也不宜设置过大,否则会导致持久化文件过大,影响数据恢复或主从同步效率

高可用集群模式

reids集群是有一个多个主从节点群组成的服务器群,他具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵,也能完成节点移除和故障转移功能。需要将每个节点设置为集群模式,这种集群没有中心节点,可以水平扩展,据官方问当称可以线性扩展到上万节点(官方不推荐超过1000节点)。Redis集群的性能和高可用均优于之前版本的哨兵模式,且集群配置非常简单。

八、Redis集群数据hash分片算法是怎么回事

Redis Cluster 将所有数据划分为16384个slots(槽位),每个节点负责其中一部分槽位。槽位的信息储存于每个节点中。

当Redis Cluster 的客户端来连接集群是,他也会得到一份集群的槽位配置信息并将其缓存存在客户端本地。这样当客户要查询某个key时,可以根据槽位定位算法定位到目标节点。

槽位定位算法

Cluster默认会对key值使用crc16算法进行hash得到一个整数值,然后用这个数值对16384进行取模来得到槽位。

HASH_SLOT = CRC16(key) mod 16384

再根据槽位值和Redis节点的对应关系就可以定位到key具体落在哪个Redis节点上。

九、Redis执行命令竟然有死循环阻塞bug

如果你想随机查看Redis中的一个key,Redis里有一个RANDOMKEY命令可以从Redis中随机取出一个key,这个命令可能导致Redis死循环阻塞。

前面的面试题讲过Redis对于过期key的清理策略是定时删除与惰性删除两种方式结合来的,二RANDOMKEY在随机拿出一个key后,首先会检查这个key是否已过期,如果该key已经过期,那么Redis会删除它,这个过程就是惰性删除,但是清理完了还不能结束,Redis还要找出一个没有过期的key,返回给客户端。

此时,Redis则会继续随机拿出一个Key,然后哦在判断他是否过期,知道找到一个没有过期的key返回给用户端。

这里就有一个问题了,如果此时Redis中,有大量key已经过期,但还未来得及清理,那这个循环就会持续很久才能结束,而且,这个耗时都话费在了清理过期key以及寻找不过期key上,导致的结果就是,RANDOMKEY执行时间变长,影响Redis性能。

以上流程,其实是在master上执行的。

如果在slave上执行RANDOMKEY,那么问题会更严重。

slave自己是不会清理过期key,当一个key要过期时,master会清理删除它,然后master想slave发消息删除key的命令,告知slave也删除这个key,1以达到主从数据一致性。

假设Redis中存在大量已过期还未清理的key,哪在slave上执行RANDOMKEY时,就会发生以下问题:

  1. slave随机取出一个key,判断是否已过期。
  2. key已过期,但slave不会删除它,而是继续随机寻找不过期的key。
  3. 由于大量key都已经过期,哪slave就会寻找不到符合条件的key,就会陷入死循环。

也就是说,在slave上执行RANDOMKEY,有可能会造成整个Redis实例卡死。

这其实是Redis的一个BUG,这个BUG一直持续到5.0才被修复,修复方案就是在slave中最多找一定次数,无论是否能找到,都会退出循环。

十、一次线上事故,Redis主从切换导致了缓存雪崩

假设,slave的机器时间比master走的快很多。

此时,Redis master里设置了过期时间key,从slave角度来看,可能很多在master里没过期的数据其实已经过期了

如果此时操作主从切换,八slave提升未新的master

它成为master,就会开始大量清理过期key,此时就会导致以下结果:

  1. mater大量清理过期key,主线进程可能会发生阻塞,无法及时处理客户端请求
  2. Redis大量数据过期,导致缓存雪崩。

当master与slave机器时钟严重不一致时,对业务影响非仓大。

所以,我们一定要保证主从机器时钟的一致性,避免发生这些问题

十一、Redis持久话RDB、AOF、混合持久话是怎么回事

RDB快照(snapshot)

在默认情况下,Redis降内存数据库快照保存在名字为dump.rdb的二进制文件中。

你可以对Redis进行设置,让他在"n秒内数据集至少有M个改动"这一条件被满足是,自动保存一次数据集。

比如说,以下设置会让Redis在满足"60秒内至少有1000个键被改动"这一条件时,自动保存一次数据集:

# save 60 1000 关闭RDB只需要将所有的save保存策略注释掉即可

开可以手动执行命令生成RDB快照,进入Redis客户端执行命令save或bgsave可以生成dump.rdb文件,每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件

bgsave的写是复制(COW)机制

Redis借助操作系统提供的写时复制技术(Copy-On-Write,COW),在生成快照的同时,依旧可以正常处理写命令。简单来说,bgsave子进程是由主线程fork生成的,可以共享主线程的所有内存数据。bgsave子进程运行后,开始读取主线程的内存数据,并把他们写入RDB文件。此时,如果主线程对这些数据也都是读操作,那么,主线程和bgsave子进程相互不影响。但是,如果主线程要修改一块数据,那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave子进程会把这个副本写入RBD文件,而在这个过程中,主线程仍然可以直接修改原来的数据。

save与bgsave对比:

命令savebgsave
IO类型同步异步
是否阻塞Redis其他命令否(在生成子进程执行调用fork函数式会有短暂阻塞)
复杂度O(n)O(n)
优点不会消耗额外内存不会阻塞客户端命令
缺点阻塞客户端命令需要fork子进程,消耗内存

配置自动生成rdb文件后台使用的是bgsave方式。

AOF(append-only file)

快照功能不是非常耐久:如果Redis因为某些原因而造成故障停机,那么服务器将丢失最近写入、切仍未保存到快照中的那些数据。从1.1版本开始,Redis增加了一种完全耐久的持久化方式:AOF持久化,将修改的每一条命令记录进文件appendonly.aof中(先写入os cache,每隔一段时间fsync到磁盘)

你可以通过修改配置文件来打开AOF功能:

# appendonly yes

从现在开始,每当Reids执行一个改变数据集的命令时(比如SET),这个命令就会被追加到AOF文件的末尾。

这样的话,当Redis重启时,程序就可以通过重新执行AOF文件中的命令来达到重建数据集的目的。

你可以配置Redis多久才将数据fsync到磁盘一次

有三个选项

# appendfsync always 每次命令
# appendfsync everysc 每秒一次
# appendfsync no 从不

推荐也是默认措施为每秒一次,这种fsync策略可以兼顾速度和安全性

AOF重写

AOF文件里可能有太多没用指令,所以AOF会定期根据内存的最新数据生成aof文件

例如执行如下几条命令

incr readcount
(integer) 1
incr readcount
(integer) 2
incr readcount
(integer) 3
incr readcount
(integer) 4
incr readcount
(integer) 5

重写后AOF文件里变成

*3
$3
SET
$2
readcount
$1
5

如果两个配置可以控制AOF自动重写频率

# auto-aof-rewrite-min-size 64mb //aof文件至少达到64M才会重写,文件太小恢复速度本来就快,重写的意义不大
# auto-aof-rewrite-percentage 100 //aof文件自上次重写后文件大小增长了100%则再次触发重写

当然AOF还可以手动重写,进入redis客户端执行bgrewriteaof重写aof

注意,AOF重写redis会fork出一个子进程去做(与bgsave命令类似),不会对redis正常命令处理有太多影响

RDB 和 AOF,我该用哪一个

命令RDBAOF
启动优先级
体积
恢复速度
数据安全性容易丢数据根据策略决定

生产环境可以都启用,redis启动时如果既有rdb文件又有aof文件则优先选择aof文件恢复数据,因为aof一般来说数据更安全一点。

Redis4.0混合持久化

重启Redis时,我们很少使用RDB来恢复内存状态,因为会丢失大量数据。我们通常使用AOF日志重放,但是重放AOF日志性能对RDB来说要慢很多,这样在Redis实例很大的情况下,启动需要花费很长时间。Redis4.0为了解决这个问题,带来了一个新的持久化选项--混合持久化。

通过如下配置可以开启混合持久化(必须先开启aof):

# aof-use-rdb-preamble yes

如果开启了混合持久化,AOF重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存叫做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。

于是在Redis重启的时候,可以先加载RDB的内容,然后在重放增量AOF日志就可以完全替代之前的AOF全量文件重放,因此重启效率大幅得到提升。

混合持久化AOF文件结构如下

appendonly.aof
RDB格式
AOF格式

线上持久化策略一般如何设置

如果对性能要求比较高,在master最好不要做持久化,可以在某个slave开启AOF备份权限,策略设置为每秒即可。

十二、一次线上事故,Redis主节点宕机导致数据全部丢失

如果你的Redis采用如下模式部署,就会发生数据丢失的问题:

  • master-slave + 哨兵部署实例。
  • master 没有开启数据持久化功能。
  • Redis进程使用supervisor管理,并配置为进程宕机,自动重启。

如果此时master宕机,就会导致下面的问题:

  • master宕机,哨兵还未发起切换,此时master进程立即被supervisor自动拉起。
  • 但master没有开启任何数据持久化,启动后是一个空实例。
  • 此时slave为了与master保持一致,他会制动清理实例的所有数据,slave也变成了一个空实例。

在这个场景下,master / slave 的数据就全部丢失了。

此时,业务应用在访问Redis时,发现缓存中没有任何数据,就会把请求全部打到后台数据库上,这还会引发缓存雪崩,对业务影响巨大。

这种情况下我们一般不应该给Redis主节点配置进程宕机马上自动重启策略,而应该等哨兵把某个Redis从节点切换为主节点再重启之前宕机的Redis主节点让起变为slave节点。

十三、Redis线上数据如何备份

  1. 写crontab定时调度脚本,每小时都copy一份rdb或aof文件到另外一台机器中去,保留最近48小时的备份
  2. 每天都保留一份当日的数据备份到一个目录中去,可以保留最近一个月的备份
  3. 每次copy备份的时候,都把太旧的备份删除

十四、Redis主从复制风暴是怎么回事

如果Redis主节点有很多从节点,在某一时刻如果所有从节点都同时连接主节点,那么主节点会同时把内存RDB发给多个从节点,这样会导致Redis主节点压力非常大,这就是所谓的Redis主从复制风暴问题。

这种问题我们队Redis主从架构做一些优化得以避免,比如可以做下面这种树形复制架构

image-20230926221609046

Last modification:October 24, 2023
如果觉得我的文章对你有用,请收藏本站