目录
1.什么是redis事务?
2.如何使用 Redis 事务?
3.Redis 事务为什么不支持原子性
4.Redis 事务支持持久性吗
5.Redis事务基于lua脚本的实现
6.Redis集群的主从复制模型是怎样的?
7.Redis集群中,主从复制的数据同步的步骤
8.Redis 增量复制的流程?
9.Redis 为什么主从全量复制使用RDB而不使用AOF?
10.Redis 为什么还有无磁盘复制模式?
11.什么是Redis Sentinel? 有什么用?
12.Redis Sentinel集群是通过什么方式组建的?
13.Redis 哨兵是如何监控Redis集群的?
14.Sentinel 如何检测节点是否下线?主观下线与客观下线的区别?
15.哨兵的选举机制是什么样的?
16.Redis主库判定客观下线了,那么如何从剩余的从库中选择一个新的主库呢?
17.新的主库选择出来后,如何进行故障的转移?
18.Sentinel 可以防止脑裂吗?
19.为什么需要 Redis Cluster?解决了什么问题?有什么优势?
20.Redis Cluster 是如何分片的?
21.为什么 Redis Cluster 的哈希槽是 16384 个?
22.如何确定给定 key 的应该分布到哪个哈希槽中?
23.Redis Cluster 支持重新分配哈希槽吗?
24.Redis Cluster 扩容缩容期间可以提供服务吗?
25.Redis Cluster 中的节点是怎么进行通信的?
1.什么是redis事务?
Redis事务是指在Redis数据库中进行一组命令的操作,这些命令可以被作为一个单独的、原子性的操作进行提交或者回滚。事务可以保证一组命令的原子性,即在一个事务中的所有命令要么全部被执行成功,要么全部不执行。
2.如何使用 Redis 事务?
在Redis中使用事务需要使用MULTI、EXEC、WATCH和DISCARD等命令。下面是一个简单的例子,展示如何使用Redis事务。
假设我们需要在Redis中将用户的账户余额减去一定数量的钱,并将这些钱转移到另一个账户。我们可以使用Redis事务来保证这两个操作的原子性。
首先,使用MULTI命令来开始一个事务:
MULTI
然后,将要执行的命令添加到事务中,例如:
DECRBY user:100:balance 50
INCRBY user:101:balance 50
这里我们使用DECRBY命令来减少用户100的余额,使用INCRBY命令来增加用户101的余额。注意,这些命令并没有直接执行,而是被添加到了事务中。
接下来,使用EXEC命令来提交事务:
EXEC
如果事务执行成功,Redis将返回一个包含每个命令执行结果的数组,如果事务执行失败,Redis将返回一个空数组。
如果在事务执行期间,我们需要监视一个或多个键,以便在其他客户端对它们进行修改时取消事务,我们可以使用WATCH命令。例如,我们可以使用以下命令来监视用户100的余额键:
WATCH user:100:balance
如果在事务执行期间,其他客户端修改了被监视的键,那么事务将被取消,所有已添加到事务中的命令都不会被执行。此时,我们可以使用DISCARD命令来取消事务并清除所有已添加到事务中的命令:
DISCARD
这样,我们就可以使用Redis事务来保证一组操作的原子性,避免由并发操作引起的数据不一致问题。
3.Redis 事务为什么不支持原子性
虽然Redis的事务可以在多个命令之间执行,但是在执行事务期间,Redis不会将它们视为一个单独的操作单元。因此,如果在一个事务中执行多个命令,其中一个命令失败,那么其他命令仍然会执行,这就违反了原子性的要求。
Redis事务的主要目的是减少多个命令之间的通信开销,并确保在执行事务期间,其他客户端不能干扰正在执行的事务。因此,Redis事务只是一种逻辑分组命令的方式,并不能保证原子性。
Redis 官网也解释了自己为啥不支持回滚。简单来说就是 Redis 开发者们觉得没必要支持回滚,这样更简单便捷并且性能更好。Redis 开发者觉得即使命令执行错误也应该在开发过程中就被发现而不是生产过程中。
4.Redis 事务支持持久性吗
Redis 的持久化方式都是异步执行的,因为同步持久化会对 Redis 的性能造成很大的影响。因此,在使用 Redis 事务时,虽然事务本身可以保证一组命令的原子性,但是对于这些命令对 Redis 中的数据所做的修改是否被持久化到磁盘上,Redis 并不能做出保证。
5.Redis事务基于lua脚本的实现
Redis 事务可以基于 Lua 脚本来实现。在 Lua 脚本中,可以使用 Redis 的 MULTI 命令和 EXEC 命令来开启和提交事务,并在事务中执行多个 Redis 命令。Lua 脚本可以将多个 Redis 命令封装为一个整体,从而保证这些命令的原子性执行。
以下是一个使用 Lua 脚本实现 Redis 事务的示例:
-- 定义 Lua 脚本,其中包含多个 Redis 命令
local script = [[
-- 开启 Redis 事务
redis.call('MULTI')
-- 执行多个 Redis 命令
redis.call('SET', 'name', 'Alice')
redis.call('INCR', 'count')
-- 提交 Redis 事务
return redis.call('EXEC')
]]
-- 加载 Lua 脚本
local sha = redis.call('SCRIPT', 'LOAD', script)
-- 执行 Lua 脚本,即执行 Redis 事务
redis.call('EVALSHA', sha)
上述 Lua 脚本使用了 Redis 的 MULTI 命令开启一个事务,然后执行了两个 Redis 命令(SET 和 INCR),最后使用 EXEC 命令提交事务。通过使用 EVALSHA 命令来执行 Lua 脚本,从而实现了 Redis 事务的功能。
在 Lua 脚本中,还可以使用 Redis 的 WATCH 命令来监控一个或多个键,并在事务执行之前检查这些键的值是否被其他客户端修改过。如果这些键的值被修改过,则事务执行失败,需要重新执行。通过使用 WATCH 命令,可以保证事务执行期间,所操作的键值不会被其他客户端修改,从而避免了其他客户端的干扰。
6.Redis集群的主从复制模型是怎样的?
Redis集群采用主从复制模型实现高可用性和可扩展性。在Redis集群中,每个节点可以是主节点或从节点。
主节点负责处理客户端请求,并将写操作同步到所有从节点。从节点接收来自主节点的同步复制数据,并复制主节点上的所有写操作,以保持自己的数据与主节点同步。
当主节点失效时,Redis集群会自动将其中一个从节点升级为主节点,以确保服务的可用性和数据的完整性。当新的主节点选举完成后,其他从节点将开始同步新的主节点,以便它们能够快速接管主节点的工作并继续服务。
Redis集群的主从复制模型通过实现数据的多个副本,提高了系统的可用性和可靠性。同时,通过添加更多的从节点,Redis集群还可以水平扩展,以处理更多的并发请求。
7.Redis集群中,主从复制的数据同步的步骤
1.同步命令:当从节点重新上线后,它会向主节点发送SYNC命令,表示要进行全量同步。
2.全量同步:主节点接收到SYNC命令后,会将自己的所有数据发送给从节点,从节点会将这些数据写入自己的内存中。
3.增量同步:全量同步完成后,主节点会将新的写操作发送给从节点,从节点也会在自己的内存中执行这些写操作,以保持数据同步。
需要注意的是,当主节点接收到写操作后,它会先将数据写入自己的内存中,然后才会将命令传递给从节点。因此,如果主节点在传递命令之前崩溃了,可能会导致从节点的数据不完整。在这种情况下,从节点需要重新向主节点发送SYNC命令,进行全量同步。
8.Redis 增量复制的流程?
Redis 增量复制是 Redis 主从复制的一种方式,用于将主节点的数据同步到从节点。其流程如下:
-
从节点发送 SYNC 命令到主节点,请求全量复制。主节点接收到请求后开始生成 RDB 快照文件,并将文件的数据同步给从节点,从节点接收到数据后将其保存到本地。
-
主节点在生成 RDB 文件的过程中,将所有的写命令(包括读写命令)缓存到内存中,并将缓存的写命令追加到一个专门用于增量复制的内存缓冲区中。这个缓冲区称为复制缓冲区(replication buffer)。
-
主节点将 RDB 文件传输完毕后,开始将复制缓冲区中的命令同步给从节点。主节点会将自己的复制缓冲区中的命令传输给从节点,并等待从节点对其进行确认。主节点会记录每个从节点在哪个位置接收到了命令,以便在下一次传输时只传输从这个位置开始的命令。
-
从节点接收到主节点的命令后,将其保存到本地的数据库中,并向主节点发送一个 ACK 命令,表示已经接收到了这些命令。主节点接收到 ACK 命令后,更新自己的记录,标记该从节点已经同步到了哪个位置。
-
从节点定期向主节点发送 PSYNC 命令,请求增量复制。主节点接收到请求后,判断是否可以进行增量复制。如果从节点的复制偏移量(replication offset)在主节点的复制缓冲区中,则可以进行增量复制;否则需要进行全量复制。
-
如果可以进行增量复制,则主节点会将从节点当前的复制偏移量所对应的命令以及其之后的命令传输给从节点。从节点接收到这些命令后,将其保存到本地数据库中。
-
如果不能进行增量复制,则主节点会重新生成 RDB 文件,并将其传输给从节点。
-
重复执行步骤 5 到 7,直到从节点与主节点同步完成。
需要注意的是,增量复制的缓存空间是有限的,如果主节点的缓存区已经满了,就无法再缓存新的写命令了,此时主节点只能进行全量复制。
9.Redis 为什么主从全量复制使用RDB而不使用AOF?
-
RDB 文件的数据格式更加简单,可以更快地进行数据加载和恢复。RDB 文件只包含一份数据的快照,而 AOF 文件则记录了所有的写命令,因此 AOF 文件的数据格式更加复杂。
-
RDB 文件的体积更小,传输速度更快。RDB 文件只包含一份数据的快照,因此其体积相对于 AOF 文件更小,传输速度也更快。在进行数据同步时,传输速度是一个重要的考虑因素。
-
RDB 文件可以在不停止 Redis 服务的情况下进行数据备份和恢复。由于 RDB 文件只包含一份数据的快照,因此可以在 Redis 不停止服务的情况下进行数据备份和恢复。而 AOF 文件则需要在 Redis 停止服务时进行备份和恢复。
10.Redis 为什么还有无磁盘复制模式?
Redis 无磁盘复制模式(Diskless replication)是一种新的主从复制方式,其主要优点是可以减少网络带宽的占用,提高主从同步的速度,同时也减轻了主节点的磁盘负载。其原理是将主节点中的数据直接传输给从节点,而不需要将数据先写入到磁盘中。在无磁盘复制模式下,主节点不需要生成 RDB 文件或 AOF 文件,因此也不需要进行磁盘的读写操作。
无磁盘复制模式的优点在于可以提高主从同步的速度,尤其是在大规模的集群中。由于数据传输是通过网络进行的,因此在数据量较大的情况下,会占用较大的带宽。使用无磁盘复制模式可以减少网络带宽的占用,提高主从同步的速度。同时,无磁盘复制模式还可以减轻主节点的磁盘负载,避免了磁盘读写带来的性能问题。
然而,无磁盘复制模式也存在一些缺点。由于数据传输是通过网络进行的,因此会增加网络的负担,特别是在数据量较大的情况下。此外,由于数据传输是直接从主节点到从节点,因此如果从节点数量较多,可能会增加主节点的负载压力。
启用 Redis 无磁盘复制模式需要同时满足以下两个条件:
-
从节点必须运行在 Redis 6.0 及以上版本。
-
主节点必须运行在 Redis 6.2 及以上版本,并且需要在配置文件中设置 "repl-diskless-sync" 选项为 "yes"。
11.什么是Redis Sentinel? 有什么用?
Redis Sentinel 的主要作用是监控 Redis 主节点和从节点的健康状况,并在发生故障时自动切换到备用节点,保证 Redis 服务的连续性和可用性。具体来说,Redis Sentinel 提供了以下功能:
-
故障检测:Redis Sentinel 可以周期性地检测 Redis 主从节点的健康状态,包括网络连接状态、CPU 使用率、内存使用率等。
-
故障转移:当 Redis 主节点宕机或不可达时,Redis Sentinel 会自动将其中一台从节点切换为新的主节点,使得 Redis 服务可以继续提供服务。
-
自动故障恢复:当 Redis 主节点恢复正常运行时,Redis Sentinel 会将其重新添加到主从复制架构中,使得 Redis 服务恢复正常运行。
-
配置管理:Redis Sentinel 可以动态地管理 Redis 主从节点的配置,包括添加、删除、修改节点、设置节点权重等。
Redis Sentinel 的主要作用是提高 Redis 服务的可靠性和可用性,保证 Redis 服务在出现故障时能够快速地自动切换到备用节点,从而实现无缝切换。在生产环境中,Redis Sentinel 经常被用于构建高可用的 Redis 集群,保证 Redis 服务能够持续、稳定地运行。
12.Redis Sentinel集群是通过什么方式组建的?
Redis Sentinel(哨兵)实例之间可以通过 Redis 提供的 pub/sub(发布/订阅)机制相互发现。当一个 Sentinel 实例启动时,它会向 Redis 主节点发送 SENTINEL is-master-down-by-addr 命令,询问主节点是否宕机。如果主节点未响应,哨兵实例会向其他 Sentinel 实例发送同样的命令,以尝试确认主节点是否宕机。这个过程就利用了 Redis 的 pub/sub 机制,哨兵实例可以通过订阅特定的频道,接收其他 Sentinel 实例发送的状态更新消息,以便及时发现主节点故障。
在哨兵集群中,每个 Sentinel 实例都会向特定的频道发送状态更新信息,包括主节点的状态、从节点的状态以及哨兵实例的状态等。其他 Sentinel 实例可以通过订阅这些频道,接收并处理状态更新信息,从而保证 Redis Sentinel 集群的稳定性和可用性。
在主从集群中,主库上有一个名为__sentinel__:hello
的频道,不同哨兵就是通过它来相互发现,实现互相通信的。在下图中,哨兵 1 把自己的 IP(172.16.19.3)和端口(26579)发布到__sentinel__:hello
频道上,哨兵 2 和 3 订阅了该频道。那么此时,哨兵 2 和 3 就可以从这个频道直接获取哨兵 1 的 IP 地址和端口号。然后,哨兵 2、3 可以和哨兵 1 建立网络连接。
通过这个方式,哨兵 2 和 3 也可以建立网络连接,这样一来,哨兵集群就形成了。它们相互间可以通过网络连接进行通信,比如说对主库有没有下线这件事儿进行判断和协商。
13.Redis 哨兵是如何监控Redis集群的?
Redis Sentinel(哨兵)通过定期发送命令检查 Redis 主从节点的健康状况,以及通过 Redis 提供的 pub/sub 机制实时监控 Redis 集群状态,从而保证 Redis 集群的可用性和稳定性。具体来说,Redis Sentinel 主要通过以下几个方面来监控 Redis 集群:
1.定期发送命令检查 Redis 主从节点的健康状况
Redis Sentinel 会定期向 Redis 主从节点发送命令,例如 PING 命令或 INFO 命令,检查节点是否正常运行。如果节点未能正常响应命令,Redis Sentinel 会将其标记为 DOWN 状态,并开始进行自动故障转移操作。
2.通过 pub/sub 机制实时监控 Redis 集群状态
Redis Sentinel 使用 Redis 提供的 pub/sub 机制,订阅特定的频道,以接收 Redis 集群状态的更新信息。例如,当一个 Redis 主节点发生故障时,哨兵会向订阅的频道发布故障信息,通知其他哨兵实例进行故障转移操作。其他哨兵实例也会通过订阅这些频道,接收更新信息,从而保证集群状态的一致性和正确性。
3.定义故障转移策略和选举算法
Redis Sentinel 可以通过配置文件或命令行参数定义故障转移策略和选举算法,以满足不同场景下的需求。例如,可以定义故障转移操作的超时时间和重试次数,或者指定选举算法的优先级和权重等。
14.Sentinel 如何检测节点是否下线?主观下线与客观下线的区别?
Redis Sentinel 是一个高可用性的解决方案,它通过监控 Redis 实例的状态来实现自动故障转移。为了检测 Redis 节点是否下线,Redis Sentinel 会使用以下两种方式:
1.主观下线(Subjective Down)
在 Redis Sentinel 中,每个 Sentinel 进程会定期向其他 Sentinel 进程发送 PING 命令来检查 Redis 实例是否正常运行。如果一个 Sentinel 进程在指定时间内没有收到 Redis 实例的 PONG 响应,那么它就会将该 Redis 实例标记为主观下线。这个判断是基于 Sentinel 进程本身的主观认定。
2.客观下线(Objective Down)
除了主观下线外,Redis Sentinel 还支持客观下线的检测。当多个 Sentinel 进程都认为某个 Redis 实例已经下线时,该 Redis 实例就会被标记为客观下线。具体来说,当超过一半的 Sentinel 进程在指定时间内都认为某个 Redis 实例已经下线时,该 Redis 实例就会被标记为客观下线。
15.哨兵的选举机制是什么样的?
哨兵的选举机制使用的是Raft选举算法。
Raft选举算法是一种分布式一致性算法,它将节点分为三种角色:leader(领导者)、follower(跟随者)和candidate(候选者)。在初始状态下,所有节点都是follower。如果某个follower节点无法与leader节点保持通信,它就会变成candidate,并且开始新一轮的选举。
选举过程分为以下几个步骤:
-
选举超时(Election Timeout):candidate节点会在随机的时间间隔内成为candidate状态。此时它会向其他节点发送请求投票的消息,并开始计时。
-
收集投票(Request Vote):收到投票请求的节点会检查候选者的term(任期)是否比自己的大,如果是,则转换为follower状态,并将自己的vote for设置为候选者的ID。如果已经投过票或者发现候选者的term比自己小,则拒绝投票。
-
获得多数投票(Winning the Election):如果候选者收到了超过半数的投票,则它将成为新的leader,并向其他节点发送心跳消息,让它们知道新的leader已经产生。
-
防止分裂(Preventing Split Votes):如果候选者的term比follower节点的term大,则follower节点会转换为candidate状态,开始新一轮的选举。
在哨兵中,每个sentinel节点都可以成为candidate,通过相互之间的投票,选出新的leader节点。为了确保选举的稳定性和可靠性,哨兵中的选举过程还加入了一些附加机制,例如quorum机制,即必须得到大部分节点的投票才能选举成功。这些机制都是为了确保哨兵集群在出现故障或者网络分裂等情况下能够快速地选出新的leader节点,保证系统的可用性。
16.Redis主库判定客观下线了,那么如何从剩余的从库中选择一个新的主库呢?
1.首先,通过 Sentinel 或其他监控工具,检测出当前可用的所有从库,并且筛选出健康的、回复哨兵 ping 响应的从节点。
2.接着,从健康的从节点中选择一个作为新的主库,可以依据以下优先级:
-
优先选择配置文件中设置了
slave-priority
优先级最高的从节点。 -
如果有多个从节点的
slave-priority
优先级相同,那么就选择其中的一个复制偏移量最大的从节点作为新的主库。因为复制偏移量最大的从节点的数据最新,因此成为新的主库后,数据的损失最小。 -
如果多个从节点的复制偏移量也相同,那么可以随机选择其中的一个作为新的主库。
3.在选择新的主库后,需要更新其他所有健康的从节点的配置文件,使其成为新的从节点,开始复制新的主库。这样可以确保数据的一致性。
总之,通过筛选健康的、回复哨兵 ping 响应的从节点,并优先选择 slave-priority
优先级最高的节点和复制偏移量最大的节点,可以选择出最适合成为新主库的从节点,从而实现 Redis 的高可用性。
17.新的主库选择出来后,如何进行故障的转移?
Redis Sentinel 实现故障转移的过程如下:
1.主节点失效检测
Sentinel 定期向主节点发送 PING 命令检测主节点是否还处于正常状态。如果 Sentinel 在指定时间内未能收到主节点的 PONG 响应,或者收到了一个认为主节点已经下线的 Sentinel 的通知,那么 Sentinel 将判断主节点已经失效。
2.选择新的主节点
当 Sentinel 发现主节点已经失效后,它会从 Redis 从节点中选择一个作为新的主节点。在选择新的主节点时,Sentinel 会考虑多个因素,比如从节点的复制偏移量、优先级等。
3.执行故障转移操作
一旦 Sentinel 确定了新的主节点,它会向其他 Sentinel 进程发送一条 SENTINEL is-master-down-by-addr 命令,要求其他 Sentinel 进程执行故障转移操作。在执行故障转移操作时,Sentinel 会:
- 将从节点切换到新的主节点,以确保数据的一致性。
- 更新 Redis 配置,使从节点成为新的主节点,并通知客户端连接新的主节点。
4.恢复原来的主节点
一旦 Sentinel 检测到原来的主节点已经恢复,它会将原来的主节点重新加入 Redis 集群中,并将其作为新的从节点进行复制。
18.Sentinel 可以防止脑裂吗?
Sentinel 可以一定程度上防止 Redis 节点的脑裂问题。
脑裂是指由于网络分区或者硬件故障等原因,导致 Redis 集群中的节点互相失去连接,无法进行正常的通信和数据同步。在这种情况下,Redis 集群会分裂成多个部分,每个部分认为自己是正确的,从而导致数据一致性和可用性的问题。
Sentinel 可以通过以下方式防止脑裂:
-
选举主节点:当 Sentinel 监测到主节点无法正常工作时,它会通过 Sentinel 集群中的选举机制,选举一个新的主节点。只有一个节点被选举为主节点,其他节点成为从节点。
-
故障转移:当 Sentinel 发现主节点无法正常工作时,它会将新主节点的地址广播给从节点,让它们切换到新的主节点。这种方式可以避免脑裂问题,因为只有一个新的主节点,从节点之间的数据同步也是在这个新主节点下进行的。
-
Quorum:Sentinel 使用 quorum 机制来保证故障转移的正确性。Quorum 是指在集群中需要多少个 Sentinel 才能执行故障转移操作。通常情况下,Quorum 设置为 (Sentinel 数量/2 +1)。这样设置可以确保故障转移只会在大多数 Sentinel 都同意的情况下进行,从而避免脑裂问题。
尽管 Sentinel 能够一定程度上防止脑裂问题,但在极端情况下,仍然有可能发生脑裂,例如网络分区过大或者 Sentinel 数量过少等情况。因此,为了进一步提高 Redis 集群的高可用性,建议采用 Redis Cluster 等分布式 Redis 方案来避免脑裂问题。
19.为什么需要 Redis Cluster?解决了什么问题?有什么优势?
Redis Cluster 是 Redis 的分布式集群方案,可以解决单机 Redis 的容量和性能瓶颈问题,并提供更高的可用性和可扩展性。
具体来说,需要 Redis Cluster 主要是为了解决以下问题:
-
容量限制:单机 Redis 的容量受限于内存大小,如果需要存储更多的数据,就需要使用更多的 Redis 实例,而这样会导致数据分散在多个实例中,管理和维护难度增加。
-
性能瓶颈:单机 Redis 的性能在处理大规模数据时会受到限制,例如大量的读写请求会导致单机 Redis 的响应速度变慢。
-
可用性:单机 Redis 面临单点故障问题,如果 Redis 实例出现故障,就会导致整个 Redis 服务不可用。
Redis Cluster 通过将数据分片到多个 Redis 节点上,实现数据的分布式存储和访问。每个 Redis Cluster 节点都可以同时处理多个请求,从而提高了 Redis 的性能和吞吐量。此外,Redis Cluster 还可以自动进行数据备份和故障转移,从而保证 Redis 集群的高可用性。
Redis Cluster 的优势主要包括:
-
横向扩展:Redis Cluster 可以方便地实现数据的横向扩展,通过增加 Redis 节点的数量,可以增加集群的容量和性能,而无需更改应用程序。
-
高可用性:Redis Cluster 采用主从复制机制来保证数据的高可用性。当主节点失效时,可以自动切换到从节点,从而避免单点故障问题。
-
分布式管理:Redis Cluster 可以方便地进行节点的添加、删除和重分片等操作,而无需停止服务。
-
自动负载均衡:Redis Cluster 可以根据每个节点的负载情况,自动将请求分配到最适合的节点上,从而实现负载均衡。
总之,Redis Cluster 提供了更高的可扩展性、高可用性、性能和灵活性,适用于需要存储大量数据和高并发访问的场景。
20.Redis Cluster 是如何分片的?
Redis Cluster 使用哈希槽(hash slot)分片策略,将数据按照键名的哈希值映射到一个 0 到 16383 的哈希槽上,然后将每个哈希槽分配到不同的节点上进行存储。
具体来说,Redis Cluster 会将每个节点分配一定数量的哈希槽,每个哈希槽都对应一个键值对。当客户端发送命令时,Redis Cluster 会根据命令所涉及的键名的哈希值,将命令分配到相应的哈希槽所在的节点上进行处理。
例如,如果一个 Redis Cluster 集群有 3 个节点 A、B、C,每个节点被分配了 5461 个哈希槽,那么哈希槽 0 到 5460 就属于节点 A,5461 到 10921 属于节点 B,10922 到 16383 属于节点 C。当客户端发送一个 SET key1 value1 命令时,Redis Cluster 会将 key1 的哈希值计算出来,并将该命令分配到对应的哈希槽所在的节点上进行处理。如果 key1 的哈希值在 0 到 5460 范围内,那么该命令就会被分配到节点 A 上进行处理。
哈希槽分片策略可以保证每个节点存储的数据均衡分布,同时也保证了集群的可扩展性。当需要扩容或缩容时,只需将哈希槽重新分配到新的节点上即可。此外,哈希槽分片策略还可以保证节点之间的数据相互独立,避免了单点故障的风险。
21.为什么 Redis Cluster 的哈希槽是 16384 个?
Redis实例之间「通讯」会相互交换「槽信息」,那如果槽过多(意味着网络包会变大),网络包变大,意味着会「过度占用」网络的带宽,Redis作者认为集群在一般情况下是不会超过1000个实例,那就取了16384个,即可以将数据合理打散至Redis集群中的不同实例,又不会在交换数据时导致带宽占用过多。
22.如何确定给定 key 的应该分布到哪个哈希槽中?
在 Redis 集群中,每个 key 都会被映射到一个哈希槽中,确定给定 key 的哈希槽可以按照以下步骤进行:
-
对 key 计算哈希值,使用的哈希函数为 CRC16 校验和算法,这个算法会返回一个 16 位的无符号整数。
-
将这个 16 位无符号整数除以哈希槽的数量(默认为 16384),取余数,得到的结果就是 key 应该被映射到的哈希槽编号。
例如,对于一个 key "hello",可以按照以下步骤计算其应该被映射到的哈希槽编号:
-
计算 "hello" 的 CRC16 校验和值为 2229。
-
将 2229 对 16384 取余数,得到的结果为 1093,所以 key "hello" 应该被映射到哈希槽编号为 1093。
23.Redis Cluster 支持重新分配哈希槽吗?
是的,Redis Cluster 支持重新分配哈希槽,也称为“resharding”。在 Redis Cluster 运行过程中,可能会遇到新增节点、节点故障、节点扩容等情况,这些变化可能会导致哈希槽的分布不再均匀,进而影响集群的性能和可用性。
为了解决这个问题,Redis Cluster 提供了一种叫做“resharding”的机制,可以重新分配哈希槽,使其均匀地分布在新的节点上。具体来说,resharding 的过程如下:
-
添加新节点:当新节点加入集群时,它的哈希槽数量会被平均分配给所有节点。
-
节点故障:当某个节点故障时,它的哈希槽会被重新分配给其他节点。
-
节点扩容:当某个节点需要扩容时,它可以请求集群管理员为其分配一部分哈希槽,管理员将这些哈希槽分配给该节点。
在这些操作中,Redis Cluster 会使用一些算法来保证哈希槽的均匀分布,例如,在添加新节点或故障转移时,集群会尝试将不同节点的哈希槽数量调整到差不多相同的水平。
需要注意的是,resharding 操作可能会对集群的性能和可用性产生影响,因此应该在必要时进行,避免过于频繁地进行哈希槽的重新分配。
24.Redis Cluster 扩容缩容期间可以提供服务吗?
在 Redis Cluster 扩容或缩容期间,集群仍然可以提供服务,但可能会对集群的性能和可用性产生一定的影响。
当集群需要扩容时,新节点会被添加到集群中,并且哈希槽会重新分配。在这个过程中,集群的性能可能会受到一定的影响,因为节点之间需要进行大量的数据传输和同步。如果数据量较大,这个过程可能会持续相当长的时间,这期间集群的响应时间可能会变慢,而且可能会出现一些数据不一致的情况。
同样的,在集群需要缩容时,一些节点会被移除,哈希槽也会重新分配。这个过程可能会导致集群的性能和可用性下降,因为移除节点需要进行大量的数据迁移和同步,这个过程可能也会比较耗时。
为了最小化对集群的影响,可以采用一些措施来优化扩容或缩容过程,例如使用在线迁移或增量同步等技术来减少节点之间的数据传输量,或者在集群负载较轻的时候进行操作。此外,在进行扩容或缩容操作时,应该尽量避免同时进行多个操作,以免影响集群的稳定性和性能。
25.Redis Cluster 中的节点是怎么进行通信的?
通过 Gossip 协议进行通信;
新加入节点: Gossip 协议向老节点,发出一个“Meet 消息”。老节点会回复“Pong 消息”。后续新节点会定期给老节点发送“ping”,老节点回复"pong",来确保联系保持
Meet 消息,用于通知新节点加入。就好像上面例子中提到的新节点上线会给老节点发送 Meet 消息,表示有“新成员”加入。
Ping 消息,这个消息使用得最为频繁,该消息中封装了自身节点和其他节点的状态数据,有规律地发给其他节点。
Pong 消息,在接受到 Meet 和 Ping 消息以后,也将自己的数据状态发给对方。同时也可以对集群中所有的节点发起广播,告知大家的自身状态。
Fail 消息,如果一个节点下线或者挂掉了,会向集群中广播这个消息。
通讯是为了保证每个节点都有其他节点的槽数据对应关系,每个节点都有ClusterState,它记录了所有槽和节点的对应关系。Redis 的客户端无论访问集群中的哪个节点都可以路由到对应的节点上
MOVED 重定向请求:请求到redis某个节点,发现slot不在该服务器,则返回moved命令,告诉redis客户端应该请求哪个节点重写向所在节点发送请求
ASK 重定向请求:Redis 客户端向“缓存节点 1”发出请求,此时“缓存节点 1”正向“缓存节点 2”迁移数据,如果没有命中对应的 Slot,它会返回客户端一个 ASK 重定向请求并且告诉“缓存节点 2”的地址。
客户端向“缓存节点 2”发送 Asking 命令,询问需要的数据是否在“缓存节点 2”上,“缓存节点 2”接到消息以后返回数据是否存在的结果。