集群状态日志分析
Redis 分片集群引入了一个逻辑上的插槽或哈希槽概念,将集群划分为16384(0~16383)个槽位,集群中的每个节点占据一部分槽位数,在逻辑上将集群中的所有节点构成了一块完整的内存空间。
这个日志中可以通过查看集群状态就能看出来:
6d7a3b1f670fcc8ce3367f663f26658cd4364831 127.0.0.1:30005@40005 slave 7823d2206f0b7071371d9854a75075d2c737059f 0 1672121650000 3 connected
ad8bbafb88417b1658fd26c0c6825522eb977bec 127.0.0.1:30002@40002 master - 0 1672121650218 2 connected 5461-10922
415eac23481b5f2445c866feeecf31f88e0049f4 127.0.0.1:30006@40006 slave 8ef0a9e69c7a8a356e9b953419a7b0c101fb66e4 0 1672121650218 1 connected
8ef0a9e69c7a8a356e9b953419a7b0c101fb66e4 127.0.0.1:30001@40001 myself,master - 0 1672121649000 1 connected 0-5460
f015eacce4b33ebadbe33670534efd772cecc9c2 127.0.0.1:30004@40004 slave ad8bbafb88417b1658fd26c0c6825522eb977bec 0 1672121649913 2 connected
7823d2206f0b7071371d9854a75075d2c737059f 127.0.0.1:30003@40003 master - 0 1672121650519 3 connected 10923-16383
每行格式由以下字段组成:
<id> <ip:port> <flags> <master> <ping-sent> <pong-recv> <config-epoch> <link-state> <slot> <slot> ... <slot>
每个字段的含义如下:
- id:节点 ID,一个40个字符的随机字符串,当一个节点被创建时不会再发生变化(除非CLUSTER RESET HARD被使用)。
- ip:port:客户端应该联系节点以运行查询的节点地址。
- flags:逗号列表分隔的标志:myself,master,slave,fail?,fail,handshake,noaddr,noflags。标志在下边有详细解释。
- master:如果节点是从属节点,并且主节点已知,则节点ID为主节点,否则为“ - ”字符。
- ping-sent:以毫秒为单位的当前激活的ping发送的unix时间,如果没有挂起的ping,则为零。
- pong-recv:毫秒 unix 时间收到最后一个乒乓球。
- config-epoch:当前节点(或当前主节点,如果该节点是从节点)的配置时期(或版本)。每次发生故障切换时,都会创建一个新的,唯一的,单调递增的配置时期。如果多个节点声称服务于相同的哈希槽,则具有较高配置时期的节点将获胜。
- link-state:用于节点到节点集群总线的链路状态。我们使用此链接与节点进行通信。可以是connected或disconnected。
- slot:散列槽号或范围。从第9个参数开始,但总共可能有16384个条目(限制从未达到)。这是此节点提供的散列槽列表。如果条目仅仅是一个数字,则被解析为这样。如果它是一个范围,它是在形式 start-end,并且意味着节点负责所有散列时隙从 start 到 end 包括起始和结束值。
flags 标志的含义:
- myself:您正在联系的节点。
- master:节点是主人。
- slave:节点是从属的。
- fail?:节点处于PFAIL状态。对于正在联系的节点无法访问,但仍然可以在逻辑上访问(不处于FAIL状态)。
- fail:节点处于FAIL状态。对于将PFAIL状态提升为FAIL的多个节点而言,这是无法访问的。
- handshake:不受信任的节点,我们握手。
- noaddr:此节点没有已知的地址。
- noflags:根本没有标志。
散列哈希槽原理
当 key 写入的时候,集群会通过一定的算法,将要写入的数据路由到指定的插槽上。需要注意的是数据 key 不是与节点绑定,而是与插槽绑定。
Redis 会根据 key 的有效部分计算插槽值,分两种情况:
- key中不包含“{}”,整个key都是有效部分
- key中包含“{}”,且“{}”中至少包含1个字符,“{}”中的部分是有效部分
计算方式是利用 CRC16 算法得到一个 hash 值,然后对 16384 取余,得到的结果就是 slot 值。
举例来说:键是k1,那么就根据 k1 计算;如果是 {test}k1,则根据 “{test}” 计算。如图:
如上图,在 30001 节点执行命令时,对 k1 做 hash 运算,并对 16384 取余,得到的结果是 12706,因此要存储到端口为 30003 的节点上,操作其他命令也是同样以此类推。
如果想要将同一类数据保存在同一个实例上,这一类数据需要使用相同的有效部分。例如:键值 “{应用}” 为前缀,如上图中的 {test}k1 和 {test}k2。