详解redis集群数据读写定位具体节点的过程

Redis 集群中,数据读写时定位到具体节点的过程依赖于 哈希槽(Hash Slot) 机制和 客户端的路由逻辑。以下是详细的工作原理和流程:

一、核心机制

1. 哈希槽(Hash Slot)
  • Redis 集群将数据划分为 16384 个哈希槽(编号 0~16383)。
  • 每个键(Key)通过 CRC16 算法 计算哈希值,再对 16384 取模,得到对应的槽位:
    def get_slot(key):
        hash_value = crc16(key)  # CRC16 算法
        slot = hash_value % 16384
        return slot
    
  • 示例
    • user:1001 的有效部分为 user:1001(若未使用 {} 标签),计算后得到槽位 X
    • {user102}:last.name 的有效部分为 user102,计算后得到槽位 Y
2. 槽位分配
  • 每个主节点负责一部分槽位(例如 3 个主节点可能分别负责 05460、546110922、10923~16383)。
  • 槽位与节点的映射关系由 集群元数据 维护,节点间通过 Gossip 协议同步该信息。

二、客户端定位数据的流程

1. 客户端直连节点
  • 客户端直接连接集群中的任意节点(如 192.168.1.101:6379)。
  • 客户端首次请求时,会缓存 槽位→节点 的映射关系(称为 槽位表)。
2. 计算键对应的槽位
  • 客户端根据键名的有效部分,使用 CRC16 算法计算槽位:
    redis-cli -c -h 192.168.1.101 -p 6379
    127.0.0.1:6379> CLUSTER KEYSLOT "user:1001"
    (integer) 9189
    
3. 查询槽位表
  • 客户端从本地缓存的槽位表中查找该槽位对应的节点地址(如 192.168.1.102:6379)。
  • 如果未找到或缓存过期,客户端会向当前连接的节点发送请求,节点会返回 MOVED 错误,并附上正确的节点地址。
4. 处理重定向
  • MOVED 重定向
    • 表示槽位的归属节点已变更(如扩容或故障转移)。
    • 客户端更新本地槽位表,并重试请求到新的节点。
    • 示例:
      127.0.0.1:6379> GET user:1001
      (error) MOVED 9189 192.168.1.102:6379
      
  • ASK 重定向
    • 表示槽位正在迁移(如数据迁移中)。
    • 客户端临时向目标节点发送 ASKING 命令,再执行操作(不更新槽位表)。
    • 示例:
      127.0.0.1:6379> GET user:1001
      (error) ASK 9189 192.168.1.102:6379
      
5. 最终定位节点
  • 客户端将请求发送到正确的节点,节点处理数据读写操作。

三、优化策略

1. 哈希标签(Hash Tag)
  • 通过 {} 显式指定键的有效部分,确保相关键分配到同一槽位:
    String sessionKey = "user:{1001}:session";
    String profileKey = "user:{1001}:profile";
    
    • 两个键的有效部分均为 1001,因此分配到同一槽位,支持多键操作(如 MGET)。
2. 客户端缓存槽位表
  • 客户端(如 Jedis、Lettuce)会缓存槽位表,减少频繁查询带来的重定向开销。
  • 示例(Jedis 源码):
    public class JedisSlotCache {
        private volatile Map<Integer, JedisPool> slots = new HashMap<>();
        public Jedis getJedisByKey(String key) {
            int slot = JedisClusterCRC16.getSlot(key);
            return slots.get(slot).getResource();
        }
    }
    
3. 热点数据分片
  • 对高频访问的键添加随机后缀,分散压力:
    int shard = userId.hashCode() % 10;
    String shardedKey = "hot:user:" + userId + ":" + shard;
    

四、集群扩容与迁移

1. 槽位迁移
  • 新增节点时,通过 CLUSTER SLOTS 命令获取槽位分配,逐步将槽位从旧节点迁移至新节点。
  • 迁移期间,客户端可能收到 ASK 重定向,需临时向目标节点发送请求。
2. 客户端自动感知
  • 客户端通过 MOVED 错误更新槽位表,无需人工干预。
  • 大厂实践(如阿里云):
    • 使用 ZooKeeper 或 etcd 作为槽位配置中心,实现动态更新。
    • 对批量操作(如 MGET)进行智能分组,减少跨节点请求。

五、实际应用示例

1. 数据存储位置查询
  • 使用 CLUSTER SLOTS 查看槽位分布:
    127.0.0.1:6379> CLUSTER SLOTS
    1) 1) (integer) 0
       2) (integer) 5460
       3) 1) "192.168.1.101"
          2) (integer) 6379
    2) 1) (integer) 5461
       2) (integer) 10922
       3) 1) "192.168.1.102"
          2) (integer) 6379
    
2. Lua 脚本保证原子性
  • 对跨槽位的多键操作,使用 Lua 脚本强制绑定到同一节点:
    EVAL "redis.call('SET', KEYS[1], ARGV[1]); redis.call('EXPIRE', KEYS[1], ARGV[2])" 1 {user}:1001:lock true 30000
    

六、总结

Redis 集群通过 哈希槽客户端路由 实现数据定位:

  1. 客户端计算键的槽位,并查询本地槽位表定位节点。
  2. 若槽位表过期,通过 MOVEDASK 重定向更新映射。
  3. 使用哈希标签、缓存优化和 Lua 脚本提升性能与一致性。
  4. 扩容时通过槽位迁移实现平滑扩展,客户端自动感知变化。

这种方式既保证了高可用性,又避免了代理层的性能瓶颈,是分布式 Redis 的核心设计之一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值