Redis集群
前言
Redis 是我们目前大规模使用的缓存中间件,由于它强大高效而又便捷的功能,得到了广泛的使用。单节点的Redis已经就达到了很高的性能,为了提高可用性我们可以使用Redis集群。本文参考了Rdis的官方文档和使用Redis官方提供的Redis Cluster工具搭建Rdis集群。
注意 :Redis的版本要在3.0以上,Redis 集群是3.0之后才引入的,在3.0之前,使用哨兵(sentinel)机制(本文将不做介绍,大家可另行查阅)来监控各个节点之间的状态。Redis 集群可谓是让很多人久等了。
Redis 集群是一组能进行数据共享的Redis 实例(服务或者节点)的设施,集群可以使用的功能是普通单机 Redis 所能使用的功能的一个子集;Redis 集群通常具有高可用、可扩展性、分布式、容错等特性。了解redis的集群后,这些晦涩的概念可结合redis的主从、集群分区和集群运维等角度理解体会。
Redis集群的概念
介绍
Redis 集群是一个可以在多个 Redis 节点之间进行数据共享的设施(installation)。
Redis 集群不支持那些需要同时处理多个键的 Redis 命令, 因为执行这些命令需要在多个 Redis 节点之间移动数据, 并且在高负载的情况下, 这些命令将降低 Redis 集群的性能, 并导致不可预测的错误。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
Redis 集群提供了以下两个好处:
- 将数据自动切分(split)到多个节点的能力。
- 当集群中的一部分节点失效或者无法进行通讯时, 仍然可以继续处理命令请求的能力。
数据分片
Redis 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot), 数据库中的每个键都属于这 16384 个哈希槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
集群中的每个节点负责处理一部分哈希槽。 举个例子, 一个集群可以有三个哈希槽, 其中:
- 节点 A 负责处理 0 号至 5500 号哈希槽。
- 节点 B 负责处理 5501 号至 11000 号哈希槽。
- 节点 C 负责处理 11001 号至 16384 号哈希槽。
这种将哈希槽分布到不同节点的做法使得用户可以很容易地向集群中添加或者删除节点。 比如说:
我现在想设置一个key,叫my_name
:
set my_name zhangguoji
按照Redis Cluster的哈希槽算法,CRC16('my_name')%16384 = 2412
那么这个key就被分配到了节点A上
同样的,当我连接(A,B,C)的任意一个节点想获取my_name
这个key,都会转到节点A上
再比如
如果用户将新节点 D 添加到集群中, 那么集群只需要将节点 A 、B 、 C 中的某些槽移动到节点 D 就可以了。
增加一个D节点的结果可能如下:
* 节点A覆盖1365-5460
* 节点B覆盖6827-10922
* 节点C覆盖12288-16383
* 节点D覆盖0-1364,5461-6826,10923-1228
与此类似, 如果用户要从集群中移除节点 A , 那么集群只需要将节点 A 中的所有哈希槽移动到节点 B 和节点 C , 然后再移除空白(不包含任何哈希槽)的节点 A 就可以了。
因为将一个哈希槽从一个节点移动到另一个节点不会造成节点阻塞, 所以无论是添加新节点还是移除已存在节点, 又或者改变某个节点包含的哈希槽数量, 都不会造成集群下线。
所以,Redis Cluster的模型大概是这样的形状
主从复制模型
为了使得集群在一部分节点下线或者无法与集群的大多数(majority)节点进行通讯的情况下, 仍然可以正常运作, Redis 集群对节点使用了主从复制功能: 集群中的每个节点都有 1 个至 N 个复制品(replica), 其中一个复制品为主节点(master), 而其余的 N-1 个复制品为从节点(slave)。
在之前列举的节点 A 、B 、C 的例子中, 如果节点 B 下线了, 那么集群将无法正常运行, 因为集群找不到节点来处理 5501 号至 11000号的哈希槽。
另一方面, 假如在创建集群的时候(或者至少在节点 B 下线之前), 我们为主节点 B 添加了从节点 B1 , 那么当主节点 B 下线的时候, 集群就会将 B1 设置为新的主节点, 并让它代替下线的主节点 B , 继续处理 5501 号至 11000 号的哈希槽, 这样集群就不会因为主节点 B 的下线而无法正常运作了。
不过如果节点 B 和 B1 都下线的话, Redis 集群还是会停止运作。
Redis一致性保证
Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作:
第一个原因是因为集群是用了异步复制. 写操作过程:
1. 客户端向主节点B写入一条命令.
2. 主节点B向客户端回复命令状态.
3. 主节点将写操作复制给他得从节点 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集群
要让集群正常工作至少需要3个主节点,在这里我们要创建6个redis节点,其中三个为主节点,三个为从节点,对应的redis节点的ip和端口对应关系如下(为了简单演示都在同一台机器上面)
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
下载安装包
wget http://download.redis.io/releases/redis-4.0.2.tar.gz
解压安装
tar zxvf redis-4.0.2.tar.gz
cd redis-4.0.2
make && make PREFIX=/usr/local/redis install
或者:
cd redis-4.0.2
make install PREFIX=/usr/local/redis
这里如果失败的自行yum安装gcc和tcl
yum install gcc
yum install tcl
创建目录
cd /usr/local/redis
mkdir cluster
cd cluster
mkdir redis7000 redis7001 redis7002 redis7003 redis7004 redis7005
拷贝修改配置文件
将已有的/usr/local/redis下的redis.conf拷贝到新创建的7000目录中
cp redis.conf /usr/local/cluster/redis7000
修改项如下:
# 端口号
port 7000
# 后台启动
daemonize yes
# 开启集群
cluster-enabled yes
#集群节点配置文件
cluster-config-file nodes-7000.conf
# 集群连接超时时间
cluster-node-timeout 5000
# 进程pid的文件位置
pidfile /var/run/redis-7000.pid
# 开启aof
appendonly yes
# aof文件路径
appendfilename "appendonly-7000.aof"
# rdb文件路径
dbfilename dump-7000.rdb
#指定数据存放路径,
dir /usr/local/redis/cluster/redis7000
上面红色项目最好全部设置,不然会出意想不到的错误,700x最好与节点文件夹保持一致。(6个配置文件按照对应的端口分别修改配置文件)
将redis7000的redis.conf改完后再拷贝到剩下的5个目录中,然后只要全局替换redis.conf中的7000为相应的节点即可。
创建启动脚本
在/usr/local/redis
目录下创建一个startcluster.sh
#!/bin/bash
bin/redis-server cluster/redis7000/redis.conf
bin/redis-server cluster/redis7001/redis.conf
bin/redis-server cluster/redis7002/redis.conf
bin/redis-server cluster/redis7003/redis.conf
bin/redis-server cluster/redis7004/redis.conf
bin/redis-server clusterredis7005/redis.conf
赋予运行权限:
chmod +x ./startcluster.sh
执行脚本进行测试:
./startcluster.sh
有6个redis进程在开启,说明我们的redis就启动成功了
开启集群(需要FQ安装redis接口,ruby环境不需要FQ)
这里我们只是开启了6个redis进程而已,它们都还只是独立的状态,还么有组成集群
这里我们使用官方提供的工具redis-trib,不过这个工具是用ruby写的,要先安装ruby的环境
关于离线搭建集群环境参考我的另一篇博客:http://www.cnblogs.com/qlqwjy/p/8858520.html
注意:
1.我安装的ruby环境是采用tar解压安装(安装ruby运行环境)
参考:http://www.runoob.com/ruby/ruby-installation-unix.html
2.gem redis接口我采用修改gem(中间我还手动安装了zlib 主要是安装这个,这个成功即可进行下面的创建集群,我安装的时候报了很多错。。。。)
(1)gem sources -a https://gems.ruby-china.org/ 把https地址改成
gem sources -a http://gems.ruby-china.org/
(2)有时候失败需要安装zlib
-
-
- 将zlib-1.2.11.tar.gz上传到服务器
- 解压安装
-
mkdir zlib tar -vxf zlib-1.2.11.tar.gz cd zlib-1.2.11/ ./configure --prefix=/usr/local/zlib make make install
-
-
- 编译 ruby 中的zlib
-
进入到/opt/ruby/ruby-2.3.7/ext/zlib目录下:
[root@iz2ze46xi6pjjj69ailg9lz zlib]# ruby ./extconf.rb checking for deflateReset() in -lz... yes checking for zlib.h... yes checking for crc32_combine() in zlib.h... yes checking for adler32_combine() in zlib.h... yes checking for z_crc_t in zlib.h... yes creating Makefile
接着编译安装
make make install
(3)接着继续修改gem镜像地址安装redis接口
- 修改gem镜像
gem sources -a http://gems.ruby-china.org/
- 查看镜像
[root@iz2ze46xi6pjjj69ailg9lz ruby]# gem source -l *** CURRENT SOURCES *** https://rubygems.org/ http://gems.ruby-china.org/
- 离线安装redis接口(前提是下载好redis-3.2.1.gem)
[root@iz2ze46xi6pjjj69ailg9lz ruby]# gem install -l redis-3.2.1.gem #安装本地的redis接口 Successfully installed redis-3.2.1 Parsing documentation for redis-3.2.1 Installing ri documentation for redis-3.2.1 Done installing documentation for redis after 1 seconds 1 gem installed
(4)将redis安装目录下的的src目录下的trib复制到相应文件夹
cp redis-4.0.2/src/redis-trib.rb /usr/local/redis/bin/
3.创建集群
./redis-trib.rb create --replicas 1 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
命令的意义如下:
* 给定 redis-trib.rb 程序的命令是 create , 这表示我们希望创建一个新的集群。
* 选项 –replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。
之后跟着的其他参数则是实例的地址列表, 我们希望程序使用这些地址所指示的实例来创建新集群。
简单来说,以上的命令的意思就是让redis-trib程序帮我们创建三个主节点和三个从节点的集群
接着, redis-trib 会打印出一份预想中的配置给你看, 如果你觉得没问题的话, 就可以输入 yes , redis-trib 就会将这份配置应用到集群当中:
结果:(如果失败可以将各个node的数据库flushdb之后重启集群)
>>> Creating cluster >>> Performing hash slots allocation on 6 nodes... Using 3 masters: 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 Adding replica 127.0.0.1:7003 to 127.0.0.1:7000 Adding replica 127.0.0.1:7004 to 127.0.0.1:7001 Adding replica 127.0.0.1:7005 to 127.0.0.1:7002 M: 4fdc2bd2fecc2a3ce898c657d8dbd378754c87f9 127.0.0.1:7000 slots:0-5460 (5461 slots) master M: 96b19ee89defe04518592a969da795bbc1aacff0 127.0.0.1:7001 slots:5461-10922 (5462 slots) master M: 56e5d0b1d7ab2bc3b072cd6328e80962d9000014 127.0.0.1:7002 slots:10923-16383 (5461 slots) master S: f9af57f3c92783f0e447b5a8efd009497a27bf79 127.0.0.1:7003 replicates 4fdc2bd2fecc2a3ce898c657d8dbd378754c87f9 S: ac1ea8658c82492ee886df0f6190530a1e1dfd6c 127.0.0.1:7004 replicates 96b19ee89defe04518592a969da795bbc1aacff0 S: 08656e58ed979664c3425c1ba199d87670dd9dc1 127.0.0.1:7005 replicates 56e5d0b1d7ab2bc3b072cd6328e80962d9000014 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: 4fdc2bd2fecc2a3ce898c657d8dbd378754c87f9 127.0.0.1:7000 slots:0-5460 (5461 slots) master 1 additional replica(s) M: 96b19ee89defe04518592a969da795bbc1aacff0 127.0.0.1:7001@17001 slots:5461-10922 (5462 slots) master 1 additional replica(s) S: 08656e58ed979664c3425c1ba199d87670dd9dc1 127.0.0.1:7005@17005 slots: (0 slots) slave replicates 56e5d0b1d7ab2bc3b072cd6328e80962d9000014 M: 56e5d0b1d7ab2bc3b072cd6328e80962d9000014 127.0.0.1:7002@17002 slots:10923-16383 (5461 slots) master 1 additional replica(s) S: ac1ea8658c82492ee886df0f6190530a1e1dfd6c 127.0.0.1:7004@17004 slots: (0 slots) slave replicates 96b19ee89defe04518592a969da795bbc1aacff0 S: f9af57f3c92783f0e447b5a8efd009497a27bf79 127.0.0.1:7003@17003 slots: (0 slots) slave replicates 4fdc2bd2fecc2a3ce898c657d8dbd378754c87f9 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
4.我们可以连接集群的一个节点进行测试:
[root@VM_0_12_centos bin]# ./redis-cli -p 7000 127.0.0.1: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_ping_sent:854 cluster_stats_messages_pong_sent:942 cluster_stats_messages_sent:1796 cluster_stats_messages_ping_received:937 cluster_stats_messages_pong_received:854 cluster_stats_messages_meet_received:5 cluster_stats_messages_received:1796 127.0.0.1:7000> CLUSTER NODES 96b19ee89defe04518592a969da795bbc1aacff0 127.0.0.1:7001@17001 master - 0 1523803905316 2 connected 5461-10922 08656e58ed979664c3425c1ba199d87670dd9dc1 127.0.0.1:7005@17005 slave 56e5d0b1d7ab2bc3b072cd6328e80962d9000014 0 1523803907000 6 connected 56e5d0b1d7ab2bc3b072cd6328e80962d9000014 127.0.0.1:7002@17002 master - 0 1523803908321 3 connected 10923-16383 ac1ea8658c82492ee886df0f6190530a1e1dfd6c 127.0.0.1:7004@17004 slave 96b19ee89defe04518592a969da795bbc1aacff0 0 1523803907319 5 connected f9af57f3c92783f0e447b5a8efd009497a27bf79 127.0.0.1:7003@17003 slave 4fdc2bd2fecc2a3ce898c657d8dbd378754c87f9 0 1523803906000 4 connected 4fdc2bd2fecc2a3ce898c657d8dbd378754c87f9 127.0.0.1:7000@17000 myself,master - 0 1523803906000 1 connected 0-5460
5.向集群添加数据进行测试:(注意加-c参数以集群模式启动)
[root@VM_0_12_centos bin]# ./redis-cli -c -p 7000. 127.0.0.1:7000> ls (error) ERR unknown command 'ls' 127.0.0.1:7000> get testKey "mystr" 127.0.0.1:7000> set clus1 success -> Redirected to slot [10606] located at 127.0.0.1:7001 OK 127.0.0.1:7001> get clus1 "success" 127.0.0.1:7001> exit [root@VM_0_12_centos bin]# ./redis-cli -c -p 7005 127.0.0.1:7005> get clus1 -> Redirected to slot [10606] located at 127.0.0.1:7001 "success" 127.0.0.1:7001>
参考: http://blog.csdn.net/zgj12138/article/details/74857510
http://blog.csdn.net/qq_20261343/article/details/51045543
手动分配卡槽: http://blog.csdn.net/candy_rainbow/article/details/70216682
关于集群的更多查看命令参考:https://blog.csdn.net/shukebai/article/details/65979718
更详细的搭建方法参考:https://www.cnblogs.com/xuliangxing/p/7146868.html