redis cluster集群部署方案

什么是 Redis 集群

    

    Redis 集群是一个分布式(distributed)、容错(fault-tolerant)的 Redis 实现,集群可以使用的功能是普通单机 Redis 所能使用的功能的一个子集(subset)。

Redis 集群中不存在中心(central)节点或者代理(proxy)节点,集群的其中一个主要设计目标是达到线性可扩展性(linear scalability)。

Redis 集群为了保证一致性(consistency)而牺牲了一部分容错性:系统会在保证对网络断线(net split)和节点失效(node failure)具有有限(limited)抵抗力的前提下,尽可能地保持数据的一致性。

请注意,本教程使用于Redis3.0(包括3.0)以上版本

如果你计划部署集群,那么我们建议你从阅读这个文档开始。

Redis集群介绍

    Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。

    Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误.

    Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令. Redis 集群的优势:

    自动分割数据到不同的节点上。
    整个集群的部分节点失败或者不可达的情况下能够继续处理命令。

Redis 集群的数据分片
Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念.

Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:

    节点 A 包含 0 到 5500号哈希槽.
    节点 B 包含5501 到 11000 号哈希槽.
    节点 C 包含11001 到 16384号哈希槽.

    这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我像移除节点A,需要将A中得槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.
Redis 集群的主从复制模型

    为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品.

    在我们例子中具有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用.

    然而如果在集群创建的时候(或者过一段时间)我们为每个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了

不过当B和B1 都失败后,集群是不可用的.

 

    Redis 一致性保证
    Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作.

第一个原因是因为集群是用了异步复制. 写操作过程:

    客户端向主节点B写入一条命令.
    主节点B向客户端回复命令状态.
    主节点将写操作复制给他得从节点 B1, B2 和 B3.

    主节点对命令的复制工作发生在返回命令回复之后, 因为如果每次处理命令请求都需要等待复制操作完成的话, 那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。 注意:Redis 集群可能会在将来提供同步写的方法。 Redis 集群另外一种可能会丢失命令的情况是集群出现了网络分区, 并且一个客户端与至少包括一个主节点在内的少数实例被孤立。

    举个例子 假设集群包含 A 、 B 、 C 、 A1 、 B1 、 C1 六个节点, 其中 A 、B 、C 为主节点, A1 、B1 、C1 为A,B,C的从节点, 还有一个客户端 Z1 假设集群中发生网络分区,那么集群可能会分为两方,大部分的一方包含节点 A 、C 、A1 、B1 和 C1 ,小部分的一方则包含节点 B 和客户端 Z1 .

    Z1仍然能够向主节点B中写入, 如果网络分区发生时间较短,那么集群将会继续正常运作,如果分区的时间足够让大部分的一方将B1选举为新的master,那么Z1写入B中得数据便丢失了.

    注意, 在网络分裂出现期间, 客户端 Z1 可以向主节点 B 发送写命令的最大时间是有限制的, 这一时间限制称为节点超时时间(node timeout), 是 Redis 集群的一个重要的配置选项:
搭建并使用Redis集群

    搭建集群的第一件事情我们需要一些运行在 集群模式的Redis实例. 这意味这集群并不是由一些普通的Redis实例组成的,集群模式需要通过配置启用,开启集群模式后的Redis实例便可以使用集群特有的命令和特性了.

目前redis支持的cluster特性

1):节点自动发现
2):slave->master 选举,集群容错
3):Hot resharding:在线分片
4):进群管理:cluster xxx
5):基于配置(nodes-port.conf)的集群管理
6):ASK 转向/MOVED 转向机制.

1)redis-cluster架构图

 

 

 

架构细节:

(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
(2)节点的fail是通过集群中超过半数的节点检测失效时才生效.
(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value2) redis-cluster选举:容错

 

 

 

(1)领着选举过程是集群中所有master参与,如果半数以上master节点与master节点通信超过(cluster-  node-timeout),认为当前master节点挂掉.
(2):什么时候整个集群不可用(cluster_state:fail),当集群不可用时,所有对集群的操作做都不可用,收到      ((error) CLUSTERDOWN The cluster is down)错误
    a:如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成进群的slot映射          [0-16383]不完成时进入fail状态.
    b:如果进群超过半数以上master挂掉,无论是否有slave集群进入fail状态.

 

一、环境

os:centos7
    ip:192.168.19.132
    redis:3.2.9
    gem-redis:3.2.2
 

二、搭建集群

1、本机下载redis-3.2.9.tar.gz

[root@zookeeper ~]# cd /usr/local/src/
[root@zookeeper src]# wget http://download.redis.io/releases/redis-3.2.9.tar.gz
 

2、安装

root@zookeeper ~]# yum -y install tcl-8.5* 
[root@zookeeper src]# tar zxf redis-3.2.9.tar.gz -C /usr/local/
[root@zookeeper src]# ln -s /usr/local/redis-3.2.9 /usr/local/redis
[root@zookeeper src]# cd /usr/local/redis
第一种:
[root@zookeeper redis]# make MALLOC=libc && make install
[root@zookeeper redis]# make test(可选,等待时间长)
......
\o/ All tests passed without errors!

Cleanup: may take some time... OK
make[1]: Leaving directory `/usr/local/redis-3.2.9/src'


第二种make:

make完成之后,进行install,默认安装路径为/usr/local/bin下,这里我们把他安装目录放到/usr/local/redis下,使用PREFIX指定目录:
[root@zookeeper redis]# make && make PREFIX=/usr/local/redis install
 

将redis可执行目录添加到环境变量中,编辑~/.bash_profile添加redis环境变量:

[root@zookeeper ~]# vim ~/.bash_profile
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin:/usr/local/redis/bin

export PATH

[root@zookeeper ~]# source ~/.bash_profile
 

3、创建文件夹

[root@zookeeper redis]#    mkdir -p /data/cluster
[root@zookeeper redis]# cd /data/cluster/
[root@zookeeper cluster]# mkdir 7000 7001 7002 7003 7004 7005
 

4、拷贝修改配置文件

[root@zookeeper cluster]# mkdir /var/log/redis/

[root@zookeeper cluster]# cp /usr/local/redis/redis.conf 7000/redis-7000.conf

vim 7000/redis-7000.conf
......
    bind 192.168.19.132
    port 7000
    daemonize yes
    cluster-enabled yes
    cluster-config-file nodes-7000.conf
    cluster-node-timeout 15000
    pidfile /var/run/redis_7000.pid
    logfile "/var/log/redis/redis-7000.log"


改完之后将此配置文件cp到所有节点上,并且修改端口号和cluster-config-file项。
[root@zookeeper cluster]# cp 7000/redis-7000.conf 7001/redis-7001.conf 
[root@zookeeper cluster]# cp 7000/redis-7000.conf 7002/redis-7002.conf 
[root@zookeeper cluster]# cp 7000/redis-7000.conf 7003/redis-7003.conf 
[root@zookeeper cluster]# cp 7000/redis-7000.conf 7004/redis-7004.conf 
[root@zookeeper cluster]# cp 7000/redis-7000.conf 7005/redis-7005.conf 
......(修改配置文件请参考redis-7000.conf)
    [root@zookeeper cluster]# vim 7001/redis-7001.conf 
    [root@zookeeper cluster]# vim 7002/redis-7002.conf 
    [root@zookeeper cluster]# vim 7003/redis-7003.conf 
    [root@zookeeper cluster]# vim 7004/redis-7004.conf 
    [root@zookeeper cluster]# vim 7005/redis-7005.conf
 

5、启动6个实例

[root@zookeeper cluster]# redis-server 7000/redis-7000.conf
[root@zookeeper cluster]# redis-server 7001/redis-7001.conf 
[root@zookeeper cluster]# redis-server 7002/redis-7002.conf 
[root@zookeeper cluster]# redis-server 7003/redis-7003.conf 
[root@zookeeper cluster]# redis-server 7004/redis-7004.conf 
[root@zookeeper cluster]# redis-server 7005/redis-7005.conf 
[root@zookeeper cluster]# 
关闭命令:[root@zookeeper cluster]# redis-cli -p 端口号 shutdown
 

用redis-cli -c -h -p命令登录
-c是以集群方式登录;
-h后跟主机号 ;
-p后跟端口号。
绑定了127.0.0.1则可以省略-h参数。不加-c则客户端不自动切换。

例如:客户端登录7000端口的,设置的数据应该存放在7001上则会报错请转到7001。而加上-c启动则会自动切换到7001客户端保存。

6、查看redis进程启动状态

[root@zookeeper cluster]# ps -ef| grep redis
root      18839      1  0 22:58 ?        00:00:00 redis-server 192.168.19.132:7000 [cluster]
root      18843      1  0 22:58 ?        00:00:00 redis-server 192.168.19.132:7001 [cluster]
root      18847      1  0 22:58 ?        00:00:00 redis-server 192.168.19.132:7002 [cluster]
root      18851      1  0 22:59 ?        00:00:00 redis-server 192.168.19.132:7003 [cluster]
root      18855      1  0 22:59 ?        00:00:00 redis-server 192.168.19.132:7004 [cluster]
root      18859      1  0 22:59 ?        00:00:00 redis-server 192.168.19.132:7005 [cluster]
root      18865   2891  0 22:59 pts/1    00:00:00 grep --color=auto redis
[root@zookeeper cluster]#
 

7、部署集群

    7.1、安装ruby依赖,返回安装软件目录

root@zookeeper src]# yum install ruby rubygems -y
[root@zookeeper src]# wget https://rubygems.org/downloads/redis-3.2.2.gem
安装集群管理工具
Redis作者应该是个Ruby爱好者,Ruby客户端就是他开发的。这次集群的管理功能没有嵌入到Redis代码中,于是作者又顺手写了个叫做redis-trib的管理脚本。redis-trib依赖Ruby和RubyGems,以及redis扩展。可以先用which命令查看是否已安装ruby和rubygems,用gem list –local查看本地是否已安装redis扩展。
[root@zookeeper src]# gem install -l redis-3.2.2.gem 
Successfully installed redis-3.2.2
Parsing documentation for redis-3.2.2
Installing ri documentation for redis-3.2.2
1 gem installed
 

    7.2、将集群管理程序复制到/usr/local/bin/

 [root@zookeeper src]# cp /usr/local/redis/src/redis-trib.rb /usr/local/bin/redis-trib
 

        可以看到redis-trib.rb具有以下功能:
        1、create:创建集群
        2、check:检查集群
        3、info:查看集群信息
        4、fix:修复集群
        5、reshard:在线迁移slot
        6、rebalance:平衡集群节点slot数量
        7、add-node:将新节点加入集群
        8、del-node:从集群中删除节点
        9、set-timeout:设置集群节点间心跳连接的超时时间
        10、call:在集群全部节点上执行命令
        11、import:将外部redis数据导入集群

 

8、创建集群

[root@zookeeper cluster]# redis-trib create --replicas 1 192.168.19.132:7000 192.168.19.132:7001 192.168.19.132:7002 192.168.19.132:7003 192.168.19.132:7004 192.168.19.132:7005

>>> Creating cluster
>>> Performing hash slots allocation on 7 nodes...
Using 3 masters:
192.168.19.132:7000
192.168.19.132:7001
192.168.19.132:7002
Adding replica 192.168.19.132:7003 to 192.168.19.132:7000
Adding replica 192.168.19.132:7004 to 192.168.19.132:7001
Adding replica 192.168.19.132:7005 to 192.168.19.132:7002
M: 3546a9930ce08543731c4d49ae8609d75b0b8193 192.168.19.132:7000
   slots:0-16383 (16384 slots) master
M: 1dd532b0f41b98574b6cd355fa58a2773c9da8fe 192.168.19.132:7001
   slots:5461-10922 (5462 slots) master
M: 2900e315a4a01df8609eafe0f9fd2a1d779ecc69 192.168.19.132:7002
   slots:10923-16383 (5461 slots) master
S: 71c8cea8e3e9c913eb7c09bd3f95c03985938eca 192.168.19.132:7003
   replicates 3546a9930ce08543731c4d49ae8609d75b0b8193
S: 046a02ea253d8912b87c13e98b28f81e6c54c0b1 192.168.19.132:7004
   replicates 1dd532b0f41b98574b6cd355fa58a2773c9da8fe
S: 8a666ed58930673b7dfc6d005c2a937751350f77 192.168.19.132:7005
   replicates 2900e315a4a01df8609eafe0f9fd2a1d779ecc69
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 192.168.19.132:7000)
M: 3da69162cde5884f21cec07f6f812ffbdda0cfc4 192.168.19.132:7000
   slots:0-10922 (10923 slots) master
   3 additional replica(s)
M: d30be1d1232e55f3cc69d8d11e9eb9a870160ac1 192.168.19.132:7001
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 6bd6589a69ce37da5335ffd10b042ce0b02e3247 192.168.19.132:7004
   slots: (0 slots) slave
   replicates d30be1d1232e55f3cc69d8d11e9eb9a870160ac1
S: 12d7db519133b96bac51b79204f69eabdfe75627 192.168.19.132:7002
   slots: (0 slots) slave
   replicates 3da69162cde5884f21cec07f6f812ffbdda0cfc4
S: 8a9d6189b42bef127ab388e221d8225938c3f038 192.168.19.132:7003
   slots: (0 slots) slave
   replicates 3da69162cde5884f21cec07f6f812ffbdda0cfc4
S: 2cfb927fc17988be6fee6b5eb1249e2789a76f82 192.168.19.132:7005
   slots: (0 slots) slave
   replicates 3da69162cde5884f21cec07f6f812ffbdda0cfc4
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
 


    当我们同意这份计划之后输入yes,,Can I set the above configuration? (type 'yes' to accept): yes,开始执行节点握手和槽分配操作。
最后输出报告说明,16384个槽全部被分配,集群创建成功。这里需要注意给redis-trib.rb的节点地址必须是不包含任何槽/数据的节点,否则会拒绝创建集群。

    --replicascas参数制定集群中每个主节点配置几个从节点,这里设置为1.节点列表顺序用于确定主从角色,先主节点之后是从节点。

创建流程如下:

1、首先为每个节点创建ClusterNode对象,包括连接每个节点。检查每个节点是否为独立且db为空的节点。执行load_info方法导入节点信息。

2、检查传入的master节点数量是否大于等于3个。只有大于3个节点才能组成集群。

3、计算每个master需要分配的slot数量,以及给master分配slave。分配的算法大致如下:
先把节点按照host分类,这样保证master节点能分配到更多的主机中。
不停遍历遍历host列表,从每个host列表中弹出一个节点,放入interleaved数组。直到所有的节点都弹出为止。
master节点列表就是interleaved前面的master数量的节点列表。保存在masters数组。
计算每个master节点负责的slot数量,保存在slots_per_node对象,用slot总数除以master数量取整即可。
遍历masters数组,每个master分配slots_per_node个slot,最后一个master,分配到16384个slot为止。
接下来为master分配slave,分配算法会尽量保证master和slave节点不在同一台主机上。对于分配完指定slave数量的节点,还有多余的节点,也会为这些节点寻找master。分配算法会遍历两次masters数组。
第一次遍历masters数组,在余下的节点列表找到replicas数量个slave。每个slave为第一个和master节点host不一样的节点,如果没有不一样的节点,则直接取出余下列表的第一个节点。
第二次遍历是在对于节点数除以replicas不为整数,则会多余一部分节点。遍历的方式跟第一次一样,只是第一次会一次性给master分配replicas数量个slave,而第二次遍历只分配一个,直到余下的节点被全部分配出去。

4、打印出分配信息,并提示用户输入“yes”确认是否按照打印出来的分配方式创建集群。

5、输入“yes”后,会执行flush_nodes_config操作,该操作执行前面的分配结果,给master分配slot,让slave复制master,对于还没有握手(cluster meet)的节点,slave复制操作无法完成,不过没关系,flush_nodes_config操作出现异常会很快返回,后续握手后会再次执行flush_nodes_config。

6、给每个节点分配epoch,遍历节点,每个节点分配的epoch比之前节点大1。

7、节点间开始相互握手,握手的方式为节点列表的其他节点跟第一个节点握手。

8、然后每隔1秒检查一次各个节点是否已经消息同步完成,使用ClusterNode的get_config_signature方法,检查的算法为获取每个节点cluster nodes信息,排序每个节点,组装成node_id1:slots|node_id2:slot2|...的字符串。如果每个节点获得字符串都相同,即认为握手成功。

9、此后会再执行一次flush_nodes_config,这次主要是为了完成slave复制操作。

10、最后再执行check_cluster,全面检查一次集群状态。包括和前面握手时检查一样的方式再检查一遍。确认没有迁移的节点。确认所有的slot都被分配出去了。

11、至此完成了整个创建流程,返回[OK] All 16384 slots covered.。

9、集群完整性检查

[root@zookeeper ~]# redis-trib check 192.168.19.132:7000
>>> Performing Cluster Check (using node 192.168.19.132:7000)
M: 8a628ee2e98c70a404be020cba3dfc1172a38335 192.168.19.132:7000
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: 154e2f4f3fad75a564f9fe2efcde7820284116c6 192.168.19.132:7003
   slots: (0 slots) slave
   replicates 8a628ee2e98c70a404be020cba3dfc1172a38335
S: f2707a3052d3dc91358b73b4786e4c8e20662a79 192.168.19.132:7004
   slots: (0 slots) slave
   replicates 0e4d1ee05b090c45ce979bc9e8ad4c027d5332ef
M: 0e4d1ee05b090c45ce979bc9e8ad4c027d5332ef 192.168.19.132:7001
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
M: 08d3663dc9e0f5f02e2bff07640d67e406211e49 192.168.19.132:7002
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: a44237119e6b2129e457d2f48a584b94b1b815f5 192.168.19.132:7005
   slots: (0 slots) slave
   replicates 08d3663dc9e0f5f02e2bff07640d67e406211e49
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.


[root@zookeeper ~]# redis-trib check 192.168.19.132:7004
>>> Performing Cluster Check (using node 192.168.19.132:7004)
S: f2707a3052d3dc91358b73b4786e4c8e20662a79 192.168.19.132:7004
   slots: (0 slots) slave
   replicates 0e4d1ee05b090c45ce979bc9e8ad4c027d5332ef
M: 0e4d1ee05b090c45ce979bc9e8ad4c027d5332ef 192.168.19.132:7001
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: 154e2f4f3fad75a564f9fe2efcde7820284116c6 192.168.19.132:7003
   slots: (0 slots) slave
   replicates 8a628ee2e98c70a404be020cba3dfc1172a38335
M: 08d3663dc9e0f5f02e2bff07640d67e406211e49 192.168.19.132:7002
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
M: 8a628ee2e98c70a404be020cba3dfc1172a38335 192.168.19.132:7000
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: a44237119e6b2129e457d2f48a584b94b1b815f5 192.168.19.132:7005
   slots: (0 slots) slave
   replicates 08d3663dc9e0f5f02e2bff07640d67e406211e49
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
 

10 测试

[root@zookeeper ~]# redis-cli -h 192.168.19.132 -p 7000
192.168.19.132:7000> 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:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_sent:414
cluster_stats_messages_received:414


3192.168.19.132:7000> CLUSTER NODES
154e2f4f3fad75a564f9fe2efcde7820284116c6 192.168.19.132:7003 slave 8a628ee2e98c70a404be020cba3dfc1172a38335 0 1496720263710 4 connected
f2707a3052d3dc91358b73b4786e4c8e20662a79 192.168.19.132:7004 slave 0e4d1ee05b090c45ce979bc9e8ad4c027d5332ef 0 1496720264715 5 connected
0e4d1ee05b090c45ce979bc9e8ad4c027d5332ef 192.168.19.132:7001 master - 0 1496720262702 2 connected 5461-10922
08d3663dc9e0f5f02e2bff07640d67e406211e49 192.168.19.132:7002 master - 0 1496720265722 3 connected 10923-16383
a44237119e6b2129e457d2f48a584b94b1b815f5 192.168.19.132:7005 slave 08d3663dc9e0f5f02e2bff07640d67e406211e49 0 1496720266730 6 connected
8a628ee2e98c70a404be020cba3dfc1172a38335 192.168.19.132:7000 myself,master - 0 0 1 connected 0-5460
 

当前集群状态是OK,集群进入在线状态。cluster nodes可以看到节点和槽的分配关系,目前还有三个及诶按没有使用,作为一个完整的集群,每个负责处理槽的节点都应该具有从节点,保证当它出现故障时可以自动进行故障转移。集群模式下,Redis节点角色分为主节点和从节点。首次启动的节点和被分配槽的节点都是主节点,从节点负责复制主节点槽信息和相关数据。

posted @ 2020-05-11 14:23  个性灬浪漫  阅读(740)  评论(0编辑  收藏  举报