Message Service
Redis
01.Redis
02.Installation From source code
03.redis的配置文件
04.Redis 命令行
05.数据结构和内部编码
06.Redis 客户端
07.慢查询
08.Pipeline
09.发布订阅 and 消息队列
10.Redis持久化策略
11.Redis主从同步
12.Redis Sentinel
13.redis cluster
14.FQA
15.Redis 数据迁移
16.supervisord 管理 redis
17.缓存管理
18.事务
19.CacheCloud
20.Redis Cluster在线扩容、缩容、迁移
22.redis 6.2.6 for arm
21.maxmemory-policy
redis-6.3.6
ActiveMQ
1.JMS
9.使用hawtio进行监控 ActiveMQ
8. Replicated Message Store
7.activeMQ支持的五种协议
6.监控 ActiveMQ
5.安全机制
4.消息持久化
3.Web管理ActiveMQeMQ
2.ActiveMQ安装配置
10.高可用环境
本文档使用 MrDoc 发布
-
+
home page
13.redis cluster
# 1.Redis集群介绍 早期Redis 分布式集群部署方案: - 客户端分区:由客户端程序决定key写分配和写入的redis node,但是需要客户端自己处理写入分配、高可用管理和故障转移等 - 代理方案:基于三方软件实现redis proxy,客户端先连接之代理层,由代理层实现key的写入分配,对客户端来说是有比较简单,但是对于集群管节点增减相对比较麻烦,而且代理本身也是单点和性能瓶颈。 - 在哨兵sentinel机制中,可以解决redis高可用问题,即当master故障后可以自动将slave提升为master,从而可以保证redis服务的正常使用,但是无法解决redis单机写入的瓶颈问题,即单机redis写入性能受限于单机的内存大小、并发数量、网卡速率等因素。 redis从3.0开始支持集群功能。redis集群采用无中心节点方式实现,无需proxy代理,客户端直接与redis集群的每个节点连接,根据同样的hash算法计算出key对应的slot,然后直接在slot对应的redis节点上执行命令。在redis看来,响应时间是最苛刻的条件,增加一层带来的开销是redis不能接受的。因此,redis实现了客户端对节点的直接访问,为了去中心化,节点之间通过gossip协议交换互相的状态,以及探测新加入的节点信息。redis集群支持动态加入节点,动态迁移slot,以及自动故障转移。 Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令。 Redis 集群的优势: -自动分割数据到不同的节点上。 - 整个集群的部分节点失败或者不可达的情况下能够继续处理命令。 # 2.数据分布  - 顺序分区的数据量不可确定性导致倾斜,不支持批量操作 - hash 算法 按照key的hash值对节点进行取模,然后得到的结果就是存放数据的节点; 缺点:如果一个节点挂了,新的数据按照新的节点数据取模导致无法取到数据(所有数据都取不到),于是就会被击穿到数据库,导致数据库压力增大。同时需要重新进行大量计算,把原有数据进行重新分配。 + 如果要增加分区,数据迁移量在80%左右 + 数据迁移第一次是无法从数据中取到的,数据库需要进行回写到新节点 + 客户端分片:哈希+取余 + 节点伸缩:数据节点关系变化,导致数据迁移 + 迁移数量和添加节点数量有关:建议翻倍扩容 - 一致性哈希分区 把节点分布到一个圆环中分布,key的hash值于当前节点hash值比较,然后确定在圆环上的位置,同时按照顺时针方向去寻找最近的一个节点。 按照上述理论,当一个节点失效的时候,就只有这个节点对应的数据无法取到,而其他节点数据依然还是可以取到。所以,可用性比hash算法要高。 缺点:针对热点数据问题,可能都会涌入到同一个节点中,而其他节点数据又少。 优化:在圆环中增加多个虚拟的同类节点,比如原来有1号 2 号 3号 这三个节点,现在虚拟出多个1号、2好、3号的节点分布在圆环上,这样就可以更细粒度的切割圆环数据,尽量减少了热点数据集中问题。 - hash slot 算法 上述都是物理节点的分布做算法,而redis cluster是通过在节点中分布虚拟的小节点来分布数据。redis cluster会在整个集群节点中分布 16384 个虚拟slot。每个节点分配一部分slot。然后所有的key的hash对 16384 取模,从而知道在哪个slot,也就知道了数据在哪个节点上。 当一个节点挂了时,就会把这个节点上的slot迁移到其他节点上。 因此,可以认为hash slot 算法,是一致性hash算法的优化。 优点:即拥有了一致性hash算法的优点,又避免了热点数据; master的增加和移除也是非常方面,增加master,就把其他节点的slot移动到这个master。移除master,就把当前master的slot移动到其他节点。  # 3.cluster 架构  redis cluster是一个去中心化的集群,每个节点都会跟其他节点保持连接,用来交换彼此的信息。 节点组成集群的方式使用cluster meet命令,meet命令可以让两个节点相互握手,然后通过gossip协议交换信息。如果一个节点r1在集群中,新节点r4加入的时候与r1节点握手,r1节点会把集群内的其他节点信息通过gossip协议发送给r4,r4会一一与这些节点完成握手,从而加入到集群中。 节点在启动的时候会生成一个全局的标识符,并持久化到配置文件,在节点与其他节点握手后,这些信息也都持久化下来。节点与其他节点通信,标识符是它唯一的标识,而不是IP、PORT地址。如果一个节点移动位置导致IP、PORT地址发生变更,集群内的其他节点能把该节点的IP、PORT地址纠正过来。 集群数据以数据分布表的方式保存在各个slot上。集群只有在16384个slot都有对应的节点才能正常工作。 slot可以动态的分配、删除和迁移。每个节点会保存一份数据分布表,节点会将自己的slot信息发送给其他节点,发送的方式使用一个unsigned char的数组,数组长度为16384/8。每个bit标识为0或者1来标识某个slot是否是它负责的。 由于节点间不停的在传递数据分布表,所以为了节省带宽,redis选择了只传递自己的分布数据。但这样的方式也会带来管理方面的麻烦,如果一个节点删除了自己负责的某个slot,这样该节点传递给其他节点数据分布表的slot标识为0,而redis采用了bitmapTestBit方法,只处理slot为1的节点,而并未把每个slot与收到的数据分布表对比,从而产生了节点间数据分布表视图的不一致。这种问题目前只能通过使用者来避免。 redis cluster安装部署: (1)开启cluster配置 (2)meet,节点握手 (3)指派槽 (4)主从复制数据、高可用、分片 # 4.安装 ## 4.1.原生命令安装 | 主机 | 端口 | 服务 | 服务 | | --- | --- | --- | --- | | 127.0.0.1 | 7000 | redis | 主节点1 | | 127.0.0.1 | 7001 | redis | 主节点2 | | 127.0.0.1 | 7002 | redis | 主节点3 | | 127.0.0.1 | 7003 | redis | 从节点1 -- 主节点1 | | 127.0.0.1 | 7004 | redis | 从节点2 -- 主节点2 | | 127.0.0.1 | 7005 | redis | 从节点3 -- 主节点3 | 使用原生命令安装Redis集群虽然使用的不多,但在学习阶段使用命令安装可以加深我们自己对整个过程的理解。 安装过程分为四步: 1. 配置开启节点 1. 使用meet操作 1. 分配槽 1. 设置主从 ### 4.1.1.配置cluster - 集群 cluster info :打印集群的信息 cluster nodes :列出集群当前已知的所有节点( node),以及这些节点的相关信息。 - 节点 cluster meet ip port:将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。 cluster forget \<node_id> :从集群中移除 node_id 指定的节点。 cluster replicate \<node_id> :将当前节点设置为 node_id 指定的节点的从节点。 cluster saveconfig :将节点的配置文件保存到硬盘里面。 step 1.开启cluster配置 ``` ################################## INCLUDES ################################### # include /path/to/local.conf # include /path/to/other.conf ################################## MODULES ##################################### # loadmodule /path/to/my_module.so # loadmodule /path/to/other_module.so ################################## NETWORK ##################################### bind ys-05 # 修改protected-mode,支持远程访问 protected-mode yes port 6379 # TCP listen() backlog. tcp-backlog 511 # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 # TCP keepalive. tcp-keepalive 300 ################################# TLS/SSL ##################################### # 默认情况下,TLS/SSL 是禁用的。 要启用它,“tls-port”配置指令可用于定义 TLS 侦听端口。 ################################# GENERAL ##################################### # 默认情况下,Redis 不作为守护进程运行。 如果需要,请使用“是”。 # 注意Redis在守护进程时会在/var/run/redis.pid中写入一个pid文件。 # Redis 由 upstart 或 systemd 监督时,此参数没有影响。 daemonize yes pidfile /var/run/redis_6379.pid logfile "6379.log" # 设置数据库数量。 默认数据库是 DB 0,您可以使用 SELECT <dbid> 为每个连接选择不同的数据库,其中 dbid 是介于 0 和 'databases'-1 之间的数字 databases 16 ################################ SNAPSHOTTING ################################ # Save the DB to disk. dbfilename dump.rdb # redis的软件安装目录 dir ./ ################################# REPLICATION ################################# # 主副本复制。 使用 replicaof 使 Redis 实例成为另一个 Redis 服务器的副本。 关于 Redis 复制的一些事情要尽快理解。 ############################### KEYS TRACKING ################################# # Redis 实现了对客户端缓存值的服务器辅助支持。 # 这是使用一个失效表来实现的,该表使用一个由键名索引的基键来记住哪些客户端有哪些键。 反过来,这用于向客户端发送无效消息。 ################################## SECURITY ################################### # 警告:由于 Redis 非常快,外部用户每秒最多可以尝试 100 万个密码来对抗现代机器。 这意味着您应该使用非常强大的密码,否则它们将很容易被破解。 # 请注意,因为密码实际上是客户端和服务器之间的共享秘密,不应该被任何人记住,密码可以很容易地是来自 /dev/urandom 或其他任何东西的长字符串,所以使用长且不可猜测的 密码没有暴力攻击是可能的。 ################################### CLIENTS #################################### # 设置同时连接的最大客户端数。 默认情况下,此限制设置为 10000 个客户端,但是如果 Redis 服务器无法配置进程文件限制以允许指定的限制,则允许的最大客户端数设置为当前文件限制 - 32(因为 Redis 保留了一个 很少有内部使用的文件描述符)。 # 一旦达到限制,Redis 将关闭所有新连接,发送错误“达到最大客户端数”。 # 重要提示:当使用 Redis Cluster 时,最大连接数也与集群总线共享:集群中的每个节点将使用两个连接,一个传入,一个传出。 在非常大的集群的情况下,相应地调整限制大小是很重要的。 # maxclients 10000 # 如果为 yes,代表为只读状态,但并不表示客户端用集群方式以从节点为入口连入集群时,不可以进行 set 操作,且 set 操作的数据不会被放在从节点的槽上,会被放到某主节点的槽上。 slave-read-only no ############################## MEMORY MANAGEMENT ################################ # 将内存使用限制设置为指定的字节数。 # 当达到内存限制时,Redis 将尝试根据选择的驱逐策略删除键(参见 maxmemory-policy)。 ############################# LAZY FREEING #################################### # Redis 有两个删除键的原语。 一种称为 DEL,是对对象的阻塞删除。 这意味着服务器停止处理新命令,以便以同步方式回收与对象关联的所有内存。 # 如果删除的键与一个小对象相关联,则执行 DEL 命令所需的时间非常短,与 Redis 中的大多数其他 O(1) 或 O(log_N) 命令相当。 # 但是,如果键与包含数百万个元素的聚合值相关联,则服务器可以阻塞很长时间(甚至几秒钟)以完成操作。 ################################ THREADED I/O ################################# # Redis 大部分是单线程的,但是有一些线程操作,例如 UNLINK、慢 I/O 访问和其他在侧线程上执行的事情。 ############################ KERNEL OOM CONTROL ############################## # 在 Linux 上,可以提示内核 OOM 杀手在内存不足时应该首先杀死哪些进程。 #################### KERNEL transparent hugepage CONTROL ###################### # 通常内核透明大页面控件默认设置为“madvise”或“never”(/sys/kernel/mm/transparent_hugepage/enabled),在这种情况下,此配置无效。 # 在将其设置为“始终”的系统上,redis 将尝试专门为 redis 进程禁用它,以避免专门针对 fork(2) 和 CoW 的延迟问题。 # 如果出于某种原因您希望保持启用状态,您可以将此配置设置为“no”,并将内核全局设置为“always”。 disable-thp yes ############################## APPEND ONLY MODE ############################### # Redis的持久化存储提供两种方式:RDB与AOF。RDB是默认配置。AOF需要手动开启 appendonly yes # 保存数据的AOF文件名称 原文1 appendfilename "appendonly.aof" # appendfysnc是对redis性能有重要影响的参数之一。可取三种值:always、everysec和no。 # 设置为always时,会极大消弱Redis的性能,因为这种模式下每次write后都会调用fsync(Linux为调用fdatasync)。 # 如果设置为no,则write后不会有fsync调用,由操作系统自动调度刷磁盘,性能是最好的。 # everysec为最多每秒调用一次fsync,这种模式性能并不是很糟糕,一般也不会产生毛刺,这归功于Redis引入了BIO线程,所有fsync操作都异步交给了BIO线程。 appendfsync always ################################ LUA SCRIPTING ############################### # Lua 脚本的最大执行时间,以毫秒为单位。 # 如果达到最大执行时间,Redis 将记录脚本在最大允许时间后仍在执行,并将开始回复错误的查询。 # 当长时间运行的脚本超过最大执行时间时,只有 SCRIPT KILL 和 SHUTDOWN NOSAVE 命令可用。 第一个可用于停止尚未调用任何写入命令的脚本。 第二种是在脚本已经发出写入命令但用户不想等待脚本自然终止的情况下关闭服务器的唯一方法。 # 将其设置为 0 或负值以无限执行而不会发出警告。 lua-time-limit 5000 ################################ REDIS CLUSTER ############################### cluster-enabled yes # 集群配置信息文件,由Redis自行更新,不用手动配置。每个节点都有一个集群配置文件用于持久化保存集群信息,需确保与运行中实例的配置文件名不冲突。 cluster-config-file nodes-6379.conf # 主观下线超时时间 cluster-node-timeout 15000 # 如果设置为0,只要发现主节点失联,那么从节点总会去尝试进行故障转移 # 如果为正数,一旦从节点与主节点失联的时间超过 cluster-node-timeout * cluster-replica-validity-factor + ping的间隔时间,那么该从节点不会进行故障转移 # 如果为非0的值,一旦所有从节点都不可进行故障转移时,集群将不再对外提供服务,直到主节点重新加入集群 cluster-replica-validity-factor 10 # 集群副本能够迁移到孤立的主服务器,即没有工作副本的主服务器。 这提高了集群抵抗故障的能力,否则如果孤立的主服务器没有工作副本,则在发生故障时无法进行故障转移。 # 只有当它们的旧主服务器至少还有给定数量的其他工作副本时,副本才会迁移到孤立的主服务器。 这个数字就是“迁移障碍”。 迁移屏障为 1 意味着只有当其主副本至少有 1 个其他工作副本时,副本才会迁移,依此类推。 它通常反映了您希望集群中每个主服务器的副本数量。 # 默认为 1(副本仅在其主服务器保留至少一个副本时迁移)。 要禁用迁移,只需将其设置为一个非常大的值或将 cluster-allow-replica-migration 设置为“no”。 # 可以设置 0 值,但仅对调试有用,在生产中很危险。 # cluster-migration-barrier 1 # 关闭此选项允许使用较少的自动集群配置。 # 它既禁止迁移到孤立的主控,也禁用从变空的主控的迁移。 # 默认为“是”(允许自动迁移)。 # cluster-allow-replica-migration yes # 默认情况下,如果 Redis Cluster 节点检测到至少有一个未覆盖的哈希槽(没有可用的节点为其提供服务),则它们会停止接受查询。 # 这样,如果集群部分宕机(例如,一系列哈希槽不再被覆盖),所有集群最终都会变得不可用。 # 一旦再次覆盖所有插槽,它就会自动返回可用。 # 但是,有时您希望正在工作的集群子集继续接受对仍然覆盖的部分键空间的查询。 为此,只需将 cluster-require-full-coverage 选项设置为 no。 # cluster-require-full-coverage yes # 当设置为“是”时,此选项可防止复制副本在主服务器故障期间尝试故障转移其主服务器。但是,如果强制执行手动故障转移,主服务器仍然可以执行手动故障转移。 # 这在不同的情况下非常有用,特别是在多个数据中心操作的情况下,如果在整个DC故障的情况下不希望一方被提升,我们希望它永远不会被提升。 cluster-replica-no-failover no # 如果将其设置为no(默认情况下为默认值),则当Redis群集被标记为失败时,或者当节点无法到达时,Redis群集中的节点将停止为所有流量提供服务法定人数的主持人或未达到完全覆盖的情况。这样可以防止从不知道群集更改的节点读取可能不一致的数据。可以将此选项设置为yes,以允许在失败状态期间从节点进行读取,这对于希望优先考虑读取可用性但仍希望防止写入不一致的应用程序很有用。当仅使用一个或两个分片的Redis Cluster时,也可以使用它,因为它允许节点在主服务器发生故障但无法进行自动故障转移时继续为写入提供服务。 cluster-allow-reads-when-down no # In order to setup your cluster make sure to read the documentation available at https://redis.io web site. ########################## CLUSTER DOCKER/NAT support ######################## # 在某些部署中,Redis Cluster 节点地址发现失败,因为地址是 NAT-ted 或者是因为端口被转发(典型情况是 Docker 和其他容器)。 ################################## SLOW LOG ################################### # Redis 慢日志是一个记录超过指定执行时间的查询的系统。执行时间不包括与客户端对话、发送回复等 I/O 操作, # 但只是实际执行命令所需的时间(这是命令执行的唯一阶段,线程被阻塞并且同时无法服务其他请求)。 ################################ LATENCY MONITOR ############################## # Redis 延迟监控子系统在运行时对不同的操作进行采样,以收集与 Redis 实例的可能延迟来源相关的数据。 ############################# EVENT NOTIFICATION ############################## # Redis 可以通知 Pub/Sub 客户端有关密钥空间中发生的事件。 # 此功能记录在 https://redis.io/topics/notifications ############################### GOPHER SERVER ################################# # Redis 包含 Gopher 协议的实现,如 RFC 1436 (https://www.ietf.org/rfc/rfc1436.txt) 中所述。 ############################### ADVANCED CONFIG ############################### # 当条目数量较少且最大条目不超过给定阈值时,哈希使用内存高效的数据结构进行编码。可以使用以下指令配置这些阈值。 ########################### ACTIVE DEFRAGMENTATION ####################### # # 什么是主动碎片整理? ``` step 2.启动Redis服务 启动redis的7000,7001,7002,7003,7004,7005服务端 ``` redis-server redis-7003.conf redis-server redis-7001.conf redis-server redis-7002.conf redis-server redis-7003.conf redis-server redis-7004.conf redis-server redis-7005.conf ``` step 3.查看服务进程 ``` [root@redis-master config]# ps -ef|grep redis root 6218 1 0 09:33 ? 00:00:00 redis-server 127.0.0.1:7000 [cluster] root 6223 1 0 09:33 ? 00:00:00 redis-server 127.0.0.1:7001 [cluster] root 6228 1 0 09:33 ? 00:00:00 redis-server 127.0.0.1:7002 [cluster] root 6233 1 0 09:33 ? 00:00:00 redis-server 127.0.0.1:7003 [cluster] root 6239 1 0 09:33 ? 00:00:00 redis-server 127.0.0.1:7004 [cluster] root 6244 1 0 09:33 ? 00:00:00 redis-server 127.0.0.1:7005 [cluster] root 6251 6154 0 09:35 pts/0 00:00:00 grep --color=auto redis [root@redis-master config]# ``` step 4.查看节点信息 ``` 127.0.0.1:7000> set hello world (error) CLUSTERDOWN Hash slot not served 127.0.0.1:7000> ``` 查看节点的信息,只能查看自己的信息 cluster-config-file;配置集群的配置文件,里面包含了集群节点的ID等信息。节点ID不同于运行ID,节点ID在集群初始化时只,创建一次,节点重启的时候不会改改变,而运行时ID会改变。 ``` [root@redis-master config]# cat nodes-7000.conf 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f :0@0 myself,master - 0 0 0 connected vars currentEpoch 0 lastVoteEpoch 0 [root@redis-master config]# ``` 执行cluster命令与配置文件的信息一致 ``` [root@redis-master config]# redis-cli -p 7000 cluster nodes 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f :7000@17000 myself,master - 0 0 0 connected [root@redis-master config]# redis-cli -p 7000 cluster info cluster_state:fail cluster_slots_assigned:0 cluster_slots_ok:0 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:1 cluster_size:0 cluster_current_epoch:0 cluster_my_epoch:0 cluster_stats_messages_sent:0 cluster_stats_messages_received:0 [root@redis-master config]# ``` Redis集群启动脚本 ``` #!/bin/bash # create nodes array nodes=(7000 7001 7002 7003 7004 7005) for node in ${nodes[@]} do ./redis-server /opt/redis/redis-5.0.7/cluster-config/redis-${node}.conf if [ $? = 0 ]; then echo "port $node start success !"; else echo " port $node start fail "; fi done exit 0 ``` ### 4.1.2.meet step 1.在节点1上执行meet,添加一个1节点 ``` [root@redis-master config]# redis-cli -p 7000 cluster meet 127.0.0.1 7001 OK [root@redis-master config]# ``` step 2.查看节点cluster信息 ``` 节点1 [root@redis-master config]# redis-cli -p 7000 cluster nodes 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f 127.0.0.1:7000@17000 myself,master - 0 0 1 connected 8f6268d5dbe182ac270d0034f3670b59b9b32bbb 127.0.0.1:7001@17001 master - 0 1617457312369 0 connected 节点2 [root@redis-master config]# redis-cli -p 7001 cluster nodes 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f 127.0.0.1:7000@17000 master - 0 1617457318571 1 connected 8f6268d5dbe182ac270d0034f3670b59b9b32bbb 127.0.0.1:7001@17001 myself,master - 0 0 0 connected ``` step 3.未加入集群的节点不能发现其他节点信息 ``` [root@redis-master config]# redis-cli -p 7002 cluster nodes 6f6c54fbd6fa015eebea66f24bfbaf0253c0bdf2 :7002@17002 myself,master - 0 0 0 connected [root@redis-master config]# ``` step 4.依次添加剩余的节点 ``` [root@redis-master config]# redis-cli -p 7000 cluster meet 127.0.0.1 7002 OK [root@redis-master config]# redis-cli -p 7000 cluster meet 127.0.0.1 7003 OK [root@redis-master config]# redis-cli -p 7000 cluster meet 127.0.0.1 7004 OK [root@redis-master config]# redis-cli -p 7000 cluster meet 127.0.0.1 7005 OK [root@redis-master config]# ``` step 5.查看cluster信息 ``` [root@redis-master config]# redis-cli -p 7000 cluster nodes a7649ef2c1f29aee5e1d7aaaca125459fa3a3a24 127.0.0.1:7004@17004 master - 0 1617457567000 0 connected 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f 127.0.0.1:7000@17000 myself,master - 0 1617457567000 1 connected 6f6c54fbd6fa015eebea66f24bfbaf0253c0bdf2 127.0.0.1:7002@17002 master - 0 1617457568000 2 connected 8f6268d5dbe182ac270d0034f3670b59b9b32bbb 127.0.0.1:7001@17001 master - 0 1617457570095 4 connected 200da24e523a3440532acd41dff95f3e379ccc2b 127.0.0.1:7003@17003 master - 0 1617457568092 3 connected 270036b7e78a9bbccb90bd91114cb7de0aaae8ab 127.0.0.1:7005@17005 master - 0 1617457569093 5 connected [root@redis-master config]# [root@redis-master config]# redis-cli -p 7000 cluster info cluster_state:fail cluster_slots_assigned:0 cluster_slots_ok:0 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:0 cluster_current_epoch:5 cluster_my_epoch:1 cluster_stats_messages_ping_sent:313 cluster_stats_messages_pong_sent:329 cluster_stats_messages_meet_sent:5 cluster_stats_messages_sent:647 cluster_stats_messages_ping_received:329 cluster_stats_messages_pong_received:318 cluster_stats_messages_received:647 [root@redis-master config]# ``` 当前集群不可用 ``` 127.0.0.1:7000> set hello world (error) CLUSTERDOWN Hash slot not served 127.0.0.1:7000> ``` meet 脚本 ``` #!/bin/bash #create other nodes,implements meet nodes=(7001 7002 7003 7004 7005) for node in ${nodes[@]} do if [ -n "$1" ]; then ./redis-cli -p $1 cluster meet 127.0.0.1 $node if [ $? = 0 ]; then echo " meet susccess ! "; else echo " meet fail !"; fi else echo " not find paras "; fi done ``` ### 4.1.3.指派槽 槽(slot) cluster addslots \<slot> [slot ...] :将一个或多个槽( slot)指派( assign)给当前节点。 cluster delslots \<slot> [slot ...] :移除一个或多个槽对当前节点的指派。 cluster flushslots :移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。 cluster setslot \<slot> node \<node_id> :将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽>,然后再进行指派。 cluster setslot \<slot> migrating \<node_id> :将本节点的槽 slot 迁移到 node_id 指定的节点中。 cluster setslot \<slot> importing \<node_id> :从 node_id 指定的节点中导入槽 slot 到本节点。 cluster setslot \<slot> stable :取消对槽 slot 的导入( import)或者迁移( migrate)。 键 cluster keyslot \<key> :计算键 key 应该被放置在哪个槽上。 cluster countkeysinslot \<slot> :返回槽 slot 目前包含的键值对数量。 cluster getkeysinslot \<slot> \<count> :返回 count 个 slot 槽中的键 step 1.使用脚本来分配槽: ``` #!/bin/bash port=$1 start=$2 end=$3 for slot in `seq $start $end ` do ./redis-cli -p ${port} cluster addslots ${slot} if [ $? = 0 ]; then echo " add slot success ! "; else echo " add slot fail "; fi done exit 0 ``` step 2.查看集群信息 ``` [root@redis-master config]# redis-cli -p 7000 cluster info cluster_state:ok cluster_slots_assigned:5462 cluster_slots_ok:5462 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:1 cluster_current_epoch:5 cluster_my_epoch:1 cluster_stats_messages_ping_sent:2317 cluster_stats_messages_pong_sent:2438 cluster_stats_messages_meet_sent:5 cluster_stats_messages_sent:4760 cluster_stats_messages_ping_received:2438 cluster_stats_messages_pong_received:2322 cluster_stats_messages_received:4760 [root@redis-master config]# ``` cluster_slots_ok:5462显示集群已经分配了5462个槽 step 3.查看节点1 发现已经配置的槽位数 ``` [root@redis-master config]# redis-cli -p 7000 cluster nodes a7649ef2c1f29aee5e1d7aaaca125459fa3a3a24 127.0.0.1:7004@17004 master - 0 1617459635000 0 connected 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f 127.0.0.1:7000@17000 myself,master - 0 1617459636000 1 connected 0-5461 6f6c54fbd6fa015eebea66f24bfbaf0253c0bdf2 127.0.0.1:7002@17002 master - 0 1617459634946 2 connected 8f6268d5dbe182ac270d0034f3670b59b9b32bbb 127.0.0.1:7001@17001 master - 0 1617459635000 4 connected 200da24e523a3440532acd41dff95f3e379ccc2b 127.0.0.1:7003@17003 master - 0 1617459636949 3 connected 270036b7e78a9bbccb90bd91114cb7de0aaae8ab 127.0.0.1:7005@17005 master - 0 1617459636000 5 connected [root@redis-master config]# ``` step 4.其他节点已经获得节点1分配槽位的信息 ``` [root@redis-master config]# redis-cli -p 7001 cluster nodes 270036b7e78a9bbccb90bd91114cb7de0aaae8ab 127.0.0.1:7005@17005 master - 0 1617459643149 5 connected 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f 127.0.0.1:7000@17000 master - 0 1617459644151 1 connected 0-5461 200da24e523a3440532acd41dff95f3e379ccc2b 127.0.0.1:7003@17003 master - 0 1617459643000 3 connected a7649ef2c1f29aee5e1d7aaaca125459fa3a3a24 127.0.0.1:7004@17004 master - 0 1617459640144 0 connected 6f6c54fbd6fa015eebea66f24bfbaf0253c0bdf2 127.0.0.1:7002@17002 master - 0 1617459642148 2 connected 8f6268d5dbe182ac270d0034f3670b59b9b32bbb 127.0.0.1:7001@17001 myself,master - 0 1617459643000 4 connected [root@redis-master config]# ``` step 5.此时可以正常连接Redis ``` 127.0.0.1:7000> set hello world OK 127.0.0.1:7000> ``` 查看集群的配置信息: ``` 127.0.0.1:7000> config get cluster* 1) "cluster-require-full-coverage" 2) "no" 3) "cluster-replica-no-failover" 4) "no" 5) "cluster-slave-no-failover" 6) "no" 7) "cluster-enabled" 8) "yes" 9) "cluster-allow-reads-when-down" 10) "no" 11) "cluster-announce-ip" 12) "" 13) "cluster-replica-validity-factor" 14) "10" 15) "cluster-slave-validity-factor" 16) "10" 17) "cluster-migration-barrier" 18) "1" 19) "cluster-announce-bus-port" 20) "0" 21) "cluster-announce-port" 22) "0" 23) "cluster-node-timeout" 24) "15000" 127.0.0.1:7000> ``` step 6.给3个节点分配slot ``` sh addslot.sh 7000 0 5461 sh addslot.sh 7001 5462 10922 sh addslot.sh 7001 10923 16383 ``` step 7.查看集群状态 ``` [root@redis-master config]# redis-cli -p 7001 cluster info cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:2 cluster_current_epoch:5 cluster_my_epoch:4 cluster_stats_messages_ping_sent:2961 cluster_stats_messages_pong_sent:2816 cluster_stats_messages_sent:5777 cluster_stats_messages_ping_received:2815 cluster_stats_messages_pong_received:2961 cluster_stats_messages_meet_received:1 cluster_stats_messages_received:5777 [root@redis-master config]# ``` ### 4.1.4.分配主从 step 1.查看node信息 ``` [root@redis-master config]# redis-cli -p 7001 cluster nodes 270036b7e78a9bbccb90bd91114cb7de0aaae8ab 127.0.0.1:7005@17005 master - 0 1617460827000 5 connected 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f 127.0.0.1:7000@17000 master - 0 1617460827000 1 connected 0-5461 200da24e523a3440532acd41dff95f3e379ccc2b 127.0.0.1:7003@17003 master - 0 1617460829678 3 connected a7649ef2c1f29aee5e1d7aaaca125459fa3a3a24 127.0.0.1:7004@17004 master - 0 1617460828675 0 connected 6f6c54fbd6fa015eebea66f24bfbaf0253c0bdf2 127.0.0.1:7002@17002 master - 0 1617460829000 2 connected 8f6268d5dbe182ac270d0034f3670b59b9b32bbb 127.0.0.1:7001@17001 myself,master - 0 1617460828000 4 connected 5462-16383 [root@redis-master config]# ``` step 2.分配主从 ``` [root@redis-master config]# redis-cli -p 7003 cluster replicate 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f OK [root@redis-master config]# [root@redis-master config]# redis-cli -p 7004 cluster replicate 8f6268d5dbe182ac270d0034f3670b59b9b32bbb OK [root@redis-master config]# [root@redis-master config]# redis-cli -p 7005 cluster replicate 6f6c54fbd6fa015eebea66f24bfbaf0253c0bdf2 OK [root@redis-master config]# ``` step 3.查看主从关系 ``` [root@redis-master config]# redis-cli -p 7000 cluster nodes a7649ef2c1f29aee5e1d7aaaca125459fa3a3a24 127.0.0.1:7004@17004 slave 8f6268d5dbe182ac270d0034f3670b59b9b32bbb 0 1617461116488 4 connected 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f 127.0.0.1:7000@17000 myself,master - 0 1617461117000 1 connected 0-5461 6f6c54fbd6fa015eebea66f24bfbaf0253c0bdf2 127.0.0.1:7002@17002 master - 0 1617461116000 2 connected 8f6268d5dbe182ac270d0034f3670b59b9b32bbb 127.0.0.1:7001@17001 master - 0 1617461113000 4 connected 5462-16383 200da24e523a3440532acd41dff95f3e379ccc2b 127.0.0.1:7003@17003 slave 8a17f64474b9411cb4d1c9f1ca9def67afe37f8f 0 1617461117490 1 connected 270036b7e78a9bbccb90bd91114cb7de0aaae8ab 127.0.0.1:7005@17005 slave 6f6c54fbd6fa015eebea66f24bfbaf0253c0bdf2 0 1617461118493 2 connected [root@redis-master config]# ``` 三主三从的cluster。 查看slot的分配 ``` [root@redis-master config]# redis-cli -p 7000 cluster slots 1) 1) (integer) 0 2) (integer) 5461 3) 1) "127.0.0.1" 2) (integer) 7000 3) "8a17f64474b9411cb4d1c9f1ca9def67afe37f8f" 4) 1) "127.0.0.1" 2) (integer) 7003 3) "200da24e523a3440532acd41dff95f3e379ccc2b" 2) 1) (integer) 5462 2) (integer) 16383 3) 1) "127.0.0.1" 2) (integer) 7001 3) "8f6268d5dbe182ac270d0034f3670b59b9b32bbb" 4) 1) "127.0.0.1" 2) (integer) 7004 3) "a7649ef2c1f29aee5e1d7aaaca125459fa3a3a24" [root@redis-master config]# ``` step 4.查看信息 ``` 127.0.0.1:7000> set dba oracle -> Redirected to slot [5732] located at 127.0.0.1:7001 OK 127.0.0.1:7001> get dba "oracle" 127.0.0.1:7001> ``` set 写入信息,连接的为 127.0.0.1:7000 get 获取信息的时候,连接的为 127.0.0.1:7001> ## 4.2.官方工具安装 | 主机 | 端口 | 服务 | 服务 | | --- | --- | --- | --- | | 127.0.0.1 | 7000 | redis | 主节点1 | | 127.0.0.1 | 7001 | redis | 主节点2 | | 127.0.0.1 | 7002 | redis | 主节点3 | | 127.0.0.1 | 7003 | redis | 从节点1 -- 主节点1 | | 127.0.0.1 | 7004 | redis | 从节点2 -- 主节点2 | | 127.0.0.1 | 7005 | redis | 从节点3 -- 主节点3 | step 1.开启cluster配置 ``` port 6379 daemonize no pidfile /var/run/redis_6379.pid logfile "6379.log" dri ./ cluster-enabled yes cluster-config-file nodes-${port}.conf cluster-node-timeout 5000 cluster-replica-validity-factor 10 cluster-migration-barrier 1 cluster-require-full-coverage yes cluster-replica-no-failover no cluster-allow-reads-when-down no ``` step 2.启动Redis服务 step 3.使用工具配置Redis集群 ### 4.2.1.redis-trib.rb redis-trib.rb是官方提供的Redis Cluster的管理工具,无需额外下载,默认位于源码包的src目录下,但因该工具是用ruby开发的,所以需要准备相关的依赖环境。 step 1.安装ruby ``` yum install ruby rubygems ``` step 2.安装gem与redis插件 - 使用源码安装 1.插件下载地址:https://rubygems.global.ssl.fastly.net/gems/redis-3.2.1.gem 2.执行安装 ``` gem install -l ./redis-3.2.1.gem ``` - 使用gem源安装 1.查看源 ``` gem sources ``` 2.删掉原来所有的源,默认外国源,国内无法访问 ``` gem sources --remove https://rubygems.org/ ``` 3.添加国内的镜像源(或者其他源或本地源) ``` gem sources --add https://gems.ruby-china.com/ ``` 4.导入Redis ``` gem install redis ``` step 3.安装Redis-trib.rb ``` [root@redis-master src]# ./redis-trib.rb WARNING: redis-trib.rb is not longer available! You should use redis-cli instead. All commands and features belonging to redis-trib.rb have been moved to redis-cli. In order to use them you should call redis-cli with the --cluster option followed by the subcommand name, arguments and options. Use the following syntax: redis-cli --cluster SUBCOMMAND [ARGUMENTS] [OPTIONS] Example: redis-cli --cluster info 127.0.0.1:7000 To get help about all subcommands, type: redis-cli --cluster help [root@redis-master src]# ``` ### 4.2.2.redis-cli管理集群 在5.0之前该工具是由ruby语言编写的redis-trib.rb,在使用前需要安装ruby语言环境。在5.0之后redis摒弃了该工具,将搭建集群的功能合并到了redis-cli上,进一步简化了搭建redis cluster的过程。 ``` [root@redis-master src]# redis-cli --cluster help Cluster Manager Commands: create host1:port1 ... hostN:portN --cluster-replicas <arg> check host:port --cluster-search-multiple-owners info host:port fix host:port --cluster-search-multiple-owners --cluster-fix-with-unreachable-masters reshard host:port --cluster-from <arg> --cluster-to <arg> --cluster-slots <arg> --cluster-yes --cluster-timeout <arg> --cluster-pipeline <arg> --cluster-replace rebalance host:port --cluster-weight <node1=w1...nodeN=wN> --cluster-use-empty-masters --cluster-timeout <arg> --cluster-simulate --cluster-pipeline <arg> --cluster-threshold <arg> --cluster-replace add-node new_host:new_port existing_host:existing_port --cluster-slave --cluster-master-id <arg> del-node host:port node_id call host:port command arg arg .. arg --cluster-only-masters --cluster-only-replicas set-timeout host:port milliseconds import host:port --cluster-from <arg> --cluster-from-user <arg> --cluster-from-pass <arg> --cluster-from-askpass --cluster-copy --cluster-replace backup host:port backup_directory help For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster. Cluster Manager Options: --cluster-yes Automatic yes to cluster commands prompts [root@redis-master src]# ``` 杀死进程 ``` # ps -ef|grep redis-server |grep 700 |awk '{print $2}'| xargs kill ``` step 1.配置Redis集群配置文件 step 2.启动所有节点 step 3.使用redis-cli创建集群 redis-cli会按照给定的顺序设置主节点和从节点 ``` [root@redis-master config]# redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1 >>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 127.0.0.1:7004 to 127.0.0.1:7000 Adding replica 127.0.0.1:7005 to 127.0.0.1:7001 Adding replica 127.0.0.1:7003 to 127.0.0.1:7002 >>> Trying to optimize slaves allocation for anti-affinity [WARNING] Some slaves are in the same host as their master M: 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 127.0.0.1:7000 slots:[0-5460] (5461 slots) master M: 0327bac06c7d9bcd8ff87a0a1d58e6c175b8c1ad 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master M: 75bbe8b96338bbc0437c45d2219f11b3d0de7e81 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master S: e474b72065c8b4ca76af8188b29ba82a2e5bfbef 127.0.0.1:7003 replicates 75bbe8b96338bbc0437c45d2219f11b3d0de7e81 S: b629cb1abaae295513de3b7e2f2d06072c044409 127.0.0.1:7004 replicates 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 S: 7337d3d9750ce40a99d5d7b8034b08a5066f7331 127.0.0.1:7005 replicates 0327bac06c7d9bcd8ff87a0a1d58e6c175b8c1ad Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join .. >>> Performing Cluster Check (using node 127.0.0.1:7000) M: 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 127.0.0.1:7000 slots:[0-5460] (5461 slots) master 1 additional replica(s) S: b629cb1abaae295513de3b7e2f2d06072c044409 127.0.0.1:7004 slots: (0 slots) slave replicates 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 M: 0327bac06c7d9bcd8ff87a0a1d58e6c175b8c1ad 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) M: 75bbe8b96338bbc0437c45d2219f11b3d0de7e81 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: e474b72065c8b4ca76af8188b29ba82a2e5bfbef 127.0.0.1:7003 slots: (0 slots) slave replicates 75bbe8b96338bbc0437c45d2219f11b3d0de7e81 S: 7337d3d9750ce40a99d5d7b8034b08a5066f7331 127.0.0.1:7005 slots: (0 slots) slave replicates 0327bac06c7d9bcd8ff87a0a1d58e6c175b8c1ad [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. [root@redis-master config]# ``` step 4.集群完整性检查 集群完整性是指所有的槽都分配到存活的redis主节点上,只要16384个槽中有一个槽未被分配,则表示集群不完整 ``` [root@redis-master config]# redis-cli --cluster check 127.0.0.1:7000 127.0.0.1:7000 (85c49aa6...) -> 0 keys | 5461 slots | 1 slaves. 127.0.0.1:7001 (0327bac0...) -> 0 keys | 5462 slots | 1 slaves. 127.0.0.1:7002 (75bbe8b9...) -> 0 keys | 5461 slots | 1 slaves. [OK] 0 keys in 3 masters. 0.00 keys per slot on average. >>> Performing Cluster Check (using node 127.0.0.1:7000) M: 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 127.0.0.1:7000 slots:[0-5460] (5461 slots) master 1 additional replica(s) S: b629cb1abaae295513de3b7e2f2d06072c044409 127.0.0.1:7004 slots: (0 slots) slave replicates 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 M: 0327bac06c7d9bcd8ff87a0a1d58e6c175b8c1ad 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) M: 75bbe8b96338bbc0437c45d2219f11b3d0de7e81 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: e474b72065c8b4ca76af8188b29ba82a2e5bfbef 127.0.0.1:7003 slots: (0 slots) slave replicates 75bbe8b96338bbc0437c45d2219f11b3d0de7e81 S: 7337d3d9750ce40a99d5d7b8034b08a5066f7331 127.0.0.1:7005 slots: (0 slots) slave replicates 0327bac06c7d9bcd8ff87a0a1d58e6c175b8c1ad [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. [root@redis-master config]# ``` 可以看到节点信息,主从配置和槽位的分配情况。 ## 4.3.测试集群故障转移 step 1.查看Redis进程 ``` [root@localhost config]# ps -ef|grep redis root 7750 1 0 22:38 ? 00:00:00 redis-server 127.0.0.1:7000 [cluster] root 7756 1 0 22:38 ? 00:00:00 redis-server 127.0.0.1:7001 [cluster] root 7762 1 0 22:38 ? 00:00:00 redis-server 127.0.0.1:7002 [cluster] root 7768 1 0 22:38 ? 00:00:00 redis-server 127.0.0.1:7003 [cluster] root 7774 1 0 22:38 ? 00:00:00 redis-server 127.0.0.1:7004 [cluster] root 7780 1 0 22:38 ? 00:00:00 redis-server 127.0.0.1:7005 [cluster] root 7797 1429 0 22:40 pts/0 00:00:00 grep --color=auto redis [root@localhost config]# ``` step 2.停止7000端口 master 节点 此时使用 7000 端口已经无法连接Redis集群 ``` [root@localhost config]# redis-cli --cluster check 127.0.0.1:7000 Could not connect to Redis at 127.0.0.1:7000: Connection refused [root@localhost config]# ``` step 3.使用 70001 端口检查集群状态 ``` [root@localhost config]# redis-cli --cluster check 127.0.0.1:7001 Could not connect to Redis at 127.0.0.1:7000: Connection refused *** WARNING: 127.0.0.1:7003 claims to be slave of unknown node ID 935fe8e1447f8159109b63e555f6899b0ec9cbaf. 127.0.0.1:7001 (b6fe5fd7...) -> 0 keys | 5462 slots | 1 slaves. 127.0.0.1:7002 (5e0c21dc...) -> 0 keys | 5461 slots | 1 slaves. [OK] 0 keys in 2 masters. 0.00 keys per slot on average. >>> Performing Cluster Check (using node 127.0.0.1:7001) M: b6fe5fd7ba6677c54fc8e8d16f0dde09ab93c157 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) S: d4a0364e61d350e370faa7ee84b8fdbf029dd266 127.0.0.1:7003 slots: (0 slots) slave replicates 935fe8e1447f8159109b63e555f6899b0ec9cbaf S: d1c352db8c29bf52fc4e24a7f0e5fc4d27928da2 127.0.0.1:7004 slots: (0 slots) slave replicates b6fe5fd7ba6677c54fc8e8d16f0dde09ab93c157 M: 5e0c21dc9b249ea3e1fdbf7a28ec0a13c500b441 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: 0dc9afccc0adb7ad3431818c4519fa1d8631c006 127.0.0.1:7005 slots: (0 slots) slave replicates 5e0c21dc9b249ea3e1fdbf7a28ec0a13c500b441 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [ERR] Not all 16384 slots are covered by nodes. [root@localhost config]# ``` 当前集群有2个master ,3 个slave节点。 错误提示: 1. 无法连接 7000 节点的Redis服务 ``` Could not connect to Redis at 127.0.0.1:7000: Connection refused *** WARNING: 127.0.0.1:7003 claims to be slave of unknown node ID 935fe8e1447f8159109b63e555f6899b0ec9cbaf. ``` 2. 提示有slot 未分配,并非所有16384插槽都被节点覆盖。 ``` [ERR] Not all 16384 slots are covered by nodes. ``` step 4.再次查看集群状态 ``` [root@localhost config]# redis-cli --cluster check 127.0.0.1:7001 Could not connect to Redis at 127.0.0.1:7000: Connection refused 127.0.0.1:7001 (b6fe5fd7...) -> 0 keys | 5462 slots | 1 slaves. 127.0.0.1:7003 (d4a0364e...) -> 0 keys | 5461 slots | 0 slaves. 127.0.0.1:7002 (5e0c21dc...) -> 0 keys | 5461 slots | 1 slaves. [OK] 0 keys in 3 masters. 0.00 keys per slot on average. >>> Performing Cluster Check (using node 127.0.0.1:7001) M: b6fe5fd7ba6677c54fc8e8d16f0dde09ab93c157 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) M: d4a0364e61d350e370faa7ee84b8fdbf029dd266 127.0.0.1:7003 slots:[0-5460] (5461 slots) master S: d1c352db8c29bf52fc4e24a7f0e5fc4d27928da2 127.0.0.1:7004 slots: (0 slots) slave replicates b6fe5fd7ba6677c54fc8e8d16f0dde09ab93c157 M: 5e0c21dc9b249ea3e1fdbf7a28ec0a13c500b441 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: 0dc9afccc0adb7ad3431818c4519fa1d8631c006 127.0.0.1:7005 slots: (0 slots) slave replicates 5e0c21dc9b249ea3e1fdbf7a28ec0a13c500b441 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. [root@localhost config]# ``` 此时节点 7003 的slave 成为master ,恢复 3个master节点。 step 5.故障节点上线后,状态检查 ``` [root@localhost config]# redis-server redis-7000.conf [root@localhost config]# redis-cli --cluster check 127.0.0.1:7001 127.0.0.1:7001 (b6fe5fd7...) -> 0 keys | 5462 slots | 1 slaves. 127.0.0.1:7003 (d4a0364e...) -> 0 keys | 5461 slots | 1 slaves. 127.0.0.1:7002 (5e0c21dc...) -> 0 keys | 5461 slots | 1 slaves. [OK] 0 keys in 3 masters. 0.00 keys per slot on average. >>> Performing Cluster Check (using node 127.0.0.1:7001) M: b6fe5fd7ba6677c54fc8e8d16f0dde09ab93c157 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) M: d4a0364e61d350e370faa7ee84b8fdbf029dd266 127.0.0.1:7003 slots:[0-5460] (5461 slots) master 1 additional replica(s) S: d1c352db8c29bf52fc4e24a7f0e5fc4d27928da2 127.0.0.1:7004 slots: (0 slots) slave replicates b6fe5fd7ba6677c54fc8e8d16f0dde09ab93c157 S: 935fe8e1447f8159109b63e555f6899b0ec9cbaf 127.0.0.1:7000 slots: (0 slots) slave replicates d4a0364e61d350e370faa7ee84b8fdbf029dd266 M: 5e0c21dc9b249ea3e1fdbf7a28ec0a13c500b441 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: 0dc9afccc0adb7ad3431818c4519fa1d8631c006 127.0.0.1:7005 slots: (0 slots) slave replicates 5e0c21dc9b249ea3e1fdbf7a28ec0a13c500b441 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. [root@localhost config]# ``` # 5.集群的伸缩 ## 5.1.集群的伸缩原理 集群的伸缩包括新节点的加入和旧节点退出。新节点时加入时,我们需要把一部分数据迁移到新节点来达到集群的负载均衡,旧节点退出时,我们需要把其上的数据迁移到其他节点上,确保该节点上的数据能够被正常访问。我们发现集群伸缩的核心其实是数据的迁移,而在Redis集群中,数据是以slot为单位的,那么也就是说,Redis集群的伸缩本质上是slot在不同机器节点间的迁移。同时,要实现扩缩容,我们不仅需要解决数据迁移,我们还需要解决数据路由问题。比如A节点正在向B节点迁移slot1的数据,在未完成迁移时,slot1中的一部分数据存在节点A上,一部分数据存在节点B上。那么以下三种情况下我们该如何路由slot1的客户端请求? 当除了A、B之外的其他节点接收到slot1的数据请求时,其他节点该路由给哪个节点? 当节点A接收到slot1的数据请求时,A该自己处理还是路由给B节点? 当节点B接收到slot1的数据请求时,B该自己处理还是路由给A节点?  - 扩容 setp 1.准备新节点  step 2.加入集群 step 3.迁移槽和数据  - 缩容 step 1.迁移槽和数据   step 2.忘记节点  step 3.关闭Redis服务 ## 5.2.使用 redis-cli扩容 step 1.查看每个集群节点的node ID和身份 ``` [root@redis-master config]# redis-cli -p 7000 cluster nodes b629cb1abaae295513de3b7e2f2d06072c044409 127.0.0.1:7004@18004 slave 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 0 1617468248000 1 connected 0327bac06c7d9bcd8ff87a0a1d58e6c175b8c1ad 127.0.0.1:7001@18001 master - 0 1617468247000 2 connected 5461-10922 75bbe8b96338bbc0437c45d2219f11b3d0de7e81 127.0.0.1:7002@18002 master - 0 1617468249933 3 connected 10923-16383 e474b72065c8b4ca76af8188b29ba82a2e5bfbef 127.0.0.1:7003@18003 slave 75bbe8b96338bbc0437c45d2219f11b3d0de7e81 0 1617468249000 3 connected 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 127.0.0.1:8000@18000 myself,master - 0 1617468247000 1 connected 0-5460 52ea3922fefa8182923d61d4a3b89814ca28de74 127.0.0.1:7006@18006 master - 0 1617468247000 0 connected 7337d3d9750ce40a99d5d7b8034b08a5066f7331 127.0.0.1:7005@18005 slave 0327bac06c7d9bcd8ff87a0a1d58e6c175b8c1ad 0 1617468247929 2 connected [root@redis-master config]# ``` step 2.添加master节点 ``` [root@redis-master config]# redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 >>> Adding node 127.0.0.1:8006 to cluster 127.0.0.1:7000 >>> Performing Cluster Check (using node 127.0.0.1:7000) M: 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 127.0.0.1:7000 slots:[0-5460] (5461 slots) master 1 additional replica(s) S: b629cb1abaae295513de3b7e2f2d06072c044409 127.0.0.1:8004 slots: (0 slots) slave replicates 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 M: 0327bac06c7d9bcd8ff87a0a1d58e6c175b8c1ad 127.0.0.1:8001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) M: 75bbe8b96338bbc0437c45d2219f11b3d0de7e81 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) S: e474b72065c8b4ca76af8188b29ba82a2e5bfbef 127.0.0.1:7003 slots: (0 slots) slave replicates 75bbe8b96338bbc0437c45d2219f11b3d0de7e81 S: 7337d3d9750ce40a99d5d7b8034b08a5066f7331 127.0.0.1:7005 slots: (0 slots) slave replicates 0327bac06c7d9bcd8ff87a0a1d58e6c175b8c1ad [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. >>> Send CLUSTER MEET to node 127.0.0.1:7006 to make it join the cluster. [OK] New node added correctly. [root@redis-master config]# ``` 这里是将节点加入了集群中,但是并没有分配slot,所以这个节点并没有真正的开始分担集群工作。 step 3.分配slot ``` redis-cli --cluster reshard 127.0.0.1:7000 --cluster-from 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 --cluster-to 52ea3922fefa8182923d61d4a3b89814ca28de74 --cluster-slots 5461 --cluster-yes ``` --cluster-from:表示slot目前所在的节点的node ID,多个ID用逗号分隔 --cluster-to:表示需要新分配节点的node ID(貌似每次只能分配一个) --cluster-slots:分配的slot数量 step 4.添加slave节点 ``` redis-cli --cluster add-node 127.0.0.1:7007 --cluster-slave --cluster-master-id 85c49aa646684cfa8a50ac7efd86c230e35f6ce3 ``` add-node: 后面的分别跟着新加入的slave和slave对应的master cluster-slave:表示加入的是slave节点 --cluster-master-id:表示slave对应的master的node ID   ## 5.3.收缩集群  下线节点127.0.0.1:7008(master)/127.0.0.1:7009(slave)  step 1.首先删除master对应的slave ``` redis-cli --cluster del-node 127.0.0.1:7009 530cf27337c1141ed12268f55ba06c15ca8494fc ``` del-node后面跟着slave节点的 ip:port 和node ID step 2.清空master的slot ``` redis-cli --cluster reshard 127.0.0.1:7008 --cluster-from 46f0b68b3f605b3369d3843a89a2b4a164ed21e8 --cluster-to 2846540d8284538096f111a8ce7cf01c50199237 --cluster-slots 1024 --cluster-yes ``` reshard子命令前面已经介绍过了,这里需要注意的一点是,由于我们的集群一共有四个主节点,而每次reshard只能写一个目的节点,因此以上命令需要执行三次(--cluster-to对应不同的目的节点)。 --cluster-yes:不回显需要迁移的slot,直接迁移。 step 3.下线(删除)节点 ``` redis-cli --cluster del-node 127.0.0.1:7008 46f0b68b3f605b3369d3843a89a2b4a164ed21e8 ``` # 6.故障发现 Redis集群内节点通过ping/pong消息实现节点通信,消息不但可以传播节点槽信息,还可以传播其他状态如:主从状态、节点故障等。因此故障发现也是通过消息传播机制实现的,主要环节包括:主观下线(pfail)和客观下线(fail)。 - 通过ping/pong消息实现故障发现:不需要Sentinel - 跟Sentinel一样,有主观下线和客观下线 ## 6.1.主观下线 主观下线指某个节点认为另一个节点不可用,即下线状态,这个状态并不是最终的故障判定,只能代表一个节点的意见,可能存在误判情况。Redis集群对于节点最终是否故障需要与集群中的其他主节点功能协商,多个节点协作完成故障发现的过程叫做客观下线。  ## 6.2.客观下线 客观下线指标记一个节点真正的下线,集群内多个节点都认为该节点不可用,从而达成共识的结果。如果是持有槽的主节点故障,需要为该节点进行故障转移。  ## 6.3.尝试客观下线 如果在cluster-node-time*2时间内无法收集到一半以上槽节点的下线报告,那么之前的下线报告将会过期,也就是说主观下线上报的速度追赶不上下线报告过期的速度,那么故障节点将永远无法被标记为客观下线从而导致故障转移失败。因此不建议将cluster-node-time设置得过小。 向集群广播下线节点的fail消息: - 通知集群内所有的节点标记故障节点为客观下线状态并立刻生效。 - 通知故障节点的从节点触发故障转移流程。  # 6.4.故障恢复 故障节点变为客观下线后,如果下线节点是持有槽的主节点则需要在它的从节点中选出一个新的主节点, 从而保证集群的高可用。下线主节点的所有从节点承担故障恢复的义务,当从节点通过内部定时任务发现自身复制的主节点进入客观下线时,将会触发故障恢复流程 (1)资格检查 - 每个从节点检查与故障主节点的断线时间 - 超过 cluster-node-timeout * cluster-slave-validity-factor 取消资格。 - cluster-slave-validity-factor : 默认是10 (2)准备选举时间 计算下线主节点所有从节点的优先级排名(复制偏移量越大优先级越高),排名高的从节点优选获得选举权,排名每降低一位则延迟1秒开始选举。  (3)选举投票 当从节点定时任务检测到达故障选举时间到达后,发起选举流。在集群内广播选举消息(FAILOVER_AUTH_REQUEST),并记录已发送过消息的状态,保证该从节点在一个配置纪元内只能发起一次选举。  (4)替换主节点 只有持有槽的主节点才会处理故障选举消息(FAILOVER_AUTH_REQUEST),因为每个持有槽的节点在一个配置纪元内都有唯一的一张选票,当接到第一个请求投票的从节点消息时回复FAILOVER_AUTH_ACK消息作为投票,之后相同配置纪元内其他从节点的发送过来的选举消息将忽略。offset越大的从节点越先发送选举消息,可以降低主从复制数据的丢失的数据量 - 如集群内有N个持有槽的主节点代表有N张选票。由于在每个配置纪元内持有槽的主节点只能投票给一个从节点,因此只能有一个从节点获得N/2+1超过半数的选票,保证能够找出唯一的从节点。 - 投票作废:每个配置纪元代表了一次选举周期,如果在开始投票之后的cluster-node-timeout*2时间内没有一个从节点获取足够数量的投票,则本次选举作废。从节点对配置纪元自增+1并发起下一轮投票,直到选举成功为止。 - 当前从节点取消复制变为主节点(slaveof no one) - 执行clusterDelSlot撤销故障主节点负责的槽,并执行clusterAddSlot把这些槽分配给自己。 - 向集群广播自己的pong消息,表明已经替换了故障从节点  ## 6.5.故障演练  具体步骤 1. 执行kill -9 节点模拟拖机 2. 观察客户端故障恢复时间 3. 观察各个节点的日志 # 7.客户端请求路由原理 ## 7.1.1. 请求重定向 在集群模式下,Redis接收任何键相关命令时首先计算键对应的槽,再根据槽找出所对应的节点,如果节点是自身,则处理键命令;否则回复MOVED重定向错误,通知客户端请求正确的节点。这个过程称为MOVED重定向。  ``` 如果key经过计算后,其分配的slot就在当前节点,那么可以请求成功,否则,回复重定向消息 [root@redis-master config]# redis-cli -p 7000 127.0.0.1:7000> set php best (error) MOVED 9244 127.0.0.1:7001 127.0.0.1:7000> ``` 重定向信息包含了键所对应的槽以及负责该槽的节点地址,根据这些信息客户端就可以向正确的节点发起请求。在10.0.0.101:6379节点上成功执行之前的命令: ``` [root@redis-master config]# redis-cli -p 7001 127.0.0.1:7001> set php best OK 127.0.0.1:7001> ``` 使用redis-cli命令时,可以加入-c参数支持自动重定向,简化手动发起重定向的操作: ``` [root@redis-master config]# redis-cli -c -p 7000 127.0.0.1:7000> set oracle dba -> Redirected to slot [7175] located at 127.0.0.1:7001 OK 127.0.0.1:7001> ``` redis-cli自动帮我们连接到正确的节点执行命令,这个过程是在redis-cli内部维护,实质上是client端接到MOVED信息指定的节点之后再次发起请求,并不是在当前Redis节点中完成请求转发,节点对于不属于它的键命令只回复重定向响应,并不负责转发。 键命令执行步骤主要分两步: 1. 计算槽 Redis首先需要计算键所对应的槽,根据键的有效部分使用CRC16函数计算出散列值,再取对16383的余数,得到槽的编号,这样每个键都可以映射到0~16383槽范围内 ``` [root@redis-master config]# redis-cli -c -p 8000 127.0.0.1:8000> cluster keyslot php (integer) 9244 127.0.0.1:8000> ``` Redis集群相对单机在功能上存在一些限制,限制如下: - key批量操作支持有限,如mset、mget,目前只支持具有相同slot值的key执行批量操作。对于映射为不同slot值的key由于执行mget、mget等操作可能存在于多个节点上因此不被支持 - key事务操作支持有限,同理只支持多key在同一节点上的事务操作,当多个key分布在不同的节点上时无法使用事务功能 - key作为数据分区的最小粒度,因此不能将一个大的键值对象如hash、list等映射到不同的节点 - 不支持多数据库空间,单机下的Redis可以支持16个数据库,集群模式下只能使用一个数据库空间,即db0 - 复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构 ``` 127.0.0.1:7000> mget name php (error) CROSSSLOT Keys in request don't hash to the same slot 127.0.0.1:7000> ``` 但通常会有这样的需求,例如把一个用户的信息存入到一个slot中,这是可以这样设置: ``` 127.0.0.1:7000> set user:{user1}:name tony -> Redirected to slot [8106] located at 127.0.0.1:7001 OK 127.0.0.1:7001> set user:{user1}:age 20 OK 127.0.0.1:7001> cluster keyslot user:{user1}:name (integer) 8106 127.0.0.1:7001> cluster keyslot user:{user1}:age (integer) 8106 127.0.0.1:7001> mget user:{user1}:name user:{user1}:age 1) "tony" 2) "20" 127.0.0.1:7001> ``` 这样,这两个key在计算hash值的时候,不会根据整个key来计算,而是只是拿{}中的内容的来计算,这样它们的hash值一定是相同的,就可以分配到同一个slot中,{}中的内容称为hash_tag 2. 查找槽所对应的节点 Redis计算得到键对应的槽后,需要查找槽所对应的节点。集群内通过消息交换每个节点都会知道所有节点的槽信息。 根据MOVED重定向机制,客户端可以随机连接集群内任一Redis获取键所在节点,这种客户端又叫Dummy(傀儡)客户端,它优点是代码实现简单,对客户端协议影响较小,只需要根据重定向信息再次发送请求即可。但是它的弊端很明显,每次执行键命令前都要到Redis上进行重定向才能找到要执行命令的节点,额外增加了IO开销,这不是Redis集群高效的使用方式。正因为如此通常集群客户端都采用另一种实现:Smart客户端 ## 7.2.ask重定向 当slot对应的数据从源节点到目标节点迁移过程中,客户端需要做到智能识别,保证键命令可正常执行。例如当一个slot数据从源节点迁移到目标节点时,期间可能出现一部分数据在源节点,而另一部分在目标节点,当出现上述情况时,客户端键命令执行流程将发生变化。   1. 客户端根据本地slots缓存发送命令到源节点,如果存在键对象则直接执行并返回结果给客户端 2. 如果键对象不存在,则可能存在于目标节点,这时源节点会回复ASK重定向异常。格式如下: ``` (error) ASK {slot} {targetIP}:{targetPort} ``` 3. 客户端收到重定向指令之后,先去目标节点执行一个不带参数的asking指令,然后在目标节点执行原来的操作请求指令 4. 客户端先执行一个不带参数的asking指令的原因是,在迁移完成之前,按道理来说,这个slot还是不属于目标节点的,于是目标节点会给客户端返回-MOVED指令,让客户端去原节点执行操作,这样就形成了"互相推托"的重定向循环。 5. asking指令就是告诉目标节点,"我的指令你必须处理,请求的slot就当成是你的吧" 6. 如果数据在目标节点存在则执行命令,不存在则返回不存在信息 7. 迁移会影响服务效率,在正常情况下,一次请求就可以完成操作,而在迁移过程中,客户端需要请求3次(发送给原节点、发送给目标节点asking指令,发送给目标节点真正的处理请求) ASK与MOVED虽然都是对客户端的重定向控制,但是有着本质区别,ASK重定向说明集群正在进行slot数据迁移,客户端无法知道什么时候迁移完成,因此只能是临时性的重定向,客户端不会更新slots缓存,但是MOVED重定向说明键对应的槽已经明确指定到新的节点,因此需要更新slots缓存 **slot迁移感知** 如果某个slot已经迁移完了,那么客户端如何才能感知到slot的变化呢? 客户端保存了slot和节点的映射关系表,它需要得到及时更新,才可以正常的将请求发送到正确的节点上 - Redis Cluster是去中心化的,客户端只要访问其中的一个节点就可以,其他的节点通过访问的这个节点来获取,当然也可以同时提供多个节点,这样安全性会更好,如果只提供一个地址,那么当这个节点挂了,客户就必须更换访问地址才可以继续访问Cluster - 当客户端发送一个请求到节点1,节点发现这个slot并不是由自己管理,于是给客户端返回-MOVED 3999 127.0.0.1:6381响应,3999是客户端要访问的key的slot的编号,后面就是该slot所在的目标节点,-MOVED中的减号则是代表客户端的这个请求是错误的,这样就告诉了客户端:"slot不再我这儿,你去127.0.0.1:6381找3999号slot" - 于是客户端根据-MOVED的提示,将请求打到真正的目标节点,此时如果目标节点正在迁移过程中,请求又会被转到原先slot所在的旧节点,如果旧节点有数据,那么就直接返回结果了,如果旧节点中没有此条数据,那么旧节点又会让客户端去目标节点拿数据,这时候旧节点返回给客户端一个asking error响应并携带着目标节点的地址,客户端又一次跑到目标节点那里去尝试获取数据,在这样的过程中,客户端不会刷新自己的槽位映射表,因为这只是临时的纠正槽位信息。 - 这样的过程不会无限制的循环下去,用户可以通过参数指定重试的次数,当超过这个次数还没有获取到数据,就会抛出异常 客户端在redis集群中请求正在迁移的slot的流程:  ### 7.3.smart客端端原理:追求性能 smart客户端:该客户端就是为了改善集群模式客户端(redis-cli -h host -p port -c)可能会因为频繁的moved和ask重定向而导致的性能浪费。  smart客户端JedisCluster简单原理介绍: 1. 从集群中选取一个可执行的节点,使用cluster slots命令获取到每个节点和其负责的槽位的关系映射,比如下图,JedisCluster也会通过类似方式,在JedisCluster创建并初始化时,会自动进行该步骤,并且将每个节点和其负责的槽位的关系映射保存在缓存中 2. 每个节点和其负责的槽位的关系映射后,为每一个节点创建一个对应的JedisPool,每当有命令传来时就从该连接池中获取一个Jedis对象,执行完命令后在将该Jedis对象还给JedisPool 3. 准备执行命令,JedisCluster中保存着节点和其负责的槽位的关系映射map,在执行命令时,通过key可以获取到其所在的槽位slot,然后依据map就可以获取到对应的节点,然后找到该节点的JedisPool获取一个Jedis对象,发送并执行命令 命令执行时的问题:如果说服务端手动进行了一些改动,比如节点迁移,而JedisCluster却未刷新缓存中的每个节点和其负责的槽位的关系映射,那么当再次执行命令时,就有可能出现连接错误,就会发生并返回ask或moved(一般都是moved)异常,此时JedisCluster客户端就会自动的刷新缓存中的节点和其负责的槽位的关系映射,然后重新发送命令执行,但命令发送次数有限制,多次失败之后就会抛出错误 ## 7.3.1.多节点命令实现  ### 7.3.2.批量命令实现     # 8.常见问题 ## 8.1.集群完整性 cluster-require-full-coverage默认为yes。 该参数需要16384个slot都正常的时候才能对外提供服务,换句话说,只要任何一个slot异常那么整个cluster不对外提供服务。 - 为了保证集群完整性,默认情况下当集群16384个槽任何一个没有指派到节点时整个集群不可用。执行任何键命令返回(error)CLUSTERDOWN Hash slot not served错误 - 这是对集群完整性的一种保护措施,保证所有的槽都指派给在线的节点。但是当持有槽的主节点下线时,从故障发现到自动完成转移期间整个集群是不可用状态,对于大多数业务无法容忍这种情况, 因此建议将参数cluster-require-full-coverage配置为no,当主节点故障时只影响它负责槽的相关命令执行,不会影响其他主节点的可用性 大多数业务无法容忍, cluster-require-full-coverage建议设置为no ## 8.2.带宽消耗  集群内Gossip消息通信本身会消耗带宽,官方建议集群最大规模在1000以内,也是出于对消息通信成本的考虑,因此单集群不适合部署超大规模的节点 集群内所有节点通过ping/pong消息彼此交换信息,节点间消息通信对带宽的消耗体现在以下几个方面: - 消息发送频率:跟cluster-node-timeout密切相关,当节点发现与其他节 点最后通信时间超过cluster-node-timeout/2时会直接发送ping消息 - 消息数据量:每个消息主要的数据占用包含:slots槽数组(2KB空 间)和整个集群1/10的状态数据(10个节点状态数据约1KB) - 节点部署的机器规模:机器带宽的上线是固定的,因此相同规模的集 群分布的机器越多每台机器划分的节点越均匀,则集群内整体的可用带宽越高 例如,一个总节点数为200的Redis集群,部署在20台物理机上每台划分 10个节点,cluster-node-timeout采用默认15秒,这时ping/pong消息占用带宽 达到25Mb。如果把cluster-node-timeout设为20,对带宽的消耗降低到15Mb以 下 集群带宽消耗主要分为:读写命令消耗+Gossip消息消耗。因此搭建Redis集群时需要根据业务数据规模和消息通信成本做出合理规划: 1)在满足业务需要的情况下尽量避免大集群。同一个系统可以针对不 同业务场景拆分使用多套集群。这样每个集群既满足伸缩性和故障转移要求,还可以规避大规模集群的弊端。如笔者维护的一个推荐系统,根据数据 特征使用了5个Redis集群,每个集群节点规模控制在100以内 2)适度提高cluster-node-timeout降低消息发送频率,同时cluster-nodetimeout还影响故障转移的速度,因此需要根据自身业务场景兼顾二者的平衡 3)如果条件允许集群尽量均匀部署在更多机器上。避免集中部署,如集群有60个节点,集中部署在3台机器上每台部署20个节点,这时机器带宽 消耗将非常严重 ## 8.3.Pub/Sub广播 Redis在2.0版本提供了Pub/Sub(发布/订阅)功能,用于针对频道实现 消息的发布和订阅。但是在集群模式下内部实现对所有的publish命令都会向 所有的节点进行广播,造成每条publish数据都会在集群内所有节点传播一 次,加重带宽负担 1)对集群所有主从节点执行subscribe命令订阅cluster_pub_spread频 道,用于验证集群是否广播消息: ``` redis-cli -p 6379 subscribe cluster_pub_spread redis-cli -p 6380 subscribe cluster_pub_spread redis-cli -p 6382 subscribe cluster_pub_spread redis-cli -p 6383 subscribe cluster_pub_spread redis-cli -p 6385 subscribe cluster_pub_spread redis-cli -p 6386 subscribe cluster_pub_spread ``` 2)在6379节点上发布频道为cluster_pub_spread的消息: ``` redis-cli -p 6379 publish cluster_pub_spread message_body_1 ``` 3)集群内所有的节点订阅客户端全部收到了消息: 针对集群模式下publish广播问题,需要引起开发人员注意,当频繁应用 Pub/Sub功能时应该避免在大量节点的集群内使用,否则会严重消耗集群内网络带宽。针对这种情况建议使用sentinel结构专门用于Pub/Sub功能,从而规避这一问题 ## 8.4.数据倾斜 集群倾斜指不同节点之间数据量和请求量出现明显差异,这种情况将加大负载均衡和开发运维的难度。 ### 8.4.1.数据倾斜 数据倾斜主要分为以下几种: - 节点和槽分配严重不均 - 不同槽对应键数量差异过大 - 集合对象包含大量元素 - 内存相关配置不一致 1)节点和槽分配严重不均。针对每个节点分配的槽不均的情况,可以使用redis-cli进行定位,命令如下: ``` #redis-cli --cluster info 127.0.0.1:6379 127.0.0.1:6379 (cfb28ef1...) -> 33348 keys | 5461 slots | 1 slaves. 127.0.0.1:6380 (8e41673d...) -> 33391 keys | 5461 slots | 1 slaves. 127.0.0.1:6386 (475528b1...) -> 33263 keys | 5462 slots | 1 slaves. [OK] 100002 keys in 3 masters. 6.10 keys per slot on average. ``` 以上信息列举出每个节点负责的槽和键总量以及每个槽平均键数量。当节点对应槽数量不均匀时,可以使用redis --cluster rebalance命令进行平衡: ``` #redis-cli --cluster rebalance 127.0.0.1:6379 ... [OK] All 16384 slots covered. *** No rebalancing needed! All nodes are within the 2.0% threshold. ``` 2)不同槽对应键数量差异过大。键通过CRC16哈希函数映射到槽上, 正常情况下槽内键数量会相对均匀。但当大量使用hash_tag时,会产生不同 的键映射到同一个槽的情况。特别是选择作为hash_tag的数据离散度较差时,将加速槽内键数量倾斜情况。通过命令:cluster countkeysinslot{slot}可 以获取槽对应的键数量,识别出哪些槽映射了过多的键。再通过命令cluster getkeysinslot{slot}{count}循环迭代出槽下所有的键。从而发现过度使用 hash_tag的键。 3)集合对象包含大量元素。对于大集合对象的识别可以使用redis-cli-- bigkeys命令识别,具体使用见后面“寻找热点key”的文章。找出大集合之后可以根据业务场景进 行拆分。同时集群槽数据迁移是对键执行migrate操作完成,过大的键集合如 几百兆,容易造成migrate命令超时导致数据迁移失败 4)内存相关配置不一致。内存相关配置指hash-max-ziplist-value、setmax-intset-entries等压缩数据结构配置。当集群大量使用hash、set等数据结构 时,如果内存压缩数据结构配置不一致,极端情况下会相差数倍的内存,从而造成节点内存量倾斜 ### 8.4.2.请求倾斜 集群内特定节点请求量/流量过大将导致节点之间负载不均,影响集群均衡和运维成本。常出现在热点键场景,当键命令消耗较低时如小对象的 get、set、incr等,即使请求量差异较大一般也不会产生负载严重不均。但是当热点键对应高算法复杂度的命令或者是大对象操作如hgetall、smembers等,会导致对应节点负载过高的情况 避免方式如下: 1)合理设计键,热点大集合对象做拆分或使用hmget替代hgetall避免整 体读取 2)不要使用热键作为hash_tag,避免映射到同一槽 3)对于一致性要求不高的场景,客户端可使用本地缓存减少热键调 用 ## 8.5.读写分离 ### 8.5.1.只读连接 集群模式下从节点不接受任何读写请求,发送过来的键命令会重定向到负责槽的主节点上(其中包括它的主节点) readonly命令(只读模式): - 当需要使用从节点分担主节点读压力时,可以使用readonly命令打开客户端连接只读状态。之前的复制配置slave-read-only在集群模式下无效 - 当开启只读状态时,从节点接收读命令处理流程变为:如果对应的槽属于自己正在复制的主节点则直接执行读命令,否则返回重定向信息 readonly命令是连接级别生效,因此每次新建连接时都需要执行readonly开启只读状态。执行readwrite命令可以关闭连接只读状态 ### 8.5.2.读写分离 集群模式下的读写分离,同样会遇到:复制延迟,读取过期数据,从节点故障等问题。 针对从节点故障问题,客户端需要维护可用节点列表,集群提供了cluster slaves {nodeId}命令,返回nodeId对应主节点下所有从节点信息,数据格式同cluster nodes,命令如下: 解析以上从节点列表信息,排除fail状态节点,这样客户端对从节点的故障判定可以委托给集群处理,简化维护可用从节点列表难度。 开发提示:集群模式下读写分离涉及对客户端修改如下: 1)维护每个主节点可用从节点列表 2)针对读命令维护请求节点路由 3)从节点新建连接开启readonly状态 集群模式下读写分离成本比较高,可以直接扩展主节点数量提高集群性能,一般不建议集群模式下做读写分离 集群读写分离有时用于特殊业务场景如: 1)利用复制的最终一致性使用多个从节点做跨机房部署降低读命令网络延迟 2)主节点故障转移时间过长,业务端把读请求路由给从节点保证读操作可用 以上场景也可以在不同机房独立部署Redis集群解决,通过客户端多写来维护,读命令直接请求到最近机房的Redis集群,或者当一个集群节点故 障时客户端转向另一个集群 ## 8.6.数据迁移 应用Redis集群时,常需要把单机Redis数据迁移到集群环境,redis-cli --cluster命令提供了导入功能,用于数据从单机向集群环境迁移的场景,命令如下: ``` redis-cli import host:port --cluster-from <arg> --cluster-copy --cluster-replace ``` 上面的命令内部采用批量scan和migrate的方式迁移数据。这种迁移方式存在以下缺点: 1)迁移只能从单机节点向集群环境导入数据 2)不支持在线迁移数据,迁移数据时应用方必须停写,无法平滑迁移数据 3)迁移过程中途如果出现超时等错误,不支持断点续传只能重新全量导入 4)使用单线程进行数据迁移,大数据量迁移速度过慢 正因为这些问题,社区开源了很多迁移工具,唯品会开发的redis-migrate-tool,豌豆荚 : redis-port,该工具可满足大多数Redis迁移需求,特点如下: - 支持单机、Twemproxy、Redis Cluster、RDB/AOF等多种类型的数据迁移 - 工具模拟成从节点基于复制流迁移数据,从而支持在线迁移数据,业务方不需要停写 - 采用多线程加速数据迁移过程且提供数据校验和查看迁移状态等功能,GitHub:https://github.com/vipshop/redis-migrate-tool
Seven
Sept. 17, 2022, 9:03 p.m.
转发文档
Collection documents
Last
Next
手机扫码
Copy link
手机扫一扫转发分享
Copy link
Markdown文件
share
link
type
password
Update password