目录
一、背景
1、redis的主从和mysql很像,但是配置很简单。slaveof 192.168.80.126 6379然后启动主从便可以了。
但是如果redis主节点发生故障,不会自动切换,需要借助redis的sentinel或者keepalive来实现主的故障转移。(故而可以使用Sentinel来做redis的集群)
2、redis集群是一个无中心的分布式redis存储架构,可以在多个节点之间进行数据共享,解决了redis高可用、可扩展等问题,redis集群提供了以下两个好处:
- 将数据自动切分(split)到多个节点
- 当集群中的某一个节点故障时,redis还可以继续处理客户端的请求。
一个 Redis 集群包含 16384 个哈希槽(hash slot),数据库中的每个数据都属于这16384个哈希槽中的一个。
集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽。集群中的每一个节点负责处理一部分哈希槽。
二、集群中的主从复制
集群中的每个节点都有1个至N个复制品,其中一个为主节点,其余的为从节点,如果主节点下线了,集群就会把这个主节点的一个从节点设置为新的主节点,继续工作。
这样集群就不会因为一个主节点的下线而无法正常工作
三、部署redis集群
1、环境搭建
redis集群管理工具redis-trib.rb依赖ruby环境,redis-trib.rb二进制文件在Redis包主目录下的src目录中,运行该工具依赖Ruby环境和gem,因此需要提前安装。
- 安装Ruby
yum -y install ruby rubygems
#查看Ruby版本信息。
[root@kube-node-1 src]# ruby --version
ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
- 安装ruby和redis的接口程序
gem install redis
gem install redis报错解决:
由于centos系统默认支持Ruby版本为2.0.0,因此执行gem install redis命令时会报以下错误。
[root@kube-node-1 src]# gem install redis
Fetching: redis-4.0.1.gem (100%)
ERROR: Error installing redis:
redis requires Ruby version >= 2.2.2.
解决方法是先安装rvm,再升级ruby版本。
1) 安装rvm
curl -L get.rvm.io | bash -s stable
2) 如果报以下错误,执行gpg2 --recv-keys
[root@kube-node-1 ~]# curl -L get.rvm.io | bash -s stable
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 194 100 194 0 0 335 0 --:--:-- --:--:-- --:--:-- 335
100 24090 100 24090 0 0 17421 0 0:00:01 0:00:01 --:--:-- 44446
Downloading https://github.com/rvm/rvm/archive/1.29.3.tar.gz
Downloading https://github.com/rvm/rvm/releases/download/1.29.3/1.29.3.tar.gz.asc
gpg: 于 2017年09月11日 星期一 04时59分21秒 CST 创建的签名,使用 RSA,钥匙号 BF04FF17
gpg: 无法检查签名:没有公钥
Warning, RVM 1.26.0 introduces signed releases and automated check of signatures when GPG software found. Assuming you trust Michal Papis import the mpapis public key (downloading the signatures).
GPG signature verification failed for '/usr/local/rvm/archives/rvm-1.29.3.tgz' - 'https://github.com/rvm/rvm/releases/download/1.29.3/1.29.3.tar.gz.asc'! Try to install GPG v2 and then fetch the public key:
gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
or if it fails:
command curl -sSL https://rvm.io/mpapis.asc | gpg2 --import -
the key can be compared with:
https://rvm.io/mpapis.asc
https://keybase.io/mpapis
NOTE: GPG version 2.1.17 have a bug which cause failures during fetching keys from remote server. Please downgrade or upgrade to newer version (if available) or use the second method described above.
#再次执行gpg2 --recv-keys的命令
[root@kube-node-1 ~]# gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
gpg: 钥匙环‘/root/.gnupg/secring.gpg’已建立
gpg: 下载密钥‘D39DC0E3’,从 hkp 服务器 keys.gnupg.net
gpg: /root/.gnupg/trustdb.gpg:建立了信任度数据库
gpg: 密钥 D39DC0E3:公钥“Michal Papis (RVM signing) <mpapis@gmail.com>”已导入
gpg: 没有找到任何绝对信任的密钥
gpg: 合计被处理的数量:1
gpg: 已导入:1 (RSA: 1)
# 接着再次执行
[root@kube-node-1 ~]# curl -L get.rvm.io | bash -s stable
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 194 100 194 0 0 310 0 --:--:-- --:--:-- --:--:-- 309
100 24090 100 24090 0 0 18230 0 0:00:01 0:00:01 --:--:-- 103k
Downloading https://github.com/rvm/rvm/archive/1.29.3.tar.gz
Downloading https://github.com/rvm/rvm/releases/download/1.29.3/1.29.3.tar.gz.asc
gpg: 于 2017年09月11日 星期一 04时59分21秒 CST 创建的签名,使用 RSA,钥匙号 BF04FF17
gpg: 完好的签名,来自于“Michal Papis (RVM signing) <mpapis@gmail.com>”
gpg: 亦即“Michal Papis <michal.papis@toptal.com>”
gpg: 亦即“[jpeg image of size 5015]”
gpg: 警告:这把密钥未经受信任的签名认证!
gpg: 没有证据表明这个签名属于它所声称的持有者。
主钥指纹: 409B 6B17 96C2 7546 2A17 0311 3804 BB82 D39D C0E3
子钥指纹: 62C9 E5F4 DA30 0D94 AC36 166B E206 C29F BF04 FF17
GPG verified '/usr/local/rvm/archives/rvm-1.29.3.tgz'
Creating group 'rvm'
Installing RVM to /usr/local/rvm/
Installation of RVM in /usr/local/rvm/ is almost complete:
* First you need to add all users that will be using rvm to 'rvm' group,
and logout - login again, anyone using rvm will be operating with `umask u=rwx,g=rwx,o=rx`.
* To start using RVM you need to run `source /etc/profile.d/rvm.sh`
in all your open shell windows, in rare cases you need to reopen all shell windows.
3) 以上表示执行成功,然后执行以下命令。
source /usr/local/rvm/scripts/rvm
4) 查看rvm库中已知的ruby版本
rvm list known
#例如:
[root@kube-node-1 ~]# rvm list known
# MRI Rubies
[ruby-]1.8.6[-p420]
[ruby-]1.8.7[-head] # security released on head
[ruby-]1.9.1[-p431]
[ruby-]1.9.2[-p330]
[ruby-]1.9.3[-p551]
[ruby-]2.0.0[-p648]
[ruby-]2.1[.10]
[ruby-]2.2[.7]
[ruby-]2.3[.4]
[ruby-]2.4[.1]
ruby-head
...
5) 升级Ruby
#安装ruby
rvm install 2.4.0
#使用新版本
rvm use 2.4.0
#移除旧版本
rvm remove 2.0.0
#查看当前版本
ruby --version
例如:
[root@kube-node-1 ~]# rvm install 2.4.0
Searching for binary rubies, this might take some time.
Found remote file https://rvm_io.global.ssl.fastly.net/binaries/centos/7/x86_64/ruby-2.4.0.tar.bz2
Checking requirements for centos.
Installing requirements for centos.
Installing required packages: autoconf, automake, bison, bzip2, gcc-c++, libffi-devel, libtool, readline-devel, sqlite-devel, zlib-devel, libyaml-devel, openssl-devel................................
Requirements installation successful.
ruby-2.4.0 - #configure
ruby-2.4.0 - #download
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 14.0M 100 14.0M 0 0 852k 0 0:00:16 0:00:16 --:--:-- 980k
No checksum for downloaded archive, recording checksum in user configuration.
ruby-2.4.0 - #validate archive
ruby-2.4.0 - #extract
ruby-2.4.0 - #validate binary
ruby-2.4.0 - #setup
ruby-2.4.0 - #gemset created /usr/local/rvm/gems/ruby-2.4.0@global
ruby-2.4.0 - #importing gemset /usr/local/rvm/gemsets/global.gems..............................
ruby-2.4.0 - #generating global wrappers........
ruby-2.4.0 - #gemset created /usr/local/rvm/gems/ruby-2.4.0
ruby-2.4.0 - #importing gemsetfile /usr/local/rvm/gemsets/default.gems evaluated to empty gem list
ruby-2.4.0 - #generating default wrappers........
[root@kube-node-1 ~]# rvm use 2.4.0
Using /usr/local/rvm/gems/ruby-2.4.0
[root@kube-node-1 ~]# rvm remove 2.0.0
ruby-2.0.0-p648 - #already gone
Using /usr/local/rvm/gems/ruby-2.4.0
[root@kube-node-1 ~]# ruby --version
ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux]
- 再次安装gem
[root@kube-node-1 ~]# gem install redis
Fetching: redis-4.0.1.gem (100%)
Successfully installed redis-4.0.1
Parsing documentation for redis-4.0.1
Installing ri documentation for redis-4.0.1
Done installing documentation for redis after 2 seconds
1 gem installed
2、集群结点规划
最小集群模式需要三个master实例 (3(Master)+3(Slave)才能建立集群),一般建议起六个实例,即三主三从。因此我们创建6个以端口号命名的目录存放实例的配置文件和其他信息。
- 准备
3台服务器上分别创建2个redis实例,总共代表6台不同的redis服务器
192.168.160.138:7001
192.168.160.138:7002
192.168.160.142:7003
192.168.160.142:7004
192.168.160.143:7005
192.168.160.143:7006
- 总创建6个配置文件redis.conf,修改内容并且启动
#编辑配置文件
#可选操作,该项设置后台方式运行,
daemonize yes
port 7000
cluster-enabled yes #必须要开启的
cluster-config-file nodes.conf #节点的日志信息
cluster-node-timeout 5000
appendonly yes #开启aof日志
#分别启动6个redis实例
../redis-server ./redis_7001.conf
../redis-server ./redis_7002.conf
....
../redis-server ./redis_7006.conf
每个实例都会生成一个Node ID,类似97a3a64667477371c4479320d683e4c8db5858b1,用来作为Redis实例在集群中的唯一标识,而不是通过IP和Port,IP和Port可能会改变,该Node ID不会改变。
- 执行redis-trib.rb命令
随便进入到一个服务器上的redis的源码目录中的src中,或者将redis-trib.rb拷贝出来
cd /usr/local/src/redis/src
#执行redis-trib.rb命令
./redis-trib.rb create --replicas 1 192.168.160.138:7001 192.168.160.138:7002 \
192.168.160.142:7003 192.168.160.142:7004 192.168.160.143:7005 192.168.160.143:7006
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.160.138:7001
192.168.160.142:7003
192.168.160.143:7005
Adding replica 192.168.160.142:7004 to 192.168.160.138:7001
Adding replica 192.168.160.143:7006 to 192.168.160.142:7003
Adding replica 192.168.160.138:7002 to 192.168.160.143:7005
M: 38536863aaaf26f39c8d12cfb97017c7a70f280a 192.168.160.138:7001
slots:0-5460 (5461 slots) master
S: acc3fafb34679ad3d6be16175f768a19644859e8 192.168.160.138:7002
replicates 51fc3b4b178bf54b86794c8715c04e085f4b027f
M: 2e6f60cab2d38358546bd41844ce78b1fb186abd 192.168.160.142:7003
slots:5461-10922 (5462 slots) master
S: 51191b7377deb44455eb0e2af6146453414b207b 192.168.160.142:7004
replicates 38536863aaaf26f39c8d12cfb97017c7a70f280a
M: 51fc3b4b178bf54b86794c8715c04e085f4b027f 192.168.160.143:7005
slots:10923-16383 (5461 slots) master
S: fa1b9a73b4dc116328328fceb856f529dc78252d 192.168.160.143:7006
replicates 2e6f60cab2d38358546bd41844ce78b1fb186abd
Can I set the above configuration? (type 'yes' to accept): #yes
参数create表示创建一个新的集群,--replicas 1表示为每个master创建一个slave。
- 集群结果验证
1)如果创建成功会显示以下信息
[OK] All 16384 slots covered
如果执行报以下错误:
[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0
解决方法是:
删除生成的配置文件nodes.conf,如果不行则说明现在创建的结点包括了旧集群的结点信息,需要删除redis的持久化文件后再重启redis,比如:appendonly.aof、dump.rdb
2) 出现节点连接失败
[ERR] Sorry, can't connect to node 192.168.160.138:7001
修改每个实例的配置文件redis.conf,默认是监听 127.0.0.1
[root@localhost redis]# ps aux | grep redis
root 30525 0.0 0.2 141088 2692 ? Ssl 11:13 0:00 ./bin/redis-server 127.0.0.1:7001 [cluster]
root 30530 0.0 0.3 140956 3992 ? Ssl 11:13 0:00 ./bin/redis-server 127.0.0.1:7002 [cluster]
生产环境下修改成真实服务器的IP,实验修改成0.0.0.0 监听所有
bind 0.0.0.0
四、 部署结果验证
(1)客户端验证
使用客户端redis-cli二进制随便访问某个服务器的实例,执行set和get的测试。
$ redis-cli -c -p 7000
redis 127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
redis 127.0.0.1:7002> set hello world
-> Redirected to slot [866] located at 127.0.0.1:7000
OK
redis 127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
redis 127.0.0.1:7000> get hello
-> Redirected to slot [866] located at 127.0.0.1:7000 #调转到对应的槽点
可以看出存储都是无序的,随便连接一个节点往其中发送数据,都会将数据存储在相应的槽中。 取出数据也是一样,随便连接一个节点,取出数据,如果在当前槽点没有数据,会moved到存储数据的槽点,同时提示一个error 可以加上 -c来避免错误,实现跳转
redis-cli -p 7000 -c
(2)集群状态
使用cluster info命令查看集群状态。
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:48273
cluster_stats_messages_pong_sent:49884
cluster_stats_messages_sent:98157
cluster_stats_messages_ping_received:49879
cluster_stats_messages_pong_received:48273
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:98157
(3)节点状态
使用cluster nodes命令查看节点状态。
be2718476eba4e56f696e56b75e67df720b7fc24 127.0.0.1:7002@17002 master - 0 1517303607000 3 connected 10923-16383
13d0c397604a0b2644244c37b666fce83f29faa8 127.0.0.1:7001@17001 master - 0 1517303606000 2 connected 5461-10922
3d02f59b34047486faecc023685379de7b38076c 127.0.0.1:7003@17003 slave 13d0c397604a0b2644244c37b666fce83f29faa8 0 1517303606030 4 connected
d5a834d075fd93eefab877c6ebb86efff680650f 127.0.0.1:7000@17000 myself,master - 0 1517303604000 1 connected 0-5460
99c07119a449a703583019f7699e15afa0e41952 127.0.0.1:7005@17005 slave d5a834d075fd93eefab877c6ebb86efff680650f 0 1517303607060 6 connected
dedf672f0a75faf37407ac4edd5da23bc4651e25 127.0.0.1:7004@17004 slave be2718476eba4e56f696e56b75e67df720b7fc24 0 1517303608082 5 connected
五、注意事项
1、如果某一个主节点和他所有的从节点都下线的话,redis集群就会停止工作了。redis集群不保证数据的强一致性,在特定的情况下,redis集群会丢失已经被执行过的写命令
2、使用异步复制(asynchronous replication)是redis 集群可能会丢失写命令的其中一个原因,有时候由于网络原因,如果网络断开时间太长,redis集群就会启用新的主节点,之前发给主节点的数据就会丢失。