Redis高频面试题(2023最新版)

文章目录

    • 1、redis为什么快?
      • 1.1 基于内存
      • 1.2 存储形式
      • 1.3 编码
      • 1.4 IO多路复用
      • 1.5 渐进式ReHash
      • 1.6 缓存时间戳
    • 2、缓存穿透、缓存击穿、缓存雪崩
      • 2.1 缓存穿透
      • 2.2 缓存雪崩
      • 2.3 缓存击穿
    • 3、热Key问题
    • 4、Redis的过期策略和内存淘汰策略
      • 4.1 过期策略
      • 4.2 内存淘汰策略
    • 5、Redis的应用场景
      • 5.1 缓存
      • 5.2 排行榜
      • 5.3 计数器
      • 5.4 共享session
      • 5.5 分布式锁
      • 5.6 社交网络
      • 5.7 消息队列
      • 5.8 位操作
    • 6、Redis持久化有哪几种方式
      • 6.1 持久化
      • 6.2 RDB
      • 6.3 AOF
      • 6.4 生产环境
    • 7、 Redis 高可用
      • 7.1 主从模式
      • 7.2 哨兵模式
        • 7.2.1 作用
        • 7.2.2 哨兵
      • 7.3 Redis Cluster
    • 8、 Redis分布式锁
    • 9、Mysql与Redis如何保证双写一致性?
      • 9.1 同步双写
      • 9.2缓存延迟双删
      • 9.3 删除缓存重试机制
      • 9.4 读取binlog日志
    • 10、为什么Redis 6.0后改为多线程?
    • 11、Redis事务
    • 12、布隆过滤器
    • 13、Redis的理解
    • 14、redis持久化方式
      • RDB(redis默认持久化机制)
      • AOF
        • AOF重写机制原理
      • redis混合持久化
    • 15、redis怎么实现高可用
      • redis主从
      • Redis哨兵
        • 哨兵作用:监控,自动选主切换,通知
        • 哨兵模式
        • 哨兵如何判定主库下线
        • 哨兵工作模式
        • 哨兵如何选主
        • 由哪个哨兵执行主从切换
        • 故障转移
        • Redis Cluster 集群

1、redis为什么快?

1.1 基于内存

内存读写效率远高于磁盘读写,省去磁盘IO操作

1.2 存储形式

Redis作为K-V键值对型的内存数据库,所有键值都是用字典来存储,即哈希表结构。哈希表的特性就有在O(1)时间复杂度就可以获取对应的值。

1.3 编码

支持多种数据结构及编码,针对不通业务场景,都有相对应的数据结构和编码。

根据元素的数量,有一个阈值,小于阈值和大于阈值的编码不同。

1.4 IO多路复用

核心思想:让单个线程去监视多个连接,某个连接就绪,就触发读写事件。即可以单个线程处理多个客户端连接,无需创建和维护过多的进程和线程。

1.5 渐进式ReHash

Redis使用全局哈希表保存键值对。

哈希表相当于一个数组,每个数组元素称为一个哈希桶,每个哈系桶中保存了键值对的数据。

数据增加到一定阈值,数组扩容会导致数据发生移动,此时访问阻塞。

渐进式ReHash:把一次性大量拷贝(数组移动)的开销,分摊到多次请求中去处理。

Redis默认使用两张全局哈希表,开始插入数据时默认使用表1,此时表2并没有被分配空间。随着数据量增多,开始执行ReHash。

  • 给表2分配更大的空间,
  • 将表1中的数据重新映射并拷贝到表2
  • 释放表1的空间

1.6 缓存时间戳

业务中,我们一般使用System.currentTimeMillis()或new Date()等方式获取系统的毫秒时间戳,每次获取都是一次系统调用(需要调用操作系统中对应的函数,涉及上下文切换),相对比较耗时。

而Redis由一个定时任务,毫秒更新一次缓存,获取时间都是从缓存中直接拿,不需要进行系统调用。

2、缓存穿透、缓存击穿、缓存雪崩

2.1 缓存穿透

本质:数据库和Redis都不存在

场景:查询id为-1的数据

坏处:每次都需要查询数据库和redis,增加磁盘IO的压力

解决方案

  1. 参数校验、屏蔽非法参数
  2. 数据库查询为空,可以给缓存一个空值或默认值,防止第二次再去数据库
  3. 使用布隆过滤器快速判断数据是否存在,将所有可能存在的数据哈希存到一个足够大的容器中,不存在的数据被这个bitmap拦截掉

2.2 缓存雪崩

本质:数据库和Redis都存在,但redis都过期了

同一时间,缓存大面积失效,大量请求都直接去访问数据库

解决方案

  1. 热点数据永不过期,或者通过异步线程在每次热点数据快要过期时,进行续期
  2. 数据的过期时间不要全一致,设为一定范围内的随机时间
  3. 并发量不高,可加入队列或者锁,限制同一时间访问数据库的阈值
  4. 分布式部署,将热点数据打散分不到多个节点
  5. 如果是缓存中间件宕机了,需要尽可能保证其高可用性,可以搭建redis集群,提前做好报警机制

2.3 缓存击穿

本质:单个key没有或过期,同一时间查询这同一条数据并发量过多

解决方案

  1. 热点数据用不过期,或者通过异步线程在每次热点数据快要过期时,进行续期
  2. 使用互斥锁,避免大量请求同时查询数据库
  3. 熔断、降级、防止系统崩溃
  4. 还可以考虑对重要的热点数据进行多级缓存

3、热Key问题

含义:访问频率较高的key

场景:秒杀、热点新闻等读多写少的场景,请求分片集中,比如固定的key落入同一台服务器。

解决方案

  1. Redis集群扩容:增加分片副本,平滩流量。
  2. 热点key分散到不同服务器
  3. 做二级缓存,即JVM本地缓存,减少Redis的读请求数量

4、Redis的过期策略和内存淘汰策略

4.1 过期策略

  • 定时过期:每个设置过期时间的key都需要创建一个定时器,到期清除key。该策略会立即清除过期的数据,对内存友好,但是会占用大量CPU去处理过期数据,影响吞吐量
  • 惰性过期:只有访问一个key时,才会判断是否已过期,过期则清除。最大化节省CPU资源,对内存不友好。可能会导致大量过期的key因未被访问而无法清除。
  • 定期过期:每隔一定时间,扫描一定数量的key,并删除其中过期的,通过调整定时扫描的时间间隔和扫描限定耗时,使CPU和内存达到一个最佳平衡状态。

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

每隔100ms就随机抽取一定量key,检查和删除。同时获取key时,会检查一下是否过期,过期则删除。

隐患:同样可能定期删除,漏删了大量过期key,也没有走惰性删除,就会导致大量过期key堆积在内存。

4.2 内存淘汰策略

volatile-lru:内存不足以写入新数据时,从设置了过期时间的key中使用LRU(不经常使用的)算法进行淘汰。

allkeys-lru:内存不足以写入新数据时,从所有key中使用LRU(不经常使用的)算法进行淘汰。

volatile-lfu:4.0版本新增,内存不足以写入新数据时,在设置了过期时间的key中使用LFU(最近最少使用)算法淘汰。

allkeys-lfu:4.0版本新增,内存不足以写入新数据时,所有key中使用LFU(最近最少使用)算法淘汰。

volatile-random:内存不足以写入新数据时,从设置了过期时间的key中,随机淘汰数据。

allkeys-random:内存不足以写入新数据时,从所有key中,随机淘汰数据。

volatile-ttl:内存不足以写入新数据时,从设置了过期时间的key中,根据过期时间进行淘汰,越早过期的越被优先提前淘汰。

noeviction:默认淘汰策略,当内存不足以容纳新写入数据时,新写入操作会报错。

5、Redis的应用场景

名称 英文名 作用域
字符串 String 缓存、计数器、分布式Session
哈希 Hash 存放对象
列表 list 消息队列、文章列表
集合 set 标签、随机数、社交图谱
有序集合 ZSET 排行榜
Bitmaps Bitmaps 布隆过滤器
HyperLogLog HyperLogLog UV
Geo Geo Redis3.2推出的,地理位置定位,用于存储地理位置信息,并对存储的信息进行操作

5.1 缓存

热点数据存放缓存,提升网页访问效率,降低DB压力,同时还提供了RDB和AOF等持久化机制。适用String数据类型和hash类型,存放对象使用hash类型。

5.2 排行榜

销量排行榜、礼物排行榜、投票排行榜。Redis提供zset数据类型,能实现这些复杂的排行榜。适用list数据类型。

5.3 计数器

短视频播放数、网站浏览数等,需要实时,Redis天然支持计数功能。适用String数据类型。

5.4 共享session

使用Redis将用户Session集中管理,多个服务共享,适用String数据类型。

5.5 分布式锁

场景:同一个资源并发访问,如秒杀、下单减库存等场景。适用String数据类型。

  • synchronize和reentrantlock本地锁性能较低
  • 并发量不大,可以使用数据库悲观锁,但并发量大的场景会降抵DB性能
  • 可以使用Redis Setnx来实现分布式锁

5.6 社交网络

点赞、粉丝、共同好友、喜好、推送、下拉刷新等功能。适用String数据类型和hash类型,存放对象使用hash类型。

标签适用于set数据类型,例如一个用户可能对娱乐、体育感兴趣,另一个用户可能对历史、新闻感兴趣,这些兴趣点就是标签。通过这些数据可以得到喜欢同一个标签的人,这些数据对于用户体验以及增强用户粘度比较重要。

排行榜适用于有序集合(ZSET)数据类型。

5.7 消息队列

业务解耦、流量削峰及异步处理实时性低的业务,Redis提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。适用list数据类型。

5.8 位操作

用于数据量上亿的场景下,例如几亿用户系统签到,去重登录次数统计,某用户是否在线状态等。此时不可能给每个用户一个key。

这里需要用到位操作,使用setbit、getbit、bitcount命令。

原理:redis内构一个足够长的数组,每个数组只能是0和1两个值,数组下标表示用户id,这个几亿长的大数组就可以通过下标和元素值(0和1)来构建一个记忆系统。

适用于BitMaps数据类型。

6、Redis持久化有哪几种方式

6.1 持久化

将数据写入磁盘,避免因进程退出而造成的数据丢失,下次重启时通过持久化文件恢复数据。

6.2 RDB

通过快照(内存中数据在某一时刻的状态记录)的方式实现持久化,根据快照的触发条件,将内存的数据快照写入磁盘,以二进制的压缩文件进行存储。

缺点:每隔一段时间触发持久化,数据安全性低。

6.3 AOF

以独立日志的方式记录每次写的命令,重启时重新执行AOF文件中的命令恢复数据

AOF重写机制:AOF文件的大小达到某个阈值时,会将其中指令进行压缩。(如果有对于某个key多次的变更指令,则仅保留最新的数据指令)。

优化

  • 因为AOF重写过程中需要读取当前内存中所有键值数据,性能较低,redis将其放在一个后台子线程中完成。
  • 为了避免重写过程中出现数据变动,主进程的数据变更需要追加到AOF重写缓冲区中,等到AOF重写完成后,再把AOF重写换乘区里面的内容追加到新的AOF文件中。

缺点:AOF文件可能过大,性能较差

6.4 生产环境

生产环境中一般采用两种持久化机制混合使用。

将内存中数据快照存储在AOF文件中(模拟RDB),后续再以AOF追加方式。

如果仅作为缓存使用,可以承受几分钟数据丢失,可以使用RDB,对主程序性能影响最小。

7、 Redis 高可用

高可用:数据不能丢失(尽量减少丢失),保证Redis

7.1 主从模式

  • 部署多台Redis服务器,主从复制以保证数据副本一致。主库通过将RDB文件发送给从库实现复制。
  • 主从之间采用读写分离,主库写操作,从库仅负责读操作。
  • 主库如果宕机了,人工切换一台从库称为主库,通知应用方更新主节点地址。

问题:数据不一致。

原因:主从库网络延迟,从库接收到命令,但它正在执行阻塞性命令。

解决方法:保证网络通畅,监控主从库复制进度。

7.2 哨兵模式

Redis从2.8开始提供哨兵机制。

7.2.1 作用
  • 监控:周期性ping主从库,检测是否挂了,标记下线状态。
  • 自动选主切换:多个从库中按照一定规则选一个作为主库。
  • 通知:选出主库后,将新主库的连接信息发送给其他从库以及应用方,重新建立联系。
7.2.2 哨兵

由一个或多个哨兵实例组成哨兵系统,监控其他Redis节点的同时,哨兵实例之间也互相监控。

哨兵之间通过发布订阅机制组成集群。一主多从

缺点:无法实现在线扩容,并发压力受限于单个服务器的资源配置。

7.3 Redis Cluster

哨兵模式解决了自动切换主从的问题,但是没有解决在线扩容的问题。

本质:Redis Cluster实现了Redis的分布式存储,每个节点存储不同的数据,实现数据分片。

引入Slot槽实现数据分片,每个节点分配一个Slot区间,当我们存取Key的时候,Redis根据key计算得到Slot值,找到对应的节点进行读写。多主多从。

8、 Redis分布式锁

概念

  • 锁:同一时间只允许一个线程或者一个应用程序进入执行。
  • 分布式锁:必须要求Redis具有"互斥"能力,可以使用SETNX命令:即key不存在了才会设置它的值,否则什么也不做。

注意事项

  1. 如果过期时间是每个服务自己生成,需要保证每个客户端时间同步。
  2. 必须保存持有者唯一标识,否则可能被别的客户端释放/解锁。
  3. 给锁设置过期时间,以免进程挂了或异常了无法释放锁。
  4. 加入看门狗:开启守护线程,定期检测锁的失效时间,如果快过期了,业务还没有执行完,则续期。

看门狗:开源框架Redisson,只要线程一个线程加锁成功,就会启动一个watch dog,每隔10秒检查一下锁是否释放,只要第一个线程还持有锁,就延长锁的失效时间,解决了锁过期但业务还没执行完的问题。

9、Mysql与Redis如何保证双写一致性?

9.1 同步双写

效率较低,代码耦合,不便维护。

9.2缓存延迟双删

先删缓存,在更新数据库,休眠一会儿,再次删除缓存,休眠期间可能出现脏数据。

9.3 删除缓存重试机制

因为延迟双删可能存在第二次缓存失败的情况,可能删除失败就多删几次包证成功。

  1. 写请求更新数据库
  2. 缓存失败
  3. 将删除失败key放到消息队列
  4. 消费消息,获取要删除的key
  5. 重试删除缓存操作

9.4 读取binlog日志

异步删除、更新缓存,

  • 可以使用canal将binlog日志采集发送到MQ队列中。
  • 通过ACK机制确认处理这条更新消息,删除缓存,保证数据一致性。

10、为什么Redis 6.0后改为多线程?

6.0之前,Redis处理客户端请求时,读写socket、解析、执行都由一个顺序串行主线程处理,即单线程。

之前几乎不存在CPU成为瓶颈的情况,Redis主要受限于内存和网络。

6.0后,Redis还使用单线程模型处理客户度请求,只用多线程处理数据读写和协议解析,执行命令还使用单线程。

使用多线程主要是提升提升IO读写效率,主要针对大公司大项目。

11、Redis事务

本质:按照顺序串行化执行队列中的每个命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

顺序性、一次性、排他性执行一个队列中的一系列命令。

命令 描述
EXEC 执行所有事务块命令
DISCARD 取消事务,放弃执行事务块所有命令
MULTI 标记一个事务块,开启事务
UNWATCH 取消WATCH命令对所有key的监视
WATCH 监视key,如果在事务执行之前,该key被其他命令改动,那么事务将被打断
        //构建redis连接
        Jedis jedis = jedisPool.getResource();
        //标记一个事务块,开启事务
        Transaction transaction = jedis.multi();
        try {
            transaction.set("name1","江先进");
//            int n = 10/0;
            transaction.set("name2","张硕");
            //执行事务
            transaction.exec();
        }catch (Exception e){
            //取消事务
            transaction.discard();
            e.printStackTrace();
        }

12、布隆过滤器

布隆过滤器可以应对缓存穿透问题

数据结构:一个很长的二进制向量和一组Hash映射函数组成。

作用:检索一个元素是否在一个集合中,空间查询效率比一般的算法要好的多。

缺点:有一定误识别率和删除困难。

原理:集合A中有n个元素,利用K个哈希散列函数,将A中每个元素映射到一个长度为a位的数组B中不同位置上。这些位置上的二进制数均设置为1。经过这个K个哈希散列函数映射后,发现其k个位置上的二进制数全部为1,这个元素很可能属于集合A。

13、Redis的理解

本质:基于Key-Value存储结构的非关系型数据库

数据类型:提供5种基本数据类型,String、set、zset、list、Map。

性能:基于内存存储,并且在数据结构上做了大量优化,IO性能较好。

作用:作为应用与数据库之间的分布式缓存组件。

高可用:提供了主从复制、哨兵、以及集群方式实现高可用,集群中通过hash槽的方式实现了数据分片,进一步提升了性能。

14、redis持久化方式

RDB(redis默认持久化机制)

redis将某一时刻的数据持久化到磁盘,是一种快照式的持久化方式,是以二进制压缩文件的形式存储的

  • 持久化触发方式:

    • 手动触发

      save命令: 在客户端中执行 save 命令,就会触发 Redis 的持久化 ,但是会阻塞主线程命令的执行

      bgsave命令:bgsave会 fork() 一个子进程来执行持久化,整个过程只有 fork() 子进程的时候有短暂的阻塞,子进程创建之后,redis的主进程就可以响应客户端的请求了

      fork() 作用:复制一个与当前进程一样的进程,新进程所有的数据数值与原进程一致,但是是一个全新的进程,并作为原进程的子进程

      一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。

  • 自动触发

    • save m n:在m秒内,如果有n个键位发生变化,则自动触发持久化,参数 m 和 n 可以在 Redis 的配置文件中找到

      eg:save 60 1 则表明在 60 秒内,至少有一个键发生改变,就会触发 RDB 持久化

    • 当设置多个save m n的时候, 满足任意一个条件都会触发持久化

      eg:save 60 10 save 600 1

      60s 内如果有 10 次 Redis 键值发生改变,就会触发持久化;如果 60s 内 Redis 的键值改变次数少于 10 次,那么 Redis 就会判断 600s 内,Redis 的键值是否至少被修改了一次,如果满足则会触发持久化。

  • redis在进行数据持久化的时候,会将数据先写入一个临时文件中,等持久化完成后,会用这个临时文件替换上次持久化好的文件,因为这种特性,我们可以随时进行备份,因为快照文件总是完整的

  • 如果需要进行大规模的数据恢复,并且对数据恢复的完整性要求不高,使用RDB比AOF更高效

  • RDB优点

    • 以二进制压缩文件的形式存储,占用内存更小
    • redis使用bgsave命令进行持久化,基本不会影响主进程,保证了redis的高性能
  • RDB缺点

    • 如果对数据完整性要求比较高,RDB就不适用,因为即使在比较短的时间间隔就持久化一次,当redis发生故障,仍然会丢失这段时间的数据

AOF

  • AOF,Append Only File,即只允许追加不允许改写的文件。

  • AOF方式是将执行过的指令记录下来,在数据恢复的时候按照从前到后的顺序将指令都执行一遍

  • 通过配置redis.conf中appendonly yes 就可以开启AOF功能,如果有写的操作,redis就会把指令追加到AOF文件的末尾

  • 默认的AOF持久化策略是每秒钟 fsync 一次(fsync是指把缓存中的写指令记录到磁盘),在这种情况下redis可以保持良好的性能,即使redis故障,也只会丢失最近一秒的数据

  • 在追加日志时,如果遇到磁盘空间满等情况导致日志写入不完整,redis提供了 redis-check-aof 工具,可以进行日志修复

  • AOF这种追加指令的方式,会造成AOF文件越来越大,因此redis提供了AOF文件重写机制,当AOF文件的大小超过所设定的阈值的时候,redis就会对AOF的文件进行压缩,只保留可以恢复数据的最小指令集

    • eg:我们执行了100条指令,AOF文件就要存储100条指令,低效的,我们可以把这100条合并成少量的命令,这就是重写机制的原理

    • 直接执行 BGREWRITEAOF 命令,redis就会生成一个全新的 AOF 文件,包括了可以恢复现有数据的最少的命令集

      AOF重写机制原理
      • 在重写开始之前,redis会创建(fork)一个重写子进程,这个进程会读取现有的 AOF 文件,并将其中包含的指令进行压缩之后写入一个临时文件中
      • 同时主进程会将新接收到的指令写入到内存缓冲区和原有的AOF文件中,这样可以保证原有的AOF文件的可用性
      • 子进程完成重写工作后,会给父进程发送信号,父进程收到信号后,将缓冲区中的指令添加到新的AOF文件中
      • 追加结束后,redis会用新的AOF文件替代旧的AOF文件,之后再有新的指令,全部写入新的AOF文件中
  • AOF优点

    • 在进行AOF重写的时候,仍然是采用先写临时文件,全部完成后再替换的方式,因此磁盘满等问题都不会影响AOF文件的可用性
    • 假设我们在操作redis的时候,执行了FLUSHALL(清除所有),导致redis内存中的数据全部清空,只要redis配置了AOF持久化方式,且AOF文件没有被重写,我们就可以暂停redis并且编辑AOF文件,删除最后一行的 FLUSHALL 命令,然后重启redis,就可以恢复redis的所有数据到 FLUSHALL 之前的状态了,但是如果AOF文件已经被重写,就无法恢复数据
  • AOF缺点

  • 同样数据规模的情况下,AOF文件比RDB文件大,而且AOF方式的恢复速度大于RDB的方式

  • 修改被写坏的AOF文件

    • 备份被写坏的AOF文件
    • 运行 redis-check-aof -fix 文件进行修复
    • 用 diff -u 查看两个文件的差异,确认修改
    • 重启redis ,加载修复后的 AOF 文件

重启redis的时候,一般使用 AOF 来恢复数据,因为数据完整性更高,但是在大数据量的情况下,花费时间很长,redis4.0为了解决这个问题,采用了混合持久化方式

redis混合持久化

原理:将RDB的文件内容和增量的AOF文件存在一起,这里的AOF不再是全量的日志,而是自持久化开始到结束的这段时间发生的增量 AOF 日志,这部分AOF日志很小

在redis重启的时候,先加载 RDB 的内容,再执行 AOF 增量日志就可以代替之前的 AOF全量文件执行,效率提高

混合持久化执行流程:

redis配置文件中的 aof-use-rdb-preamble 参数开启混合持久化,默认开启是 yes ,混合持久化结合了 RDB 和AOF 的优点,在redis 5.0 之后默认开启的

混合持久化优点

  • 结合了 RDB 和 AOF 的优点,可以更快的启动,同时也降低了数据丢失的风险

混合持久化缺点

  • AOF 文件中添加了 RDB 格式的内容,可读性降低
  • 如果开启混合持久化,那么支持次混合持久化 AOF 文件,不能用在redis 4.0 之前的

15、redis怎么实现高可用

高可用:数据不丢失/ 尽量减少丢失(AOF和RDB),保证redis的可用性(不能单点部署)

为了实现高可用,通常是将redis数据库复制多个部署在不同的服务器上,即使其中一台挂了其他的也可以继续提供服务。redis实现高可用有三种模式:主从模式,哨兵模式,集群模式

redis主从

主从概念

  • 部署多台服务器,有主库和从库,通过主从复制保证数据一致
  • 主从库采用读写分离的方式,主库负责读操作,从库负责写操作
  • 如果主库挂了,切换从库成为主库

redis主从同步过程

  • 第一阶段:主从库建立连接,协商同步

    • 从库向主库发送 psync 命令,告诉主库要进行数据同步
    • 主库接收到 psync 命令后,响应 FULLRESYNC 命令(表第一次复制是全量复制)
  • 第二阶段:主库把数据发送到从库,从库收到数据后,完成本地加载

    • 主库执行 bgsave 命令,生成 RDB 文件,接着把这个文件发送给从库,从库收到后,会先清空数据库。然后加载 RDB 文件
    • 主库生成 RDB文件到发送给从库的过程中,新的写入操作,会被记录到 replication buffer(复制缓冲区)中
      • 主库生成 RDB 文件的过程不会影响主线程,但是这个期间的操作并没有记录到 RDB文件中,可能会导致数据不一致,因此redis会将开始生成 RDB 文件后的写操作记录到缓冲区
  • 第三阶段:主库把新的写入操作发送到从库

    • 主库发送完成后,会把 replication buffer 中写的操作发送给从库,从库执行这些操作,这样主从库就实现数据同步了

redis主从可能产生的问题

  • 主从数据不一致

    • 主从库网络延迟
    • 从库收到了主库命令,但是从库正在执行阻塞性命令 (如 hgetall)

    如何解决数据不一致问题

    • 更换硬件,保证网络通畅
    • 监控主从库复制进度
    • 读取过期数据

全量同步,增量同步,指令同步:

全量同步:一般发生在第一次建立主从关系的时候,会把主库的所有数据都进行同步

增量同步:从库会定时发起同步,如果每次都把所有数据进行同步,性能消耗大且耗时,因此每次同步的时候只需要同步和上一次不同的数据就可以

指令同步:主库输入的命令会异步同步给从库

Redis哨兵

主从模式中,一旦主节点故障,需要人为更换主节点,同时还要通知客户端更换主节点地址,多数场景不能接受这种故障处理方式,因此redis2.8 开始正是提供了 Redis哨兵机制来处理这个问题

哨兵作用:监控,自动选主切换,通知
  • 哨兵进程在运行期间,会监控所有的主从库,它周期性的给主从库发送 PING 命令,检测主从库是否存活,如果从库没有在规定时间内响应,哨兵就会把它标记为下线状态,如果主库没有在规定时间内响应,哨兵就会判定主库下线,从而切换到选主任务
  • 所谓选主,就是从多个从库中,按照一定的规则,选出一个做为主库。
  • 通知就是,选出主库之后,哨兵把新主库信息发送给其他从库,让它们和新主库建立主从关系,同时哨兵还会把新主库的连接信息发送给客户端,让它们把请求发送到新主库上
哨兵模式
  • 就是由一个或者多个哨兵组成的哨兵系统,他可以监视所有的主节点和子节点,当被监视的主节点下线的时候,会自动将某个子节点升级为主节点,一个哨兵对redis节点进行监控,可能会出现问题,所以一般使用多个哨兵来监控,并且多个哨兵之间还能互相监控
  • 哨兵之间通过发布订阅机制组成集群,同时哨兵之间通过INFO命令获得从库信息,也能和从库建立连接,从而进行监控
哨兵如何判定主库下线
  • 哨兵向主从库发送PING命令,如果主从库没有在规定时间内响应,哨兵就把他标记为主观下线
  • 如果主库被标记为主观下线,则正在监视这个主库的所有哨兵以每秒一次的频率确认主库是否真的进入主观下线状态了,当有多数哨兵(一般少数服从多数,也可以由redis管理员自行设置一个值)在规定时间内认为主库确实进入了主观下线的状态,主库就会被标记为客观下线,这样做的原因是,避免对主库的误判,减少没有必要的主从切换,节约资源
  • 假设有N个哨兵,如果有 N/2 + 1 个哨兵判断主库主观下线,就可以把这个节点标记为客观下线,就可以进行主从切换了
哨兵工作模式
  • 每个哨兵以每秒钟一次的频率向它知道的主从库发送PING命令
  • 如果一个节点距离上一次有效恢复PING命令的时间超过 down-after- milliseconds 所设置的特定值,这个节点就会被标记为主观下线
  • 如果主库被标记为主观下线,那么监视这个主库的所有哨兵要以每秒一次的频率确认主库是否真的进去主观下线状态了
  • 当有足够多的哨兵在规定时间内确认主库真的进入了主观下线主状态,主库就会被标记为客观下线
  • 当主库被确认为客观下线,就会进入选主模式
  • 若没有足够数量的哨兵认为主库已经主观下线,主库的主观下线状态就会被移除;若主库重新向哨兵命令返回有效值,主库的主观下线状态也会被移除
哨兵如何选主

过滤和打分:在多个库中,先按照一定的筛选条件,把不符合条件的从库过滤掉,再按照一定规则,给剩下的从库逐个打分,将得分最高的选为新的从库

  • 先判断从库状态,已下线的,直接过滤掉

  • 如果从库网络不好,也会被过滤掉,参数 down-after- milliseconds 表示我们认定的主从库断连最大超时时间

  • 过滤掉不合适的,给剩下的从库打分,按照三个规则来:

    • 从库优先级
    • 从库复制进度
    • 从库ID
  • 从库优先级越高,打分越高,优先级可以通过 slave-priority 配置,如果优先级一样,就选与旧主库复制最快的从库,如果优先级和复制进度一样,选从库ID号小的

由哪个哨兵执行主从切换
  • 一个哨兵标记主库为主观下线后,它会征求其他哨兵的意见,确认主库是否进去主观下线状态,如果足够多数量的数量的哨兵认为主库已经主观下线,主库就会被标记为客观下线。

  • 主库被标记为客观下线后,标记的这个哨兵就会发起投票(这个过程成为Leader选举),在进行投票的这段时间,任何一个判断主库客观下线的哨兵都可以进行投票,最终票数最多的哨兵(也被称为Leader)执行主从切换。

    想要称为Leader要满足两个条件

    • 拿到的不少于 N/2 +1 票数
    • 拿到的票数要大于哨兵配置文件中的 quorum 值

    如果没有产生符合条件的Leader,等待一段时间(一般是哨兵故障转移超时时间的 2 倍),会重新进行选举

故障转移

假设有三个哨兵,一个主库M1,两个从库 S1 和 S2

当哨兵检测到M1出现故障,就需要进行故障转移

假设选出哨兵3作为Leader,流程如下:

  • 从库S1 升级为主库
  • 从库S2成为S1的从库
  • 原主库即使恢复也变成S1的主库
  • 通知客户端应用程序新主库地址
Redis Cluster 集群

哨兵模式基于主从模式,可以自动切换主从,可用性高,但是每个节点存储的数据是一样的,浪费内存,还难以扩容,因此redis 3.0 之后加入了 Redis Cluster 集群(切片集群),也就是说每台redis服务器上存储的内容都不同,解决了扩容问题,它还可以保存大量数据,还提供了复制和故障转移功能

你可能感兴趣的:(面试题,redis,数据库,缓存)