Redis cluster tutorial
Redis cluster 教学
https://redis.io/topics/cluster-tutorial // redis cluster
https://redis.io/topics/sentinel //redis sentinel
这个文档是对Redis Cluster的简单介绍, 不使用复杂的来了解分布式系统概念。
它提供了说明如何设置集群,测试,操作它
然而,这个教材是提供信息关于高可用和一致性
注意本教程需要Redis 3.0或更高版本。
Redis Cluster 101
Redis Cluster 提供了一种方式来运行一个Redis安装 ,其中数据是自动分片到多个Redis节点
Redis Cluster 也提供一定程序的可用性在分区期间,
当一些节点失败或者不能通信时还能继续操作 但是 发生更大的故障(例如,大多数主服务器不可用)
集群将停止运行
那么从实际意思上将,Redis 集群有啥好处呢?
1.可以自动分散数据到所有节点
2.继续操作当一部分节点故障或者不能和集群剩下的节点进行通信
Redis Cluster TCP 端口:
每个Redis Cluster 节点需要2个TCP 连接端口.
通常的Redis TCP 端口用来服务客户端,比如 6379
加上通过向数据端口添加10000而获得的端口,因此示例中为16379
第2个大的端口(16379)是用于集群总线,
这是一个节点到节点通讯通道使用一个二进制协议。
集群总线是用于节点故障检测,配置更新,故障授权等
客户端应该不要尝试和cluster bus 端口通讯,总是和6379端口通讯
命令端口和cluster bus 端口偏移量是固定的,总是10000
请注意,要使Redis集群正常工作,您需要为每个节点:
通常的客户端通信端口(通常是6379) 用于和客户端通信
集群Bus端口(16379) 必须是索引其他集群节点可访问的
如果你不同时打开这两个端口,你的集群无法按预期工作
集群总线使用一个不同的,二进制协议,对于节点到节点数据交换
Redis Cluster and Docker
当前Redis cluster 不支持NAT 环境也不支持地址或者端口是被映射的
Docker 使用一种端口映射的技术,程序运行在Docker容器里是暴露一个不同的端口
Redis Cluster data sharding 数据分片
Redis Cluster 不支持使用一致性hash,但是一个冉的形式的数据分片 ,
每个key 是概念上的一个hahs slot的一部分
有16384 个hash slots 在Redis Cluster里,
计算 给定的key 是哪个hash slot ,我们简单的使用CRC16 算法
每个节点在一个Redis cluster 是负责hash slot的结果子集,例如,
你可能有一个包含3个节点的集群,其中:
Node A contains hash slots from 0 to 5500.
Node B contains hash slots from 5501 to 11000.
Node C contains hash slots from 11001 to 16383.
这个允许增加或者删除节点在集群里, 比如
如果我需要增加一个新的节点D,我需要移动一些hash slot 从A,B,C 到B
类似的,如果我删除节点A从集群里,我只需要move A中的hash slots 到B和C
当节点A为空时,我们可以从集群中完全删除
因为移动hash slots 从一个及诶单到另外一个 不需要停止操作
增加和删除节点,或者改变一个节点持有的hash slots的百分比,都不需要任何停机时间
Redis Cluster 支持多个key操作 ,只要所有的key 加入到一个单独的命令
所有属于相同的hash slot
用户可以强制多个Key 到相同hash slot的一部分 使用一个概念叫做hash tags
Hash tags 是记录在Redis Cluster 规范
但是要点是如果有一个子字符串 between {}在一个key
Redis Cluster master-slave model
为了保持可用性当master node的一个子集失败
或者 不能和大多数节点保持通讯
Redis Cluster 使用一个master-slave model
每个hash slot 有1到N个副本
在我们的例子cluster 有节点A,B,C 如果节点B 故障
集群是不能继续工作,因为不能有一种方式服务hash slots 在5501-11000的
但是当cluster 被创建后 我们增加一个slave节点给每个master,
因此最终的集群是由A B C是master节点,A1,B1,C1是slave节点
系统可以继续工作当节点B故障后
节点B1 复制B 当B故障后,cluster 会提升节点B1作为新的master
可以继续操作
然而 当节点B和B1 同时故障 Redis cluster 不能继续运行
Redis Cluster consistency guarantees 集群一致性保证
Redis Cluster 是不能保证强一致性的,
实际上 这意味着在某些条件下 集群会丢失写
Redis集群丢失写操作的第一个原因是它使用异步复制。这意味着在写入过程中会发生以下情况:
Your client writes to the master B.
The master B replies OK to your client.
The master B propagates the write to its slaves B1, B2 and B3.
客户短写到master b
master B 回复OK给你的客户端
master B 传播写操作到slaves B1,B2和B3
如你所见,B不会等待B1 B2 B3 的确认在回复给客户端前
因为这个会是一个禁止性的延迟惩罚 对于Redis
所以如果你的客户端写东西,B确认写操作 但是崩溃发生在你发送写到slaves前
其中一个slaves提升为master 但是丢失了写操作
这是非常类似的 很多的数据库时配置为刷新数据到磁盘是每秒钟一次
因此它是一个预测 你已经可以知道原因 因为过去的传统的数据库系统不涉及分布式系统
类似的 你可以改善一致性通过强制数据库flush数据到磁盘在回复客户端前
但是这个实际上导致性能降低。 对于Redis集群,这相当于同步复制。
基本上,有一个权衡在性能和一致性上:
Redis Cluster 支持同步写当需要时,通过WAIT命令实现
这个会大大降低写丢失的情况,然而注意Redis Cluster 不会强制实现一致性
即使同步复制被使用 ,在更复杂的故障情况下,slave不能收到写请求就被选取为master
让我们以6个节点的集群为例,它们有A ,B ,C ,A1,B1,C1 使用3个masters和3个slaves组成
也有一个客户端,我们称为Z1
在一个分区发生后,它是可以在分区的一边 我们有A,C,A1,B1,C1 在另外一边我们有B和Z1
Z1是仍旧写到B, 会接收它的写请求。
如果分区在很短的时间内恢复, 集群可以继续工作
然而如果分区持久足够长的时间,B1被提升为master 在分区的大部分区域中
写操作到B1会丢失
注意 这里有一个最大的窗口期Z1发送到B 如果分区的多数已经消耗了足够的事件 来选取
slave称为master 每个master节点
Creating and using a Redis Cluster
创建和使用一个redis 集群
注意: 手动部署一个Redis Cluster 它是非常重要的 学习某些操作方面的技术
如果你希望尽快的启动并运行集群,请跳过本节和下一节,直接使用create cluster脚本创建Redis集群。
要创建一个集群,第一件事情我们需要有一些空的redis实例。
这个基本意味着 clusters 不是使用普通的redis实例来创建的
因为需要配置一种特殊的模式,redis 实例可以让集群启用特定的功能和命令
以下是redis集群的最小配置文件:
[root@node01 redis-4.0.11]# cat /opt/redis/conf/redis7000.conf
bind 192.168.137.2 #填写第一台机器ip
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
timeout 3600
daemonize yes
pidfile /opt/redis/pid/redis7000.pid
logfile "/opt/redis/log/redis7000.log"
dir "/opt/redis/data/7000"
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command KEYS ""
因为你可以看到 启用集群的模式是cluster-enabled
每个实例也包含一个文件的路径,这个节点配置存储的地方 默认是nodes.conf
这个文件不是人工创建的,它是简单的在redis cluster 实例启用的时候产生的
注意:
最小集群需要包含至少3个master节点, 如果你需要测试它的健康性
启动一个6节点的集群 具有3个master和3个slaves
mkdir cluster-test
cd cluster-test
mkdir 7000 7001 7002 7003 7004 7005
创建一个redis.conf 文件 在每个目录,从7000到7005,
现在复制你的redis-server 执行文件,到每个cluster-test目录
这个ID 永远有特定的实例使用,以便实例有一个唯一的名字在集群中。
每个节点记住每个其他节点使用的ID ,不是通过IP或者端口
IP地址和端口可能会变,但是唯一节点表示不会改变
7000
2160:M 11 Feb 18:39:57.229 * No cluster configuration found, I'm 1b83e27acd5235726aea44702526a8ca0ede9a48
你可以看到每个实例的日志,因为没有nodes.conf文件存在,每个节点分配自己一个新的ID
7001:
2187:M 11 Feb 18:44:11.368 * No cluster configuration found, I'm 3c6510bd29af80703ae7c0be5a5884caaa60cd4e
7002
10874:M 17 Jul 06:38:21.303 * No cluster configuration found, I'm 66b5ebc09ce63ff1246f35943b2615faf2e9d8f4
7003
10920:M 17 Jul 06:39:34.102 * No cluster configuration found, I'm 9d4382a4bf01d8acbc0e3a52440c56e9d309a1e3
7004
2159:M 11 Feb 18:55:58.964 * No cluster configuration found, I'm a7287834bc7db37249614d23e06ed8f9a6c7b3d3
7005
2180:M 11 Feb 18:58:24.546 * No cluster configuration found, I'm bf0edaba80c4f31e9b56101572d2a5ccc8aa145c
Creating the cluster
创建集群:
现在我们有很多运行的实例,我们需要创建我们的集群通过写入一些有意义的配置:
如果你是使用Redis5,这是非常容易完成的 因为通过redis cluster 命令行工具嵌入到了redis-clip
可以用于创建新的集群
对于redis 3和4版本,有一个老的工具称为redis-trib.rb
你需要安装redis gem才能运行redis-trib
yum -y install ruby rubygems
gem install redis
Complete!
node2:/root/cluster/7002#gem install redis
ERROR: Error installing redis:
redis requires Ruby version >= 2.3.0.
node2:/root/ruby-2.7.0#ruby -v
ruby 1.8.7 (2013-06-27 patchlevel 374) [x86_64-linux]
在线升级ruby:
升级ruby:
1、sudo yum install curl 安装curl
2、http://www.rvm.io/ 官网首页就可以看到
执行:
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
3、下载rvm :curl -sSL https://get.rvm.io | bash -s stable
[root@node01 ~]# curl -ksSL https://get.rvm.io | bash -s stable
Downloading https://github.com/rvm/rvm/archive/1.29.9.tar.gz
Downloading https://github.com/rvm/rvm/releases/download/1.29.9/1.29.9.tar.gz.asc
gpg: Signature made Wed 10 Jul 2019 04:31:02 PM CST using RSA key ID 39499BDB
gpg: Good signature from "Piotr Kuczynski <piotr.kuczynski@gmail.com>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7D2B AF1C F37B 13E2 069D 6956 105B D0E7 3949 9BDB
GPG verified '/usr/local/rvm/archives/rvm-1.29.9.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.
* Please do NOT forget to add your users to the rvm group.
The installer no longer auto-adds root or users to the rvm group. Admins must do this.
Also, please note that group memberships are ONLY evaluated at login time.
This means that users must log out then back in before group membership takes effect!
Thanks for installing RVM
Please consider donating to our open collective to help us maintain RVM.
Donate: https://opencollective.com/rvm/donate
4、
查找配置文件 node2:/root/ruby-2.7.0#find / -name rvm
/usr/local/rvm
/usr/local/rvm/scripts/rvm
/usr/local/rvm/lib/rvm
/usr/local/rvm/src/rvm
/usr/local/rvm/src/rvm/scripts/rvm
/usr/local/rvm/src/rvm/lib/rvm
/usr/local/rvm/src/rvm/bin/rvm
/usr/local/rvm/bin/rvm
/root/rvm-1.29.4/scripts/rvm
/root/rvm-1.29.4/lib/rvm
/root/rvm-1.29.4/bin/rvm
source /usr/local/rvm/scripts/rvm
下载rvm依赖 rvm requirements
查看rvm库ruby版本 rvm list known
安装ruby指定版本 rvm install ruby-2.4.1
node2:/root/ruby-2.7.0#rvm install ruby-2.4.1
Searching for binary rubies, this might take some time.
No binary rubies available for: centos/6/x86_64/ruby-2.4.1.
Continuing with compilation. Please read 'rvm help mount' to get more information on binary rubies.
Checking requirements for centos.
Requirements installation successful.
Installing Ruby from source to: /usr/local/rvm/rubies/ruby-2.4.1, this may take a while depending on your cpu(s)...
ruby-2.4.1 - #downloading ruby-2.4.1, this may take a while depending on your connection...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 11.9M 100 11.9M 0 0 14702 0 0:14:15 0:14:15 --:--:-- 20070
ruby-2.4.1 - #extracting ruby-2.4.1 to /usr/local/rvm/src/ruby-2.4.1.....
ruby-2.4.1 - #applying patch /usr/local/rvm/patches/ruby/2.4.1/random_c_using_NR_prefix.patch.
ruby-2.4.1 - #configuring..................................................................
ruby-2.4.1 - #post-configuration..
ruby-2.4.1 - #compiling.......................................................................................................
ruby-2.4.1 - #installing.........................
ruby-2.4.1 - #making binaries executable..
ruby-2.4.1 - #downloading rubygems-3.0.6
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 866k 100 866k 0 0 15873 0 0:00:55 0:00:55 --:--:-- 25836
No checksum for downloaded archive, recording checksum in user configuration.
ruby-2.4.1 - #extracting rubygems-3.0.6.....
ruby-2.4.1 - #removing old rubygems........
ruby-2.4.1 - #installing rubygems-3.0.6...............................................
ruby-2.4.1 - #gemset created /usr/local/rvm/gems/ruby-2.4.1@global
ruby-2.4.1 - #importing gemset /usr/local/rvm/gemsets/global.gems................................................................
ruby-2.4.1 - #generating global wrappers.......
ruby-2.4.1 - #gemset created /usr/local/rvm/gems/ruby-2.4.1
ruby-2.4.1 - #importing gemsetfile /usr/local/rvm/gemsets/default.gems evaluated to empty gem list
ruby-2.4.1 - #generating default wrappers.......
ruby-2.4.1 - #adjusting #shebangs for (gem irb erb ri rdoc testrb rake).
Install of ruby-2.4.1 - #complete
Please be aware that you just installed a ruby that requires 1 patches just to be compiled on an up to date linux system.
This may have known and unaccounted for security vulnerabilities.
Please consider upgrading to ruby-2.6.3 which will have all of the latest security patches.
Ruby was built without documentation, to build it run: rvm docs generate-ri
You have mail in /var/spool/mail/root
使用ruby版本默认 rvm use 2.4.1 default
node2:/root/ruby-2.7.0#ruby -version
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]
-e:1:in `<main>': undefined local variable or method `rsion' for main:Object (NameError)
node2:/root/ruby-2.7.0#
gem install redis
[root@node01 ~]# ruby -v
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]
执行 gem install 时,忽略 SSL 证书检查
[root@node01 ~]# cat .gemrc
---
:sources:
- https://gems.ruby-china.org/
:benchmark: false
:verbose: true
:backtrace: false
:update_sources: true
:bulk_threshold: 1000
:ssl_verify_mode: 0
报证书错误
$gem sources -a https://gems.ruby-china.org/
ERROR: SSL verification error at depth 1: unable to get local issuer certificate (20)
ERROR: You must add /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority to your local trusted store
ERROR: SSL verification error at depth 2: self signed certificate in certificate chain (19)
ERROR: Root certificate is not trusted (/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA)
https://gems.ruby-china.org/ added to sources
解决方式:
$ sudo curl -O http://curl.haxx.se/ca/cacert.pem
$ sudo mv cacert.pem cert.pem
[root@node01 ~]# gem install redis
ERROR: SSL verification error at depth 0: certificate is not yet valid (9)
ERROR: Certificate /CN=gems.ruby-china.org not valid until 2019-08-19T01:56:42Z
ERROR: Could not find a valid gem 'redis' (>= 0), here is why:
Unable to download data from https://gems.ruby-china.org/ - bad response Not Found 404 (https://gems.ruby-china.org/specs.4.8.gz)
[root@node01 ~]# gem sources -a https://rubygems.org
https://rubygems.org added to sources
[root@node01 ~]# gem install redis
ERROR: SSL verification error at depth 0: certificate is not yet valid (9)
ERROR: Certificate /CN=gems.ruby-china.org not valid until 2019-08-19T01:56:42Z
Fetching redis-4.1.3.gem
Successfully installed redis-4.1.3
Parsing documentation for redis-4.1.3
Installing ri documentation for redis-4.1.3
Done installing documentation for redis after 2 seconds
WARNING: Unable to pull data from 'https://gems.ruby-china.org/': bad response Not Found 404 (https://gems.ruby-china.org/specs.4.8.gz)
1 gem installed
redis-trib.rb create --replicas 1 192.168.137.2:7000 192.168.137.2:7001 192.168.137.3:7002 192.168.137.3:7003 192.168.137.4:7004 192.168.137.4:7005
[ERR] Node 192.168.137.3:7002 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.
node2:/root/ruby-2.7.0#redis-trib.rb create --replicas 1 192.168.137.2:7000 192.168.137.2:7001 192.168.137.3:7002 192.168.137.3:7003 192.168.137.4:7004 192.168.137.4:7005
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.137.2:7000
192.168.137.3:7002
192.168.137.4:7004
Adding replica 192.168.137.3:7003 to 192.168.137.2:7000
Adding replica 192.168.137.4:7005 to 192.168.137.3:7002
Adding replica 192.168.137.2:7001 to 192.168.137.4:7004
M: 1b83e27acd5235726aea44702526a8ca0ede9a48 192.168.137.2:7000
slots:0-5460 (5461 slots) master
S: 3c6510bd29af80703ae7c0be5a5884caaa60cd4e 192.168.137.2:7001
replicates a7287834bc7db37249614d23e06ed8f9a6c7b3d3
M: 191d7306b81ffa85b5837898562eb6bf1479122c 192.168.137.3:7002
slots:5461-10922 (5462 slots) master
S: 9809b72ec290d73d99a3e1b0d12c4c7bf8583c45 192.168.137.3:7003
replicates 1b83e27acd5235726aea44702526a8ca0ede9a48
M: a7287834bc7db37249614d23e06ed8f9a6c7b3d3 192.168.137.4:7004
slots:10923-16383 (5461 slots) master
S: bf0edaba80c4f31e9b56101572d2a5ccc8aa145c 192.168.137.4:7005
replicates 191d7306b81ffa85b5837898562eb6bf1479122c
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.137.2:7000)
M: 1b83e27acd5235726aea44702526a8ca0ede9a48 192.168.137.2:7000
slots:0-5460 (5461 slots) master
1 additional replica(s)
S: bf0edaba80c4f31e9b56101572d2a5ccc8aa145c 192.168.137.4:7005
slots: (0 slots) slave
replicates 191d7306b81ffa85b5837898562eb6bf1479122c
M: 191d7306b81ffa85b5837898562eb6bf1479122c 192.168.137.3:7002
slots:5461-10922 (5462 slots) master
1 additional replica(s)
S: 9809b72ec290d73d99a3e1b0d12c4c7bf8583c45 192.168.137.3:7003
slots: (0 slots) slave
replicates 1b83e27acd5235726aea44702526a8ca0ede9a48
S: 3c6510bd29af80703ae7c0be5a5884caaa60cd4e 192.168.137.2:7001
slots: (0 slots) slave
replicates a7287834bc7db37249614d23e06ed8f9a6c7b3d3
M: a7287834bc7db37249614d23e06ed8f9a6c7b3d3 192.168.137.4:7004
slots:10923-16383 (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
这里使用的命令是create,因为我们想创建一个新集群。选项--cluster replicas 1意味着我们希望为创建的每个主服务器都有一个从服务器。
其他参数是要用于创建新群集的实例的地址列表
from rediscluster import RedisCluster
startup_nodes = [
{"host":"192.168.137.2", "port":7000},
{"host":"192.168.137.2", "port":7001},
{"host":"192.168.137.3", "port":7002},
{"host":"192.168.137.3", "port":7003},
{"host":"192.168.137.4", "port":7004},
{"host":"192.168.137.4", "port":7005}
]
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True,password='')
print dir(rc)
print rc.cluster_info()
a= rc.cluster_nodes()
print type(a)
for x in a:
#print x
print 'host,id,port,master'
print x['host'],x['id'],x['port'],x['master']
./redis-cli -h 192.24.54.1 -p 6379 -a '123456'
192.24.54.1:6379> get name
(error) MOVED 5798 192.24.54.2:6379
解决方法:
这种情况一般是因为启动 redis-cli 时没有设置集群模式所导致。
启动时使用 -c 参数来启动集群模式,命令如下:
./redis-cli -h 192.24.54.1 -p 6379 -a '123456' -c
192.24.54.1:6379> get name
-> Redirected to slot [5798] located at 192.24.54.2:6379
"yayun"
redis keys *报错:
192.168.137.2:7000> keys *
(error) ERR unknown command `keys`, with args beginning with: `*`,
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command KEYS ""
做了安全加固,不让运行这些命令
FLUSHALL和FLUSHDB会清除redis的数据,比较危险
KEYS在键过多的时候使用会阻塞业务请求