Redis高频面试题完整版

文章目录:

  • Redis概述

    • 什么是Redis?

    • Redis的优缺点?

    • Redis为什么常常用做缓存?相比于guava有什么优势?

    • Redis和Memcached的区别与共同点?

    • Redis是单线程还是多线程?Redis为什么这么快?

    • Redis6.0之后为什么引入了多线程?

    • Redis的数据类型有哪些?

    • Redis的数据结构有哪些?

    • Redis的应用场景有哪些?

    • Redis是单线程的,如何提高CPU的利用率?

  • 过期键的删除策略

    • 键的过期删除策略

    • Redis的内存淘汰机制是什么样的?

  • Redis的持久化

    • 什么是Redis的持久化?

    • Redis常见的持久化机制有哪些?有什么有优缺点?

  • Redis的事务

    • 什么是Redis的事务

    • Redis事务的相关命令

    • Redis事务执行的三个阶段

    • Redis事务的特性

    • Redis事务为什么不支持回滚?

  • Redis的集群、主从、哨兵

    • Redis集群的实现方案有哪些?

    • Redis主从架构中数据丢失吗

    • 如何解决主从架构数据丢失问题?

    • Redis集群的主从复制过程是什么样的?

    • Redis是如何保证主从服务器一致处于连接状态以及命令是否丢失?

    • 因为网络原因在主从复制过程中停止复制会怎么样?

    • 了解Redis哈希槽吗?

    • Redi集群最大的节点个数是多少?为什么?

    • Redis集群是如何选择数据库的?

    • Redis高可用方案如何实现?

  • Redis的分区

    • Redis的分区作用是什么?

    • Redis分区有哪些实现方案?

    • Redis分区的缺点?

  • Redis的分布式问题

    • 什么是分布式锁?

    • 分布式锁具有哪些特性?

    • 分布式锁的实现方法?

    • Redis如何实现分布式锁?

    • Redis并发竞争key问题应该如何解决?

    • 什么是RedLock

  • Redis的缓存问题

    • 说下什么是缓存雪崩、缓存穿透、缓存击穿,及它们的解决方案

    • 如何保证缓存与数据库双写时的数据一致性?

  • Redis其他高频面试题

    • 一个字符串类型的值能存储最大容量是多少?

    • Redis如何实现大量数据插入?

    • 如何通过Redis实现异步队列?

    • 如何通过Redis实现延时队列?

    • Redis回收使用什么算法?

    • Redis 里面有1亿个 key,其中有 10 个 key 是包含 java,如何将它们全部找出来?

    • 生产环境中的Redis是如何部署的


Redis概述

什么是Redis?

Redis是一个高性能的非关系型的键值对数据库,使用C编写实现的。与传统的数据库不同的是Redis是存在内存中的,所以读写速度非常快,每秒可以处理超过10万次的读写操作,这也是Redis常常被用作缓存的原因。

Redis的优缺点?

优点:

  • 读写性能好,读的速度可达110000次/s,写的速度可达81000次/s。
  • 支持数据持久化,有AOF和RDB两中持久化方式
  • 数据结构丰富,支持String、List、Set、Hash等结构
  • 支持事务,Redis所有的操作都是原子性的,并且还支持几个操作合并后的原子性执行,原子性指操作要么成功执行,要么失败不执行,不会执行一部分。
  • 支持主从复制,主机可以自动将数据同步到从机,进行读写分离。

缺点:

  • 因为Redis是将数据存到内存中的,所以会受到内存大小的限制,不能用作海量数据的读写
  • Redis不具备自动容错和恢复功能,主机或从机宕机会导致前端部分读写请求失败,需要重启机器或者手动切换前端的IP才能切换

Redis为什么常常用做缓存?相比于guava有什么优势?

缓存的定义是访问速度比一般随机存取存储器快的一种高速存储器,而因为Redis是基于内存提供了高性能的数据存取功能,其比较显著的优势就是非常地快。

缓存可以分为本地缓存或者分布式缓存,比较常用的guava缓存就是一种本地缓存,其主要特点是轻量并且快速,生命周期随着JVM的销毁而结束,缺点是在多实例的情况下,每个实例都要自己保存一份缓存,这样会导致缓存的一致性出现问题。

Redis则是分布式缓存,在多实例情况下,每个实例都共享一份缓存数据,缓存具备一致性。缺点是要保持Redis的高可用整体架构会比较复杂。

Redis和Memcached的区别与共同点?

相同点:

  • 两者的读写性能都比较高
  • 都是基于内存的数据库,通常被当作缓存使用
  • 都有过期策略
  • 都是基于C语言实现

不同点:

不同点 Redis Memcached
是否支持复制 支持主从复制 不支持复制
key长度 长度最大为 2GB 长度最多为 250 个字节
数据类型 不仅支持key-value类型的数据,还支持hash、list、set、zset等数据等数据类型的数据 仅支持key-value类型的数据
数据持久化 支持数据持久化,可以将数据保存到磁盘 不支持数据持久化
网络IO模型 单线程的多路 IO 复用模型 多线程的非阻塞IO模式
集群 原生支持cluster 模式集群 无原生

Redis是单线程还是多线程?Redis为什么这么快?

Redis6.0之前是单线程的,为什么Redis6.0之前采用单线程而不采用多线程呢?

简单来说,就是Redis官方认为没必要,单线程的Redis的瓶颈通常在CPU的IO,而在使用Redis时几乎不存在CPU成为瓶颈的情况。使用Redis主要的瓶颈在内存和网络,并且使用单线程也存在一些优点,比如系统的复杂度较低,可为维护性较高,避免了并发读写所带来的一系列问题。

Redis为什么这么快主要有以下几个原因:

  • 运行在内存中
  • 数据结构简单
  • 使用多路IO复用技术
  • 单线程实现,单线程避免了线程切换、锁等造成的性能开销。

Redis6.0之后为什么引入了多线程?

前面说了那么多Redis使用单线程的原因,但从Redis6.0后开始支持多线程了,简直打脸有点快。那么为什么较新的Redis版本又开始支持多线程了呢?

前面也说了Redis的瓶颈在内存和网络,Redis6.0引入多线程主要是为了解决网路IO读写这个瓶颈,执行命令还是单线程执行的,所以也不存在线程安全问题。

Redis6.0默认是否开启了多线程呢?

默认是没有开启的,如需开启,需要修改配置文件redis.conf:io-threads-do-reads  no,no改为yes

Redis的数据类型有哪些?

Redis的常见的数据类型有String、Hash、Set、List、ZSet。还有三种不那么常见的数据类型:Bitmap、HyperLogLog、Geospatial

数据类型 可以存储的值 可进行的操作 应用场景
STRING 字符串、整数、浮点数 对整数或浮点数可以进行自增、自减操作
对字符串操作
键值对缓存及常规计数: 微博数, 粉丝数
LIST 列表(内部使用双向列表实现) 向列表两端添加元素,或者获得列表的某一个片段 存储文章ID列表、存储评论列表等
SET 无序集合(内部使用值为空的散列表) 增加/删除元素、获取集合中元素、取交集并集等等 共同好友、共同关注等
ZSET 有序集合(内部使用散列表和跳表) 添加、获取、删除元素
根据分值范围或者成员来获取元素
计算一个键的排名
去重、获取排名前几的用户
HASH 包含键值对的无序散列表 添加、获取、移除单个键值对
获取所有键值对
检查某个键是否存在
常用于存储对象

Bitmap:位图,是一个以位为单位的数组,数组中只能存储1或0,数组的下标在Bitmap中叫做偏移量。Bitmap实现统计功能,更省空间。面试中常问的布隆过滤器就有用到这种数据结构,布隆过滤器可以判断出哪些数据一定不在数据库中,所以常被用来解决Redis缓存穿透问题。

Hyperloglog:HyperLogLog 是一种用于统计基数的数据集合类型,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。HyperLogLog 的优点是,在输入元素的数量或者体积非常大时,计算基数所需的空间总是固定 的、并且是很小的。缺点是 HyperLogLog 的统计规则是基于概率完成的,所以它给出的统计结果是有一定误差的,标准误算率是 0.81%。常见的应用场景:统计网站的UV

Geospatial:主要用于存储地理位置信息,常用于定位附近的人,打车距离的计算等。

Redis的数据结构有哪些?

很多人都会把数据结构和数据类型混为一谈,包括很多面试官问的时候也没有刻意区分这两个。Redis的数据结构比较多,篇幅有限,这里只重点介绍面试常问的跳跃表。

Redis的数据结构有简单动态字符串链表字典跳跃表整数集合压缩列表等。

简单动态字符串:大家都知道,Redis的底层是用C语言编写,但Redis并没有直接使用C语言传统的字符串表示,而是构建了一种名为简单动态字符串的抽象类型。

链表:链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。链表是列表的底层实现之一

字典:字典,又称为符号表(symbol table)、关联数组(associativearray)或映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构。字典在Redis中的应用相当广泛,比如Redis的数据库就是使用字典来作为底层实现的,对数据库的增、删、查、改操作也是构建在对字典的操作之上的。

整数集合: 整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。

压缩列表(ziplist):压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。

对象:可能看到这里,很多人在想Redis的数据结构和数据类型的区别,其实上面介绍的是Redis的底层数据结构,但Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象有序集合对象这五种类型的对象,每种对象都用到了至少一种我们前面所介绍的数据结构,是不是这就和前面对上了。

看到这里很多人会好奇,为什么不直接使用这些底层数据结构,而是要创建对象系统。对象系统主要有以下优点:

  • 通过这五种不同类型的对象,Redis可以在执行命令之前,根据对象的类型来判断一个对象是否可以执行给定的命令。
  • 我们可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。
  • 实现了基于引用计数技术的内存回收机制,当程序不再使用某个对象的时候,这个对象所占用的内存就会被自动释放,了解Java虚拟机的垃圾回收机制看到这里是不是很熟悉。
  • edis还通过引用计数技术实现了对象共享机制,这一机制可以在适当的条件下,通过让多个数据库键共享同一个对象来节约内存。

对象这部分占了比较大的篇幅,其实面试中问的也不多,但为了更方便理解,介绍地多些。顺便看下这些底层数据结构和对象系统的对应关系。

最后介绍下面试中常问的跳跃表。

跳跃表(skiplist):跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。跳跃表支持平均O(logN)、最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。跳跃表作是序集合键的底层实现之一

和链表、字典等数据结构被广泛地应用在Redis内部不同,Redis只在两个地方用到了跳跃表,一个是实现有序集合键,另一个是在集群节<stron>,除此之外,跳跃表在Redis里面没有其他用途。 </stron>

跳跃表本质上采用的是一种空间换时间的策略,是一种可以可以进行二分查找有序链表,跳表在原有的有序链表上增加了多级索引,通过索引来实现快速查询。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。

这是一个原始的有序列表,时间复杂度为O(n)。

为了提高查找效率,可以对链表建立一级索引,如下图,在之前找到11这个元素需要遍历6个节点,现在需要5个。链表越长,效率提升越明显。

为了继续提高查找效率可以继续增加索引

对于理想的跳表,每向上一层索引节点数量都是下一层的1/2,跳表的**时间复杂度为o(logn),空间复杂度为o(n)**,虽然是空间换时间的策略,这里举例存储的只是数字,如果是存储比较大的对象,浪费的空间就不值得一提了,因为索引结点只需要存储关键值和几个指针,并不需要存储对象。

跳表相比于红黑树的优点(redis为什么用跳表不同红黑树):

  • 内存占用更少,自定义参数化决定使用多少内存
  • 查询性能至少不比红黑树差
  • 简单更容易实现和维护

上面这三个优点是我在一篇博客中看到,这个问题redis作者本人也回应过。我这蹩脚的英文水平就不翻译了,以免跑偏了。

  • They are not very memory intensive. It's up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees.
  • A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees.
  • They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.

最后,说下Redis中的跳跃表和普通的跳跃表有什么区别?

  • Redis中的跳跃表分数(score)允许重复,即跳跃表的key允许重复,如果分数重复,还需要根据数据内容来进字典排序。普通的跳跃表是不支持的。
  • 第1层链表不是一个单向链表,而是一个双向链表。这是为了方便以倒序方式获取一个范围内的元素。
  • 在Redis的跳跃表中可以很方便地计算出每个元素的排名。

篇幅有限,关于跳表的实现细节就不过多介绍了,有兴趣的同学可以自行了解,本小节部分内容参考《Redis设计与实现》

Redis的应用场景有哪些?

  • 缓存:Redis基于内存,读写速度非常快,并且有键过期功能和键淘汰策略,可以作为缓存使用。
  • 排行榜:Redis提供的有序集合可以很方便地实现排行榜。
  • 分布式锁:Redis的setnx功能来实现分布式的锁。
  • 社交功能:实现共同好友、共同关注等
  • 计数器:通过String进行自增自减实现计数功能
  • 消息队列:Redis提供了发布、订阅、阻塞队列等功能,可以实现一个简单的消息队列。

Redis是单线程的,如何提高CPU的利用率?

可以在一个服务器上部署多个Redis实例,把他们当作不同的服务器使用。

过期键的删除策略

键的过期删除策略

常见的过期删除策略是惰性删除定期删除定时删除

  • 惰性删除:只有访问这个键时才会检查它是否过期,如果过期则清除。优点:最大化地节约CPU资源。缺点:如果大量过期键没有被访问,会一直占用大量内存。
  • 定时删除:为每个设置过期时间的key都创造一个定时器,到了过期时间就清除。优点:该策略可以立即清除过期的键。缺点:会占用大量的CPU资源去处理过期的数据。
  • 定期删除:每隔一段时间就对一些键进行检查,删除其中过期的键。该策略是惰性删除和定时删除的一个折中,既避免了占用大量CPU资源又避免了出现大量过期键不被清除占用内存的情况。

Redis中同时使用了惰性删除定期删除两种。

Redis的内存淘汰机制是什么样的?

Redis是基于内存的,所以容量肯定是有限的,有效的内存淘汰机制对Redis是非常重要的。

当存入的数据超过Redis最大允许内存后,会触发Redis的内存淘汰策略。在Redis4.0前一共有6种淘汰策略。

  • volatile-lru:当Redis内存不足时,会在设置了过期时间的键中使用LRU算法移除那些最少使用的键。(注:在面试中,手写LRU算法也是个高频题,使用双向链表和哈希表作为数据结构)
  • volatile-ttl:从设置了过期时间的键中移除将要过期的
  • volatile-random:从设置了过期时间的键中随机淘汰一些
  • allkeys-lru:当内存空间不足时,根据LRU算法移除一些键
  • allkeys-random:当内存空间不足时,随机移除某些键
  • noeviction:当内存空间不足时,新的写入操作会报错

前三个是在设置了过期时间的键的空间进行移除,后三个是在全局的空间进行移除

在Redis4.0后可以增加两个

  • volatile-lfu:从设置过期时间的键中移除一些最不经常使用的键(LFU算法:Least Frequently Used))
  • allkeys-lfu:当内存不足时,从所有的键中移除一些最不经常使用的键

这两个也是一个是在设置了过期时间的键的空间,一个是在全局空间。

Redis的持久化

什么是Redis的持久化?

因为Redis是基于内存的,为了让防止一些意外情况导致数据丢失,需要将数据持久化到磁盘上。

Redis常见的持久化机制有哪些?有什么有优缺点?

Redis提供了两种不同的持久化方式,一种是RDB,一种是AOF

RDB

RDB是redis默认的持久化方式,按照一定的时间间隔将内存的数据以快照的形式保存到硬盘,恢复时是将快照读取到内存中。RDB持久化实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。如下图

优点:

  • 适合对大规模的数据恢复,比AOF的启动效率高
  • 只有一个文件 dump.rdb,方便持久化
  • 性能最大化,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

缺点:

  • 数据安全性低,在一定间隔时间内做一次备份,如果Redis突然宕机,会丢失最后一次快照的修改
  • 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

AOF

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

优点:

  • 具备更高的安全性,Redis提供了3种同步策略,分别是每秒同步、每修改同步和不同步。相比RDB突然宕机丢失的数据会更少,每秒同步会丢失一秒种的数据,每修改同步会不会丢失数据。
  • 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。
  • AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作,可以通过该文件完成数据的重建。

缺点:

  • 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
  • 根据AOF选择同步策略的不同,效率也不同,但AOF在运行效率上往往会慢于RDB。

Redis的事务

什么是Redis的事务

Redis的事务是一个单独的隔离操作,事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断,所以Redis事务是在一个队列中,一次性、顺序性、排他性地执行一系列命令。

Redis 事务的主要作用就是串联多个命令防止别的命令插队

Redis事务的相关命令

Redis事务相关的命令主要有以下几种:

  • DISCARD:命令取消事务,放弃执行事务队列内的所有命令,恢复连接为非 (transaction) 模式,如果正在使用 WATCH 命令监视某个(或某些) key,那么取消所有监视,等同于执行命令 UNWATCH 。
  • EXEC:执行事务队列内的所有命令。
  • MULTI:用于标记一个事务块的开始。
  • UNWATCH:用于取消 WATCH命令对所有 key 的监视。如果已经执行过了EXEC或DISCARD命令,则无需再执行UNWATCH命令,因为执行EXEC命令时,开始执行事务,WATCH命令也会生效,而 DISCARD命令在取消事务的同时也会取消所有对 key 的监视,所以不需要再执行UNWATCH命令了
  • WATCH:用于标记要监视的key,以便有条件地执行事务,WATCH命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。

Redis事务执行的三个阶段

  1. 开始事务(MULTI)
  2. 命令入列
  3. 执行事务(EXEC)

Redis事务的特性

  • Redis事务不保证原子性,单条的Redis命令是原子性的,但事务不能保证原子性。
  • Redis事务是有隔离性的,但没有隔离级别,事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。(顺序性、排他性)
  • Redis事务不支持回滚,Redis执行过程中的命令执行失败,其他命令仍然可以执行。(一次性)

Redis事务为什么不支持回滚?

在Redis的事务中,命令允许失败,但是Redis会继续执行其它的命令而不是回滚所有命令,是不支持回滚的。

主要原因有以下两点:

  • Redis 命令只在两种情况失败:
    • 语法错误的时候才失败(在命令输入的时候不检查语法)。
    • 要执行的key数据类型不匹配:这种错误实际上是编程错误,这应该在开发阶段被测试出来,而不是生产上。
  • 因为不需要回滚,所以Redis内部实现简单并高效。(在Redis为什么是单线程而不是多线程也用了这个思想,实现简单并且高效)

Redis的集群、主从、哨兵

Redis集群的实现方案有哪些?

在说Redis集群前,先说下为什么要使用Redis集群,Redis单机版主要有以下几个缺点:

  • 不能保证数据的可靠***部署在一台服务器上,一旦服务器宕机服务就不可用。
  • 性能瓶颈,内存容量有限,处理能力有限

Redis集群就是为了解决Redis单机版的一些问题,Redis集群主要有以下几种方案

  • Redis 主从模式
  • Redis 哨兵模式
  • Redis 自研
  • Redis Clustert

下面对这几种方案进行简单地介绍:

Redis主从模式

Redis单机版通过RDB或AOF持久化机制将数据持久化到硬盘上,但数据都存储在一台服务器上,并且读写都在同一服务器(读写不分离),如果硬盘出现问题,则会导致数据不可用,为了避免这种问题,Redis提供了复制功能,在master数据库中的数据更新后,自动将更新的数据同步到slave数据库上,这就是主从模式的Redis集群,如下图

主从模式解决了Redis单机版存在的问题,但其本身也不是完美的,主要优缺点如下:

优点:

  • 高可靠性,在master数据库出现故障后,可以切换到slave数据库
  • 读写分离,slave库可以扩展master库节点的读能力,有效应对大并发量的读操作

缺点:

  • 不具备自动容错和恢复能力,主节点故障,从节点需要手动升为主节点,可用性较低

Redis 哨兵模式

为了解决主从模式的Redis集群不具备自动容错和恢复能力的问题,Redis从2.6版本开始提供哨兵模式

哨兵模式的核心还是主从复制,不过相比于主从模式,多了一个竞选机制(多了一个哨兵集群),从所有从节点中竞选出主节点,如下图

从上图中可以看出,哨兵模式相比于主从模式,主要多了一个哨兵集群,哨兵集群的主要作用如下:

  • 监控所有服务器是否正常运行:通过发送命令返回监控服务器的运行状态,处理监控主服务器、从服务器外,哨兵之间也相互监控
  • 故障切换:当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换master。同时那台有问题的旧主也会变为新主的从,也就是说当旧的主即使恢复时,并不会恢复原来的主身份,而是作为新主的一个从。

哨兵模式的优缺点:

优点:

  • 哨兵模式是基于主从模式的,解决可主从模式中master故障不可以自动切换故障的问题。

缺点:

  • 浪费资源,集群里所有节点保存的都是全量数据,数据量过大时,主从同步会严重影响性能
  • Redis主机宕机后,投票选举结束之前,谁也不知道主机和从机是谁,此时Redis也会开启保护机制,禁止写操作,直到选举出了新的Redis主机。
  • 只有一个master库执行写请求,写操作会单机性能瓶颈影响

Redis 自研

哨兵模式虽然解决了主从模式存在的一些问题,但其本身也存在一些弊端,比如数据在每个Redis实例中都是全量存储,极大地浪费了资源,为了解决这个问题,Redis提供了Redis Cluster,实现了数据分片存储,但Redis提供Redis Cluster之前,很多公司为了解决哨兵模式存在的问题,分别自行研发Redis集群方案。

客户端分片

客户端分片是把分片的逻辑放在Redis客户端实现,通过Redis客户端预先定义好的路由规则(使用哈希算法),把对Key的访问转发到不同的Redis实例中,查询数据时把返回结果汇集。如下图

客户端分片的优缺点:

优点:Redis实例彼此独立,相互无关联,每个Redis实例像单服务器一样运行,非常容易线性扩展,系统的灵活性很强。

缺点:

  • 客户端sharding不支持动态增删节点。服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新调整。
  • 运维成本比较高,集群的数据出了任何问题都需要运维人员和开发人员一起合作,减缓了解决问题的速度,增加了跨部门沟通的成本。
  • 在不同的客户端程序中,维护相同的路由分片逻辑成本巨大。比如:java项目、PHP项目里共用一套Redis集群,路由分片逻辑分别需要写两套一样的逻辑,以后维护也是两套。
代理分片

客户端分片的最大问题就是服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新调整。

为了解决这个问题,代理分片出现了,代理分片将客户端分片模块单独分了出来,作为Redis客户端和服务端的桥梁,如下图

代理模式的优点:解决了服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新调整的问题。缺点是由于Redis客户端的每个请求都经过代理才能到达Redis服务器,这个过程中会产生性能损失。

常见的代理分片有Twitter开源的Redis代理Twemproxy和豌豆荚自主研发的Codis

Redis Cluster

前面介绍了为了解决哨兵模式的问题,各大企业提出了一些数据分片存储的方案,在Redis3.0中,Redis也提供了响应的解决方案,就是Redist Cluster。

Redis Cluster是一种服务端Sharding技术,Redis Cluster并没有使用一致性hash,而是采用slot(槽)的概念,一共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行。

什么是Redis哈希槽呢?本来不想详细介绍这个的,但面试确实经常问,还是简单说一下,

在介绍slot前,要先介绍下一致性哈希(客户端分片经常会用的哈希算法),那这个一致性哈希有什么用呢?其实就是用来保证节点负载均衡的,那么多主节点,到底要把数据存到哪个主节点上呢?就可以通过一致性哈希算法要把数据存到哪个节点上。

一致性哈希

下面详细说下一致性哈希算法,首先就是对key计算出一个hash值,然后对2^32取模,也就是值的范围在[0, 2^32 -1],一致性哈希将其范围抽象成了一个圆环,使用CRC16算法计算出来的哈希值会落到圆环上的某个地方。

现在将Redis实例也分布在圆环上,如下图(图片来源于网络)


假设A、B、C三个Redis实例按照如图所示的位置分布在圆环上,通过上述介绍的方法计算出key的hash值,发现其落在了位置E,按照顺时针,这个key值应该分配到Redis实例A上。

如果此时Redis实例A挂了,会继续按照顺时针的方向,之前计算出在E位置的key会被分配到RedisB,而其他Redis实例则不受影响。

但一致性哈希也不是完美的,主要存在以下问题:当Redis实例节点较少时,节点变化对整个哈希环中的数据影响较大,容易出现部分节点数据过多,部分节点数据过少的问题,出现数据倾斜的情况,如下图(图片来源于网络),数据落在A节点和B节点的概率远大于B节点


为了解决这种问题,可以对一致性哈希算法引入虚拟节点(A#1,B#1,C#1),如下图(图片来源于网络),

那这些虚拟节点有什么用呢?每个虚拟节点都会映射到真实节点,例如,计算出key的hash值后落入到了位置D,按照顺时针顺序,应该落到节点C#1这个虚拟节点上,因为虚拟节点会映射到真实节点,所以数据最终存储到节点C。

虚拟槽

在Redis Cluster中并没有使用一致性哈希,而引进了虚拟槽。虚拟槽的原理和一致性哈希很像,Redis Cluster一共有2^14(16384)个槽,所有的master节点都会有一个槽范围比如0~1000,槽数是可以迁移的。master节点的slave节点不分配槽,只拥有读权限,其实虚拟槽也可以看成一致性哈希中的虚拟节点。

虚拟槽和一致性哈希算法的实现上也很像,先通过CRC16算法计算出key的hash值,然后对16384取模,得到对应的槽位,根据槽找到对应的节点,如下图。

使用虚拟槽的好处:

  • 更加方便地添加和移除节点,增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了,当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了,不需要停掉Redis任何一个节点的服务(采用一致性哈希需要增加和移除节点需要rehash




....博主太懒了字数太多了,不想写了...

#Java开发##后端开发##面试##Java找工作##redis#
全部评论
你好,请问这是原创的还是搬运的,写得挺好的,想看剩下的内容😀
点赞 回复 分享
发布于 2022-07-24 19:14

相关推荐

10 121 评论
分享
牛客网
牛客企业服务