数据分片

Q:redis 集群是怎么做数据分片的?

A:Redis集群是通过数据分片(sharding)来实现数据在不同节点间的分配。Redis集群中有16384个hash slot(槽位),每个节点可以处理其中的一部分槽位,不同节点之间可以共同处理这16384个槽位。当一个键值对需要被放到Redis集群中时,Redis会先计算该键值对的key对应的hash值,然后将其放到对应的槽位中。Redis集群的每个节点都知道自己负责处理哪些槽位,所以能够快速定位数据在哪个节点上。

具体来说,Redis集群使用哈希槽分片(hash slot sharding)来将所有数据分成16384个哈希槽。当客户端连接Redis集群时,会得到集群中任意一个节点的IP和端口信息,然后将客户端的请求路由到正确的节点上。每个节点都负责处理一部分哈希槽,这些哈希槽的范围通过分布式哈希算法(比如CRC16)来计算,然后分配到不同的节点上。当一个客户端请求需要操作某个key时,Redis会先根据key计算出该key对应的哈希槽,然后将请求路由到负责该哈希槽的节点上。这样就实现了数据的分片存储和访问。

Q:一个槽位,可以被多个 redis 主节点共同持有吗?

A:在 Redis 集群中,一个槽位只能被一个 Redis 节点持有,即每个槽位只能被分配给 Redis 集群中的一个主节点。在 Redis 集群中,槽位会被均匀地分配给各个主节点,确保每个主节点都分配了一定数量的槽位,从而实现数据的分布式存储。如果一个槽位被多个主节点持有,那么可能会导致数据冲突和数据一致性问题。因此,Redis 集群的设计保证了一个槽位只被一个主节点持有。

Q:一个 redis 主节点,怎么知道自己持有哪些槽位?持有的槽位是离散的,还是连续的?

A:在 Redis 集群中,一个 Redis 主节点会持有一部分槽位,具体是哪些槽位是由集群的节点管理器指定的。一个槽位只会被分配给一个 Redis 主节点,而不会被多个主节点共同持有。每个 Redis 主节点都会记录自己持有的槽位信息,可以通过命令 CLUSTER SLOTS 来获取当前节点所持有的槽位信息,例如:

127.0.0.1:6379> CLUSTER SLOTS
1) 1) (integer) 0
   2) (integer) 5460
   3) 1) "127.0.0.1"
      2) (integer) 6379
      3) "f2b5a96e2e79d08ce5d5a5b1d64b5a31f07a734f"
   4) 1) "127.0.0.1"
      2) (integer) 6380
      3) "92d24c0209384f4c4c9abef7adff0f129b73e7b6"
2) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 6380
      3) "92d24c0209384f4c4c9abef7adff0f129b73e7b6"
   4) 1) "127.0.0.1"
      2) (integer) 6379
      3) "f2b5a96e2e79d08ce5d5a5b1d64b5a31f07a734f"
...

上述命令返回了当前节点所持有的所有槽位信息。其中,每个槽位由一个起始槽号和一个结束槽号组成,例如 (integer) 0 到 (integer) 5460 表示第一个槽位的起始编号为 0,结束编号为 5460。对于连续的槽位,它们的起始和结束编号是连续的,而对于离散的槽位,它们的编号是不连续的。

Q:客户端怎么知道应该把数据写到 redis 集群的哪个节点?

A:在 Redis 集群中,客户端可以使用哈希槽位算法来确定应该将请求发送到哪个节点。哈希槽位算法将每个 key 映射到 16384 个槽位中的一个,并将每个槽位分配给集群中的一个节点。当客户端需要与集群通信时,它会将 key 映射到一个槽位,并向负责该槽位的节点发送请求。如果请求需要访问多个 key,则使用所有 key 的槽位来确定应该将请求发送到哪个节点。

在 Redis 集群中,每个节点都知道哪些槽位被分配给它。当一个节点收到一个请求,如果它不是负责该槽位的节点,则它会返回一个 MOVED 错误,告诉客户端应该将请求发送到负责该槽位的节点。

因此,客户端通过哈希槽位算法将 key 映射到节点,并使用 MOVED 错误来确定应该将请求发送到哪个节点。

Q:redis 集群的客户端,怎么知道哪个 redis server 节点负责哪个槽位?

A:Redis集群的客户端通过集群中的元数据(metadata)来获取槽和节点之间的映射关系,这个元数据保存在所有的Redis节点上。当一个客户端需要知道某个槽位的信息时,它会向Redis集群的其中一个节点发送CLUSTER SLOTS命令,该节点会返回一个包含当前集群中节点和槽位信息的列表,客户端通过解析这个列表就可以了解槽位和节点之间的映射关系。客户端在获取了这个映射关系后,就可以将请求发送到正确的节点上。

Q:redis 集群的元数据一般会很大吗?会占用很多网络通讯资源吗?

A:Redis 集群的元数据一般不会很大,因为它只包含了关于槽位与节点的映射信息。在默认的情况下,Redis 集群有 16384 个槽位,因此元数据的大小与节点数目和槽位的数量成正比。当集群中有成百上千个节点时,元数据会变得比较大,但是它通常不会成为瓶颈。

关于网络通讯资源的占用,元数据的大小相对较小,因此它对网络通讯资源的占用也较小。在 Redis 集群中,每个节点都需要与其他节点交换元数据信息以保持整个集群的状态一致性,这可能会占用一定的网络带宽。但是,Redis 采用了一些优化措施,如增量同步和只同步变化的信息等,以降低网络通讯资源的占用。

Q:redis 集群的客户端,怎么知道哪个 redis server 节点上线了或者下线了?怎么更新元信息?

A:Redis集群的客户端可以通过监听cluster:slots、cluster:meet、cluster:forget等事件来感知Redis节点的上线、下线、添加、删除等操作,并相应地更新集群元数据。

具体来说,客户端可以使用Redis提供的SUBSCRIBE命令来订阅cluster:slots、cluster:meet、cluster:forget等事件的通知,当Redis节点的状态发生变化时,Redis会发布相应的事件通知给所有客户端。客户端接收到通知后,可以更新自己本地的集群元数据,以确保后续的操作可以正确地路由到相应的节点。

例如,Python客户端redis-py-cluster提供了RedisCluster类来操作Redis集群,可以使用该类的pubsub属性来订阅相应的事件通知,如下所示:

from rediscluster import RedisCluster

startup_nodes = [{"host": "127.0.0.1", "port": "7000"}]
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)

# 订阅cluster:slots事件
pubsub = rc.pubsub(ignore_subscribe_messages=True)
pubsub.subscribe("__keyspace@0__:*")

# 监听事件通知
for item in pubsub.listen():
    print(item)

上面的代码订阅了__keyspace@0__:*频道,即监听所有key空间的事件通知。当有节点上线、下线、添加、删除等操作时,Redis会发布相应的事件通知到该频道,客户端通过监听该频道,就可以更新自己本地的集群元数据。

Q:对于一个 redis 集群,如果有一个 redis 主节点宕机下线了,这个时候,客户端往这个节点写入数据怎么办?

A:如果一个 Redis 主节点宕机下线了,客户端往这个节点写入数据的请求会失败。为了避免这种情况,Redis 集群提供了自动故障迁移(automatic failover)机制,使得当主节点宕机下线后,集群能够自动将从节点升级为新的主节点,以保证集群的高可用性。

当主节点宕机下线后,集群会检测到该节点下线,然后从其它从节点中选出一个新的主节点。选举新主节点的过程是通过集群内的投票机制来实现的。首先,集群会在所有从节点中选举一个新主节点,然后将新主节点的信息广播给集群中的所有节点。当客户端向集群发送写入请求时,集群会自动将请求路由到新的主节点上,从而保证集群的正常运行。

Q:Redis 集群提供的"自动故障迁移"需要几秒钟?

A:Redis 集群提供的自动故障转移(Automatic Failover)可以在几秒钟内完成,具体时间取决于集群的配置和网络延迟情况。在默认情况下,Redis 集群会在 15 秒内尝试执行故障转移。这个时间可以通过 cluster-node-timeout 参数进行配置。如果某个主节点超过了设定的 cluster-node-timeout 时间没有响应,那么这个节点将被认为是宕机了,Redis 集群就会开始自动故障转移过程。

在自动故障转移过程中,Redis 集群会选举一个新的主节点来接管原来宕机的节点的槽位,并将数据重新分布到集群中的其他节点。这个过程通常只需要几秒钟的时间。但是需要注意的是,在自动故障转移期间,集群中的某些操作可能会出现短暂的不可用性,直到转移过程完成。

Q:那这 15 秒内,客户端都无法往这些槽写入数据了,对吗?

A:是的,如果发生故障迁移,需要在迁移完成之前暂停向失效节点所持有的槽写入数据,否则可能会造成数据的不一致性。在 Redis 集群的故障迁移过程中,通常需要在数秒内完成。

数据副本

Q:redis 集群的副本是怎么分配的?

A:在 Redis 集群中,每个主节点会被分配若干个从节点,每个从节点都会在运行时通过复制主节点的数据来保持与主节点的数据一致。在默认情况下,每个主节点会被分配一个从节点,可以通过增加主节点的数量来增加集群的可用性和容错性。Redis 集群使用的是无中心架构,所以每个节点都会分配一个固定的槽位,对于任何给定的槽位,只会有一个主节点和一个或多个从节点负责处理。

当一个主节点下线时,其对应的从节点会被提升为新的主节点,从而确保集群的高可用性和可靠性。Redis 集群还支持对数据进行分片,从而将数据分布在多个主节点之间,提高并发处理能力和可扩展性。


universe_king
3.4k 声望680 粉丝