Redis槽位slot解读
Redis中的槽位(slot)是Redis Cluster用来实现分布式功能的核心概念。Redis Cluster将数据分片存储在不同的节点上,每个节点存储一部分槽位,节点之间通过Gossip协议进行信息交换,以保证数据的一致性和可用性。
版本说明
本文写于 2022-03。
slot 分配与哈希算法(CRC16 % 16384)未变,经 2026-07 复核仍适用。
在Redis Cluster中,每个节点负责一部分槽位,槽位的数量固定为16384个,每个槽位对应一个整数编号,从0到16383。例如,节点A负责槽位0到5460,节点B负责槽位5461到10922,以此类推。当客户端发送请求时,Redis Cluster通过槽位号将请求路由到相应的节点。
当一个Redis Cluster节点加入到集群中时,它会接收到所有槽位的信息,并根据槽位的数量平均分配槽位。如果有节点离开集群,它的槽位会被重新分配到其他节点。这种动态分配槽位的方式使得Redis Cluster具有高可用性和可扩展性。
可以使用以下命令在Redis Cluster中查看槽位的分配情况:
cluster slots
2
该命令将返回每个节点负责的槽位范围,例如:
1) 1) (integer) 0
2) (integer) 5460
3) 1) "<PUBLIC_IP>"
2) (integer) 6379
3) "abcdefghijk123456"
2) 1) (integer) 5461
2) (integer) 10922
3) 1) "<PUBLIC_IP>"
2) (integer) 6380
3) "abcdefghijk123457"
2
3
4
5
6
7
8
9
10
11
这表示第一个节点负责槽位0到5460,第二个节点负责槽位5461到10922。
在使用Redis Cluster时,应该避免使用槽位0到16383中的部分槽位,以便后续扩容时能够有效利用这些槽位。此外,每个键被分配到槽位的算法可以通过修改Redis配置文件中的hash-tag选项进行自定义。默认情况下,该选项设置为{},表示使用键中的整个字符串作为哈希函数的输入。如果将该选项设置为{foo},则表示只使用键中包含{foo}的部分作为哈希函数的输入。这可以帮助开发人员在Redis Cluster中实现特定的数据分布策略。
除了上述基本的槽位信息之外,Redis Cluster还支持一些其他的槽位操作和命令:
节点手动迁移槽位
可以使用以下命令将槽位从一个节点迁移到另一个节点:
CLUSTER SETSLOT <slot> IMPORTING <node-id>
CLUSTER SETSLOT <slot> MIGRATING <node-id>
2
3
4
5
其中,表示要迁移的槽位编号,
获取槽位的分配情况
可以使用以下命令获取每个槽位的分配情况:
CLUSTER NODES
2
3
该命令将返回所有节点的信息,其中包括节点负责的槽位信息。
- 手动设置槽位的分配情况
可以使用以下命令手动设置节点负责的槽位信息:
CLUSTER ADDSLOTS <slot> [slot ...]
CLUSTER DELSLOTS <slot> [slot ...]
2
3
其中,ADDSLOTS命令将指定的槽位分配给当前节点,DELSLOTS命令将指定的槽位从当前节点中移除。
- 槽位迁移的状态
可以使用以下命令查看当前槽位迁移的状态:
CLUSTER GETKEYSINSLOT <slot> <count>
2
该命令将返回当前正在迁移的槽位中的键列表和当前迁移的进度。
总的来说,槽位是Redis Cluster实现分布式存储的核心概念,掌握槽位的分配和操作方法可以帮助开发人员更好地使用Redis Cluster来实现高可用和高性能的分布式存储方案。
在 Redis 集群中,槽位是将数据分配到不同节点的一种方式。默认情况下,Redis 集群将所有槽位均匀地分配给集群中的节点。如果您想要预留部分槽位,可以通过以下步骤来实现:
- 确定要预留的槽位范围。例如,如果您想要预留前 100 个槽位,则槽位范围是 0-99。
- 在 Redis 集群中选择一个节点,该节点将用于托管预留的槽位。
- 在所选节点上使用 CLUSTER ADDSLOTS 命令将预留的槽位分配给节点。例如,如果您要将前 100 个槽位分配给节点,则可以使用以下命令:
redis-cli -p <port> cluster addslots {0..99}
2
其中
确认节点已成功分配了预留的槽位。您可以使用 CLUSTER NODES 命令查看集群节点的信息,并确保节点已托管预留的槽位。
将其他节点从集群中移除,并将它们重新加入集群,以便重新分配剩余的槽位。
注意,预留槽位可能会导致集群中的负载不平衡。因此,您应该仔细考虑是否需要预留槽位,并根据实际情况进行调整。
# 为什么是 16384 个槽位
Redis Cluster 选择 16384(2^14) 个槽位,是一个工程上的折中:
为什么不更多:
- 心跳包大小:Cluster 节点通过 gossip 协议交换状态,心跳包中包含节点负责的槽位信息,用 16384 位的 bitmap 表示(2048 字节)。如果槽位增加到 65536(2^16),心跳包会变成 8KB,增加网络开销。
- 迁移粒度:槽位越多,单次迁移的数据越少,迁移控制的 overhead 越高。
为什么不更少:
- 负载均衡粒度:槽位太少(如 1024 个),在节点数多时每个节点负责的槽位范围太大,难以精细分配负载。
16384 的选取:
- 是 2^14,可以用 14 位表示,bitmap 压缩效率高
- 对 1000 个节点规模的集群,每个节点平均约 16 个槽位,足以做细粒度负载均衡
- 心跳包 2KB,在大部分网络环境下可接受
官方说明:16384 是 "足够大以支持大规模集群,足够小以保持心跳包紧凑" 的折中。
# 如何确认槽位分布是否均衡
# 1. 查看槽位分布概览
redis-cli -h <any-node> -p 6379 cluster nodes
关注最后一列(槽位范围),理想情况是各主节点的槽位数量大致相等。
# 2. 量化不均衡度
# 统计每个主节点的槽位数量
redis-cli -h <any-node> -p 6379 cluster nodes | grep master | while read line; do
node=$(echo $line | awk '{print $2}' | cut -d'@' -f1)
slots=$(echo $line | grep -oE '\[?[0-9]+-[0-9]+\]?' | tr ',' '\n' | awk -F'-' '{sum+=$2-$1+1} END {print sum}')
echo "$node: $slots slots"
done
2
3
4
5
6
各主节点的槽位数量差异不应超过平均值 ±10%。
# 3. 查看 key 分布
# 各节点 key 数量(近似值)
redis-cli --cluster info <any-node>:6379
2
注意:key 数与槽位数可能不成比例(某些槽位数据量大),需要结合 INFO memory 分析。
# 坑与边界
槽位分布不均的检测:即使槽位数量均衡,实际数据分布可能不均(热点 key 集中在某槽位)。应监控各节点的
used_memory而非只看槽位数量。ASK 重定向:槽位迁移期间,客户端访问正在迁移的槽位会收到
ASK重定向(而非MOVED)。ASK是临时性的,只针对本次请求;MOVED则是永久性的,客户端应更新槽位映射缓存。hash tag 的陷阱:用
{}保证相关 key 落在同一槽位时,如果 tag 设计不当(如所有 key 都用{user:123}),会导致严重的热点。建议 tag 要有足够的离散度,如{user_id}而非固定的{user}。跨槽位操作的限制:
MGET、MSET、SUNION等多 key 操作要求所有 key 在同一槽位。如果业务需要这些操作,必须用 hash tag 强制把它们钉在同一槽位——但这也引入了热点风险。CLUSTER ADDSLOTS 的误用:手动
ADDSLOTS可以创建重叠的槽位分配,导致集群进入异常状态。除非深度理解 Cluster 协议,否则应使用redis-cli --cluster reshard进行槽位管理。节点重启后的槽位恢复:节点重启后,它从
nodes.conf文件恢复槽位信息。如果该文件被删除或损坏,节点会以空白身份加入,造成槽位「丢失」。备份nodes.conf是运维基线。