Redis分片集群散列插槽

一、前言:为什么 Redis Cluster 用 16384 个槽?

在 Redis 分片集群(Cluster)中,数据不是随机分布,而是通过"散列插槽"(Hash Slot)机制进行分片

你可能好奇:

  • 为什么是 16384 个槽,不是 65536 或 1024?
  • key 是如何映射到具体节点的?
  • 为什么 MGET user:1 user:2 有时会报错?

本文将带你彻底搞懂 Redis 散列插槽的设计哲学与工作原理


二、散列插槽:Redis Cluster 的分片基石

2.1 基本概念

  • Redis Cluster 将整个 key 空间划分为 16384 个哈希槽(slot)

  • 每个 key 通过公式计算归属哪个 slot:

    复制代码
    slot = CRC16(key) % 16384
  • 每个 master 节点负责一部分连续或不连续的 slot

优势

  • 分片规则简单、确定
  • 扩容/缩容只需迁移 slot,无需 rehash 全量数据

三、为什么是 16384?不是更多或更少?

这是 Redis 作者 Salvatore Sanfilippo(antirez) 在 GitHub issue 中亲自解释的经典问题。

3.1 官方理由(精简版)

"16384 is the right balance between:

  • 消息大小:每个节点每秒通过 gossip 协议广播 cluster state
  • 集群规模:最多支持 1000 个节点(实际建议 ≤ 100)
  • 内存开销:每个 slot 状态需 2 字节,16384 × 2 = 32KB"

3.2 详细分析

槽位数 每节点心跳包大小 支持最大节点数 内存开销
65536 ~130KB < 100 128KB
16384 ~32KB ~1000 32KB
4096 ~8KB > 1000 8KB
  • 65536 太大:心跳包过大,网络带宽浪费
  • 4096 太小:节点多时,slot 分配不均(如 100 节点,平均仅 40 个 slot/节点)
  • 16384 刚好:兼顾扩展性与效率

🔑 结论 :16384 是工程上的最优折中


四、Key 到 Slot 的计算过程(含代码)

4.1 CRC16 算法

Redis 使用 CRC16_CCITT 变种(初始值 0,无反转)。

4.2 Java 实现示例

java 复制代码
public class RedisSlot {
    private static final int SLOT_COUNT = 16384;

    public static int getSlot(String key) {
        // 1. 提取 Hash Tag(如有)
        String tagKey = extractTag(key);
        // 2. 计算 CRC16
        int crc = crc16(tagKey.getBytes(StandardCharsets.UTF_8));
        // 3. 取模
        return crc % SLOT_COUNT;
    }

    // 支持 Hash Tag:{user1001}.name 和 {user1001}.age 落在同一 slot
    private static String extractTag(String key) {
        int start = key.indexOf('{');
        if (start != -1) {
            int end = key.indexOf('}', start + 1);
            if (end != -1 && end != start + 1) {
                return key.substring(start + 1, end);
            }
        }
        return key;
    }

    // 简化版 CRC16(实际应使用标准实现)
    private static int crc16(byte[] bytes) {
        int crc = 0;
        for (byte b : bytes) {
            crc = ((crc << 8) ^ LOOKUP_TABLE[((crc >> 8) ^ (b & 0xFF)) & 0xFF]) & 0xFFFF;
        }
        return crc;
    }

    private static final int[] LOOKUP_TABLE = { /* CRC16 表 */ };
}

关键点Hash Tag 可强制多个 key 落在同一 slot!


五、客户端如何定位 Key 所在节点?

当客户端访问一个 key 时,流程如下:

5.1 首次访问(无本地缓存)

  1. 随机连接一个节点(如 7001)

  2. 节点计算 key 的 slot,发现不在自己负责范围

  3. 返回 MOVED 重定向

    复制代码
    MOVED 5461 192.168.1.10:7002
  4. 客户端缓存 <slot -> node> 映射,并重连 7002

5.2 后续访问(有缓存)

  • 直接根据 slot 路由到目标节点,无重定向

💡 Lettuce / Jedis Cluster 客户端会自动处理 MOVED/ASK


六、跨 Slot 操作为何失败?

6.1 问题场景

bash 复制代码
# 假设 user:1 → slot 1000, user:2 → slot 2000
redis-cli -c MGET user:1 user:2
# 报错:CROSSSLOT Keys in request don't hash to the same slot

6.2 原因

  • Redis Cluster 要求多 key 操作必须在同一 slot
  • 否则无法保证原子性和一致性

6.3 解决方案:使用 Hash Tag

bash 复制代码
# 强制 user:1 和 user:2 落在同一 slot
MGET {user100}.name {user100}.age  # OK!
  • {} 内的内容作为 hash key
  • user100 相同 → slot 相同

最佳实践:对需要 multi-key 操作的数据,使用相同 Hash Tag


七、Slot 迁移与集群扩容

当新增 master 节点时,需迁移部分 slot

7.1 迁移流程

  1. 目标节点声明"我将接管 slot X"
  2. 源节点进入 MIGRATING 状态
  3. 客户端访问 slot X 的 key:
    • 若 key 存在 → 返回 ASK 重定向 到目标节点
    • 若 key 不存在 → 允许在目标节点写入
  4. 源节点逐步迁移 key(CLUSTER GETKEYSINSLOT + MIGRATE
  5. 迁移完成,更新集群拓扑

7.2 客户端感知

  • 收到 ASK 时,先发 ASKING 命令,再发原命令
  • Lettuce/Jedis 自动处理,业务无感

八、监控与运维命令

8.1 查看 slot 分配

bash 复制代码
redis-cli -p 7001 CLUSTER SLOTS
# 返回:[[0,5460,"192.168.1.10",7001], [5461,10922,"192.168.1.10",7002], ...]

8.2 查看 key 所属 slot

bash 复制代码
redis-cli -p 7001 CLUSTER KEYSLOT "user:1001"
# 返回:5461

8.3 查看节点负责的 slots

bash 复制代码
redis-cli -p 7001 CLUSTER NODES
# 输出中:connected 0-5460

九、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
fzb5QsS1p2 小时前
MySQL 事务的二阶段提交是什么?
数据库·mysql
清风徐来QCQ5 小时前
Lombok/SSM/devTools
数据库
LaughingZhu5 小时前
Product Hunt 每日热榜 | 2026-04-05
前端·数据库·人工智能·经验分享·神经网络
2601_949814696 小时前
使用mysql报Communications link failure异常解决
数据库·mysql
搜佛说6 小时前
02-第2章-核心概念与架构
数据库·物联网·微服务·架构·边缘计算·iot
x***r1516 小时前
RedisStudio-en-0.1.5可视化管理工具安装步骤详解(附Redis可视化与Key管理教程)
redis
IGAn CTOU6 小时前
PHP使用Redis实战实录2:Redis扩展方法和PHP连接Redis的多种方案
开发语言·redis·php
C'ᴇsᴛ.小琳 ℡7 小时前
高性能NoSQL
数据库·nosql
i220818 Faiz Ul8 小时前
动漫商城|基于springboot + vue动漫商城系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·动漫商城系统
iNgs IMAC8 小时前
redis 使用
数据库·redis·缓存