键操作与Moved错误

在对数据库中的16384个槽都进行了指派之后,集群就会进入上线状态,这时客户端就可以向集群中的节点发送数据命令了。

实现原理

当客户端向节点发送与数据库键有关的命令时,接收命令的节点会计算出命令要处理的数据库键属于哪个槽,并检查这个槽是否指派给了自己:

  • 如果键所在的槽正好就指派给了当前节点,那么节点直接执行这个命令。

  • 如果键所在的槽并没有指派给当前节点,那么节点会向客户端返回一个MOVED错误,指引客户端转向(redirect)至正确的节点,并再次发送之前想要执行的命令。

集群模式中每个节点的数据库

  • 集群节点保存键值对以及键值对过期时间的方式,与单机Redis服务器保存键值对以及键值对过期时间的方式完全相同。
  • 节点和单机服务器在数据库方面的一个区别是,节点只能使用0号数据库,而单机Redis服务器则没有这一限制。
执行步骤

计算键属于哪个槽

  • 节点使用以下算法来计算给定键key属于哪个槽:

    def slot_number(key):return CRC16(key) & 16383
    

    其中CRC16(key)语句用于计算键key的CRC-16校验和,而&16383语句则用于计算出一个介于0至16383之间的整数作为键key的槽号。

  • 使用CLUSTER KEYSLOT <key>命令可以查看一个给定键属于哪个槽

判断槽是否由当前节点负责处理

  • 当节点计算出键所属的槽i之后,节点就会检查自己在clusterState.slots数组中的项i,判断键所在的槽是否由自己负责:

    • 如果clusterState.slots[i]等于clusterState.myself,那么说明槽i由当前节点负责,节点可以执行客户端发送的命令。
    • 如果clusterState.slots[i]不等于clusterState.myself,那么说明槽i并非由当前节点负责,节点会根据clusterState.slots[i]指向的clusterNode结构所记录的节点IP和端口号,向客户端返回MOVED错误,指引客户端转向至正在处理槽i的节点。

返回MOVED错误重定向

  • 当节点发现键所在的槽并非由自己负责处理的时候,节点就会向客户端返回一个MOVED错误,指引客户端转向至正在负责槽的节点。

  • MOVED错误的格式为:

    MOVED <slot> <ip>:<port>
    
    • 其中slot为键所在的槽,而ip和port则是负责处理槽slot的节点的IP地址和端口号
  • 被隐藏的MOVED错误:

    • 集群模式的redis-cli客户端在接收到MOVED错误时,并不会打印出MOVED错误,而是根据MOVED错误自动进行节点转向,并打印出转向信息,所以我们是看不见节点返回的MOVED错误的

    • 但是,如果我们使用单机(stand alone)模式的redis-cli客户端,再次向节点7000发送相同的命令,那么MOVED错误就会被客户端打印出来,因为单机模式的redis-cli客户端不清楚MOVED错误的作用,所以它只会直接将MOVED错误直接打印出来,而不会进行自动转向。

    $ redis-cli -c -p 7000 # 集群模式
    127.0.0.1:7000> SET msg "happy new year!"
    -> Redirected to slot [6257] located at 127.0.0.1:7001
    OK
    127.0.0.1:7001>===========================================$ redis-cli -p 7000 # 单机模式
    127.0.0.1:7000> SET msg "happy new year!"
    (error) MOVED 6257 127.0.0.1:7001
    127.0.0.1:7000>
    
  • 一个集群客户端通常会与集群中的多个节点创建套接字(Socket)连接,而所谓的节点转向实际上就是换一个套接字(Socket)来发送命令。

    • 如果客户端尚未与想要转向的节点创建套接字(Socket)连接,那么客户端会先根据MOVED错误提供的IP地址和端口号来连接节点,然后再进行转向。

重分片与ASK错误

Redis集群的重新分片操作(比如新增节点)可以将任意数量已经指派给某个节点(源节点)的槽改为指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点。

  • 重新分片操作可以在线(online)进行,在重新分片的过程中,集群不需要下线,并且源节点和目标节点都可以继续处理命令请求。
实现原理
  • Redis集群的重新分片操作是由Redis的集群管理软件redis-trib负责执行的,Redis提供了进行重新分片所需的所有命令,而redis-trib则通过向源节点和目标节点发送命令来进行重新分片操作。

redis-trib对集群的单个槽slot进行重新分片的步骤如下:

  • redis-trib对目标节点发送 CLUSTER SETSLOT <slot> IMPORTING <source_id> 命令,让目标节点准备好从源节点导入(import)属于槽slot的键值对。

  • redis-trib对源节点发送 CLUSTER SETSLOT <slot> MIGRATING <target_id> 命令,让源节点准备好将属于槽 slot 的键值对迁移(migrate)至目标节点。

  • redis-trib向源节点发送 CLUSTER GETKEYSINSLOT <slot> <count> 命令,获得最多 count 个属于槽 slot 的键值对的键名(key name)。

  • 对于步骤3获得的每个键名,redis-trib 都向源节点发送一个 MIGRATE <target_ip> <target_port> <key_name> 0 <timeout> 命令(用来标志该节点的这个 key 已经被迁移到了 target 节点),将被选中的键原子地从源节点迁移至目标节点。

  • 重复执行步骤3和步骤4,直到源节点保存的所有属于槽 slot 的键值对都被迁移至目标节点为止。

  • redis-trib 向集群中的任意一个节点发送 CLUSTER SETSLOT <slot> NODE <target_id> 命令,将槽slot指派给目标节点,这一指派信息会通过消息发送至整个集群,最终集群中的所有节点都会知道槽slot已经指派给了目标节点。

ASK错误

在进行重新分片期间,源节点向目标节点迁移一个槽的过程中,可能会出现这样一种情况:属于被迁移槽的一部分键值对保存在源节点里面,而另一部分键值对则保存在目标节点里面。

当客户端向源节点发送一个与数据库键有关的命令,并且命令要处理的数据库键恰好就属于正在被迁移的槽时:

  • 源节点会先在自己的数据库里面查找指定的键,如果找到的话,就直接执行客户端发送的命令。
  • 相反地,如果源节点没能在自己的数据库里面找到指定的键,那么这个键有可能已经被迁移到了目标节点,源节点将向客户端返回一个ASK错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令。

被隐藏的ASK错误

  • 和接到MOVED错误时的情况类似,集群模式的redis-cli在接到ASK错误时也不会打印错误,而是自动根据错误提供的IP地址和端口进行转向动作。如果想看到节点发送的ASK错误的话,可以使用单机模式的redis-cli客户端:

    $ redis-cli -p 7002
    127.0.0.1:7002> GET "love"
    (error) ASK 16198 127.0.0.1:7003
    

ASK错误和MOVED错误的区别

  • ASK错误和MOVED错误都会导致客户端转向

    • 原理和 dict 的 rehash 很像,都是先到路由到自己的索引表,查看是否包含要查找的 key,不包含的话就路由去新的位置。
  • 区别

    • MOVED错误代表槽的负责权已经从一个节点转移到了另一个节点:在客户端收到关于槽i的MOVED错误之后,客户端每次遇到关于槽i的命令请求时,都可以直接将命令请求发送至MOVED错误所指向的节点,因为该节点就是目前负责槽i的节点。
    • 与此相反,ASK错误只是两个节点在迁移槽的过程中使用的一种临时措施:
      • 在客户端收到关于槽i的ASK错误之后,客户端只会在接下来的一次命令请求中将关于槽i的命令请求发送至ASK错误所指示的节点,因为可能该槽位还有没迁移完的元素,所以只有全部迁移完之后才会把这个槽位标记成 target 节点的。
      • 所以这种转向不会对客户端今后发送关于槽i的命令请求产生任何影响,客户端仍然会将关于槽i的命令请求发送至目前负责处理槽i的节点,除非ASK错误再次出现。
执行步骤

CLUSTER SETSLOT IMPORTING 实现

  • clusterState结构的 importing_slots_from 数组记录了当前节点正在从其他节点导入的槽:

    typedef struct clusterState {// ...clusterNode *importing_slots_from[16384];// ...
    } clusterState;
    

    如果importing_slots_from[i]的值不为NULL,而是指向一个clusterNode结构,那么表示当前节点正在从clusterNode所代表的节点导入槽i。

  • 在对集群进行重新分片的时候,向目标节点发送命令:

    # 将目标节点 clusterState.importing_slots_from[i] 的值设置为 source_id 所代表节点的clusterNode结构。
    CLUSTER SETSLOT <i> IMPORTING <source_id>
    
  例如:7003 客户端向节点发送以下命令```shell# 9dfb... 是节点7002 的ID 127.0.0.1:7003> CLUSTER SETSLOT 16198 IMPORTING 9dfb4c4e016e627d9769e4c9bb0d4fa208e65c26OK

CLUSTER SETSLOT MIGRATING 实现

  • clusterState结构的 migrating_slots_to 数组记录了当前节点正在迁移至其他节点的槽:

    typedef struct clusterState {// ...clusterNode *migrating_slots_to[16384];// ...
    } clusterState;
    

    如果migrating_slots_to[i]的值不为NULL,而是指向一个clusterNode结构,那么表示当前节点正在将槽i迁移至clusterNode所代表的节点。

  • 在对集群进行重新分片的时候,向源节点发送命令:

    # 将源节点clusterState.migrating_slots_to[i]的值设置为target_id所代表节点的clusterNode结构。
    CLUSTER SETSLOT <i> MIGRATING <target_id>
    

    例如:7002客户端向节点发送以下命令

    # 0457... 是节点7003 的ID
    127.0.0.1:7002> CLUSTER SETSLOT 16198 MIGRATING 04579925484ce537d3410d7ce97bd2e260c459a2
    OK
    

ASK 错误流程

  • 如果节点收到一个关于键key的命令请求,并且键key所属的槽i正好就指派给了这个节点,那么节点会尝试在自己的数据库里查找键key,如果找到了的话,节点就直接执行客户端发送的命令。

    与此相反,如果节点没有在自己的数据库里找到键key,那么节点会检查自己的clusterState.migrating_slots_to[i],看键key所属的槽i是否正在进行迁移,如果槽i的确在进行迁移的话,那么节点会向客户端发送一个ASK错误,引导客户端到正在导入槽i的节点去查找键key。

  • 接到ASK错误的客户端会根据错误提供的IP地址和端口号,转向至正在导入槽的目标节点,然后首先向目标节点发送一个ASKING命令,之后再重新发送原本想要执行的命令。

  • ASKING 命令

    ASKING命令唯一要做的就是打开发送该命令的客户端的REDIS_ASKING标识,以下是该命令的伪代码实现:

    def ASKING():# 打开标识client.flags |= REDIS_ASKING# 向客户端返回OK 回复reply("OK")
    
    • 在一般情况下,如果客户端向节点发送一个关于槽i的命令,而槽i又没有指派给这个节点的话,那么节点将向客户端返回一个MOVED错误;但是,如果节点的clusterState.importing_slots_from[i]显示节点正在导入槽i,并且发送命令的客户端带有REDIS_ASKING标识,那么节点将破例执行这个关于槽i的命令一次

    • 另外要注意的是,客户端的REDIS_ASKING标识是一个一次性标识,当节点执行了一个带有REDIS_ASKING标识的客户端发送的命令之后,客户端的REDIS_ASKING标识就会被移除。
    127.0.0.1:7003> GET "love"  # 单机模式,love 所在的槽位正在被重分配
    (error) MOVED 16198 127.0.0.1:7002#========================================================127.0.0.1:7003> ASKING       # 打开 REDIS_ASKING 标识
    OK
    127.0.0.1:7003> GET "love"   # 访问一次后自动移除 REDIS_ASKING 标识
    "you get the key 'love'"
    127.0.0.1:7003> GET "love"   # 此时 REDIS_ASKING 标识未打开,执行失败
    (error) MOVED 16198 127.0.0.1:7002
    

【Redis实现系列】集群MOVED错误与ASK错误相关推荐

  1. Redis主从复制和集群配置系列之四

    非常感谢  http://blog.csdn.net/dc_726/article/details/48552531 Redis技术学习 https://www.itkc8.com 全面剖析Redis ...

  2. Redis系列---集群模式

    目录 1. 数据分片 1.1. 哈希算法 1.1.1. 优点 1.1.2. 缺点 1.2. 一致性哈希算法 1.2.1. 优点 1.2.2. 缺点 1.3. 范围算法 1.3.1. 优点 1.3.2. ...

  3. 2W 字详解 Redis 6.0 集群环境搭建实践

    原文链接:https://www.cnblogs.com/hueyxu/p/13884800.html 本文是Redis集群学习的实践总结(基于Redis 6.0+),详细介绍逐步搭建Redis集群环 ...

  4. Redis高可用集群-哨兵模式(Redis-Sentinel)搭建配置教程【Windows环境】

    ================================================= 人工智能教程.零基础!通俗易懂!风趣幽默!大家可以看看是否对自己有帮助! 点击查看高清无码教程 == ...

  5. redis 主从 哨兵 集群 及原理

    1.主从哨兵 1.主从哨兵架构图: 此图为最常见的一主两从结构,一个master主机,两个slave主机.每台主机上都运行着两个进程: redis-server 服务,处理redis正常的数据操作与响 ...

  6. redis 04:Redis高可用集群

    文章目录 一.Redis集群方案比较 二.Redis高可用集群搭建 三.Java操作redis集群 四.Redis集群原理分析 五.集群伸缩 5.1 集群扩展 5.1 缩容集群 六.总结 以下参考了图 ...

  7. CentOS7下安装Redis伪集群(基于Redis官方Cluster集群模式版本redis-5.0.10)

    文章目录 Redis简介 什么是redis redis的优点 Redis集群都有哪些模式 主从复制(Master-Slave Replication) 哨兵模式(Sentinel) Redis官方 C ...

  8. redis 持久化 + 主从复制+ 集群

    2019独角兽企业重金招聘Python工程师标准>>> 一. Linux 下的 Redis 安装 && 启动 && 关闭 && 卸载 ...

  9. Redis 3种集群方式,别傻傻分不清!

    文章目录 Redis 3种集群方式,别傻傻分不清! 1 redis 主从模式配置 前言 redis 主从模式配置 首先更改配置文件 (redis.windows.conf ) 配置主从配置 (redi ...

  10. Redis 多服务器集群搭建

    Redis 多服务器集群搭建 近期,想到之前使用的Redis集群测试使用的是单服务器上的伪集群,重温<Redis深度历险-核心原理与应用实践>的案例,觉得还是搭建一下多服务器集群来玩一玩会 ...

最新文章

  1. oracle中快速复制数据表(创建数据表)
  2. 光流 | 基于KLT(Kanade-Lucas-Tomasi)特征点跟踪算法(附代码,可扩展)
  3. weex web获取html高度,weex 中的 web 标签如何控制高度?
  4. map集合的putall_Map.put和Map.putAll方法之间的区别?
  5. CentOS6.9安装Kafka
  6. php 调用日历控制,基于ThinkPHP实现的日历功能实例详解
  7. ffplay.exe操作方式
  8. JVM GC一篇通 - 基础与调优
  9. 租房系统代码java_基于Java的租房管理系统的设计及实现.doc
  10. win10开始屏幕计算机,为什么Win10系统开始屏幕没反应?解决Win10系统开始屏幕没反应的方法...
  11. notepad++下的字体设置
  12. VS2010 编译 openssl 源代码(输出 libeay32 and ssleay32 静态库和动态库)
  13. 三维地理信息系统空间的可视化分析
  14. Linux 常用软件
  15. Thinkpad E430c使用u盘安装系统
  16. java调用阿里云短信服务器-发送短信
  17. 机器学习系列(五) -- 逻辑回归(莺尾花数据集)
  18. 面试总结+感悟+分享
  19. 哪款蓝牙耳机音质好又耐用?便宜耐用的蓝牙耳机
  20. 【前沿技术】浅析搜狗AI主播背后的核心技术

热门文章

  1. 微信充值页面开发总结
  2. vmagent入门(一)
  3. WordPress主题/两栏响应式博客主题:翠竹林CUI v2.06
  4. 陕西大学最早的计算机专业,2019计算机考研:陕西地区计算机专业优质院校
  5. 数据载入、存储及文件格式——《利用python数据分析》第六章学习
  6. 合肥 python 培训
  7. process_vm_readv/writev进程间数据传输
  8. Android开源项目汇总20150712更新
  9. 一个蚂蚁曾经的辛酸面试历程
  10. 友情链接查询工具 php源码,php 友情链接批量查询工具下载