redis-cluster 分布式集群

redis分布式集群概述

Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。
Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误.
Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令.
Redis 集群的优势:

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

如何搭建环境

  1. 集群服务器配置
    集群中有三台服务器,分别是192.168.56.101、192.168.56.103、192.168.56.105,预期搭建环境:三主三从,使用这三台服务器上的7000/7001端口提供redis服务
  2. 安装软件

    安装ruby以及rubygems

sudo apt-get install ruby_full rubygems

安装rubygems的redis包

sudo gem install redis

安装redis 4.0.11

 cd  /usr/local/src
 sudo wget http://download.redis.io/releases/redis-4.0.11.tar.gz
 sudo tar zxf redis-4.0.11.tar.gz
 cd redis-4.0.11
 sudo make
 sudo make test
 sudo make install 
  1. 配置集群

配置文件 (创建工作目录,生成配置文件)

sudo mkdir -p /usr/local/redis-cluster
cd /usr/local/redis-cluster
sudo mkdir 7000 7001
sudo cp /usr/local/src/redis-4.0.11/redis.conf /usr/local/redis-cluster/7000
sudo cp /usr/local/src/redis-4.0.11/redis.conf /usr/local/redis-cluster/7001

修改配置文件 步骤如下:

  • (1)绑定端口,port 7000

  • (2)注释掉IP绑定,##bind 127.0.0.1

  • (3)指定数据存放路径,dir /usr/local/redis-cluster/7000

  • (4)启动集群模式,cluster-enabled yes

  • (5)指定集群节点配置文件,cluster-config-file nodes-7000.conf

  • (6)后台启动,daemonize yes

  • (7)指定集群节点超时时间,cluster-node-timeout 5000

  • (8)修改pidfile配置,pidfile /var/run/redis_7000.pid

  • (9)将 protected-mode yes 修改为 protected-mode no

启动redis服务 (分别在各个服务器上执行以下命令)

sudo /usr/local/bin/redis-server /usr/local/redis-cluster/7000/redis.conf
sudo /usr/local/bin/redis-server /usr/local/redis-cluster/7001/redis.conf

启动集群 (在一个服务器节点上执行即可)

sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb create --replicas 1 192.168.56.105:7000 192.168.56.105:7001 192.168.56.101:7000 192.168.56.101:7001 192.168.56.103:7000 192.168.56.103:7001
  1. 验证是否正常工作
    集群启动成功

查询集群中的主节点 (可以看到现在集群中共三主三从)

redis-cli -h 192.168.56.105 -p 7000 cluster nodes |grep master
192.168.56.105:7000> cluster nodes
050a20ab3750ae41053047ef1fb57c65b1738ff2 192.168.56.101:7001@17001 slave ac2cf6f9a81dc30d0feef1c2475429a674ea5a03 0 1543817706527 4 connected
ce77ac2f93303cfa8b8df45f7ddbb2ed5703addc 192.168.56.103:7001@17001 slave c69fa64175e952b7f8a18cc3974ad94e10bfd50e 0 1543817705620 6 connected
b13cf9657a49a4df32ac2504c5adfce64b13606a 192.168.56.103:7000@17000 master - 0 1543817706000 5 connected 10923-16383
7710f528109bab3b518065e822753bb6d83749fa 192.168.56.105:7001@17001 slave b13cf9657a49a4df32ac2504c5adfce64b13606a 0 1543817705000 5 connected
c69fa64175e952b7f8a18cc3974ad94e10bfd50e 192.168.56.101:7000@17000 master - 0 1543817706626 3 connected 5461-10922
ac2cf6f9a81dc30d0feef1c2475429a674ea5a03 192.168.56.105:7000@17000 myself,master - 0 1543817706000 1 connected 0-5460

验证redis集群读写数据

192.168.56.105:7000> set person1 liubei
-> Redirected to slot [8665] located at 192.168.56.101:7000
OK
192.168.56.101:7000> set person2 guanyu
-> Redirected to slot [4538] located at 192.168.56.105:7000
OK
192.168.56.105:7000> set person3 zhangfei
OK
192.168.56.105:7000> set person4 zhugeliang
-> Redirected to slot [12668] located at 192.168.56.103:7000
OK

单节点读取节点数据 (如果数据不再本节点上会报错)
在这里插入图片描述
集群中读取节点数据 (数据会自动重定向到相应节点)
redis-cli -c Enable cluster mode (follow -ASK and -MOVED redirections).
在这里插入图片描述
注意:读取从节点,用redis-cli连接,先发送“readonly”命令才可以。

测试geo相关命令

geoadd eating 116.487187 40.002045 "soho-t3" 
geoadd eating 116.488092 40.001923 "xiaoxiangge" 116.488249 40.002431 "youjingge" 116.488473 40.001778 "yaogeyongcan" 116.487063 40.002462 "xuebing" 116.487665 40.002672 "maishisala" 116.487162 40.002662 "gongcha" 116.486349 40.001664 "lanzhouniuroulamian"

查找离soho-T3最近的一家餐厅

georadius eating 116.48718684911727905 40.00204543082474373 200 m WITHDIST ASC

在redis集群中,发布订阅不受影响
在任一节点上发布消息,其他订阅的节点都会收到消息。

如何增删节点

如何添加一个新节点,包括一主一从并分配slot给新节点
这里比如添加两个新节点 (主:192.168.56.103:7002 从:192.168.56.101:7002)

######  在101和103上分别添加一个新实例 端口:7002:
sudo mkdir /usr/local/redis-cluster/7002 && sudo cp /usr/local/redis-cluster/7000/redis.conf /usr/local/redis-cluster/7002/redis.conf && sudo sed -i "s/7000/7002/g" /usr/local/redis-cluster/7002/redis.conf

###### 分别在101和103上启动redis实例
sudo /usr/local/bin/redis-server /usr/local/redis-cluster/7002/redis.conf

###### 将这两个启动的实例加入到集群中
sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb add-node 192.168.56.101:7002 192.168.56.105:7000
sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb add-node 192.168.56.103:7002 192.168.56.105:7000

###### 给192.168.56.103:7002重新分配槽 4096个槽给节点 192.168.56.103:7002 ID:4ba66479f7036cb8827cd69c8b8d71e8025617e7
sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb reshard 192.168.56.105:7000
######重新分片的过程中会提示接收槽的节点及从哪些节点获取槽
...
What is the receiving node ID? b988f19d9f7832f68b797f29cd66165950135a18
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:all
...

###### 登录到192.168.56.101:7002 ,并将该节点设置为103:7002的从节点
redis-cli -h 192.168.56.101 -p 7002
cluster replicate 4ba66479f7036cb8827cd69c8b8d71e8025617e7

######也可以先添加主节点,再为该节点添加一个从节点
sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb add-node --slave --master-id '1493e944e61a228a2cfe5fa58866c0f9fe13befb' 192.168.56.103:7002 192.168.56.105:7000

最终结果如下:(可以看到101:7002已经变成了103:7002的从节点)在这里插入图片描述

如何删除一个节点 (删除前需要先将其中的槽分片到其他节点)
这里演示先删除从节点101:7002,再删除节点103:7002

###### 集群中删除一个节点 使用 del-node 命令即可: 
./redis-trib del-node 127.0.0.1:7000 `<node-id>`
第一个参数是任意一个节点的地址,第二个节点是你想要移除的节点地址。

###### 先删除从节点 101:7002
sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb  del-node 192.168.56.105:7000 '1f716ae0975de33b124e2a8115b679720ac1522a'

###### 删除前按照设置自动迁移数据
sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb reshard --from b988f19d9f7832f68b797f29cd66165950135a18 --to ac2cf6f9a81dc30d0feef1c2475429a674ea5a03 --slots 1024 --yes 192.168.56.105:7000
sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb reshard --from b988f19d9f7832f68b797f29cd66165950135a18 --to c69fa64175e952b7f8a18cc3974ad94e10bfd50e --slots 1024 --yes 192.168.56.105:7000
sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb reshard --from b988f19d9f7832f68b797f29cd66165950135a18 --to b13cf9657a49a4df32ac2504c5adfce64b13606a --slots 2048 --yes 192.168.56.105:7000
###分片后删除主节点
sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb  del-node 192.168.56.105:7000 'b988f19d9f7832f68b797f29cd66165950135a18'

重新分片的过程中有可能会出错,如果出错了,就重新修复。
###### 修复命令: sudo /usr/local/src/redis-4.0.11/src/redis-trib.rb fix 192.168.56.105:7001
###### 客户端登录相应节点,可以使用命令取消slots迁移(5461为slot的ID):cluster setslot 5461 stable
###### 取消删除后要针对该节点上的数据重新分片

删除成功后,现在的集群又变成了三主三从的状态
在这里插入图片描述

如果主节点服务停止,集群如何处理

这里重新添加101:7002主节点以及103:7002从节点,然后人为地杀掉101:7002进程看系统会不会启动故障转移

### 删除前集群节点状态
192.168.56.105:7001> cluster nodes
b13cf9657a49a4df32ac2504c5adfce64b13606a 192.168.56.103:7000@17000 master - 0 1543820785196 10 connected 1024-1364 5461-6826 10923-11263 12288-16383
c69fa64175e952b7f8a18cc3974ad94e10bfd50e 192.168.56.101:7000@17000 master - 0 1543820786508 3 connected 6827-10922
ce77ac2f93303cfa8b8df45f7ddbb2ed5703addc 192.168.56.103:7001@17001 slave c69fa64175e952b7f8a18cc3974ad94e10bfd50e 0 1543820785000 6 connected
3573dbaba4528d305afcf78fdf67ab1ca6b455cf 192.168.56.103:7002@17002 slave 1493e944e61a228a2cfe5fa58866c0f9fe13befb 0 1543820784686 0 connected
ac2cf6f9a81dc30d0feef1c2475429a674ea5a03 192.168.56.105:7000@17000 master - 0 1543820786506 11 connected 0-1023 1365-5460 11264-12287
1493e944e61a228a2cfe5fa58866c0f9fe13befb 192.168.56.101:7002@17002 master - 0 1543820786201 0 connected
050a20ab3750ae41053047ef1fb57c65b1738ff2 192.168.56.101:7001@17001 slave ac2cf6f9a81dc30d0feef1c2475429a674ea5a03 0 1543820785000 11 connected
7710f528109bab3b518065e822753bb6d83749fa 192.168.56.105:7001@17001 myself,slave b13cf9657a49a4df32ac2504c5adfce64b13606a 0 1543820782000 2 connected

### 杀掉主节点 192.168.56.101:7002端口的redis-server进程
shiyf@shiyf-VirtualBox:/usr/local/redis-cluster$ ps -ef|grep redis
root      2220     1  0 11:07 ?        00:00:48 /usr/local/bin/redis-server *:7000 [cluster]
root      2226     1  0 11:07 ?        00:00:40 /usr/local/bin/redis-server *:7001 [cluster]
root      2675     1  0 15:03 ?        00:00:00 /usr/local/bin/redis-server *:7002 [cluster]
shiyf     2680  1985  0 15:04 pts/0    00:00:00 grep --color=auto redis
shiyf@shiyf-VirtualBox:/usr/local/redis-cluster$ sudo kill -9 2675

可以看到删除后从节点仍然没有变化,主节点状态变成 fail
在这里插入图片描述> 这时候需要手动将从节点提升为主节点,保证集群数据可用
主节点挂掉,如何将从节点升级为主节点?
如果主节点服务挂掉,我们可以先登录到从节点服务器上,然后使用 cluster failover force命令,将从节点升级为主节点。
”CLUSTER FAILOVER”命令支持两个选项:FORCE和TAKEOVER。使用这两个选项,可以改变上述的流程。如果有FORCE选项,则从节点不会与主节点进行交互,主节点也不会阻塞其客户端,而是从节点立即开始故障转移流程:发起选举、统计选票、赢得选举、升级为主节点并更新配置。
如果有TAKEOVER选项,则更加简单粗暴:从节点不再发起选举,而是直接将自己升级为主节点,接手原主节点的槽位,增加自己的configEpoch后更新配置。
因此,使用FORCE和TAKEOVER选项,主节点可以已经下线;
而不使用任何选项,只发送”CLUSTER FAILOVER”命令的话,主节点必须在线。

###### 如果待删除节点已经不能连接,则调用CLUSTER FORGET剔除(需要在任一节点上执行一次FORGET):
CLUSTER FORGET '141cdfe5ff0808f20780c8fa574c844027250dad'	

###登录到从节点 103:7002,并主动发起主节点选举
redis-cli -h 192.168.56.103 -p 7002
192.168.56.103:7002> cluster failover takeover
OK

手动执行故障转移后,结果如下(101:7002成为主节点):
在这里插入图片描述

如何通过PHP访问redis集群

目前我们用到的 php 的 redis 扩展 主要有2个,第一个是最常用的 phpredis, 它是用c写的php的高效扩展:https://github.com/phpredis,还有1个是predis, 它是用php代码写的,也用的蛮多的:https://github.com/nrk/predis

//这里以phpredis中的RedisCluster为例
<?php

//测试主节点读写
$objCluster = new RedisCluster(null, ['192.168.56.105:7000', '192.168.56.101:7000', '192.168.56.103:7000']);

$objCluster->lPush('wuguo', 'sunquan');
$objCluster->lPush('wuguo', 'zhouyu');
$objCluster->lPush('wuguo', 'lvmeng');
$objCluster->lPush('wuguo', 'luxun');
$wuguoPersons = $objCluster->lRange('wuguo', 0, -1);

$objCluster->sAdd('weiguo', 'caocao');
$objCluster->sAdd('weiguo', 'guojia');
$objCluster->sAdd('weiguo', 'simayi');
$objCluster->sAdd('weiguo', 'zhangliao');
$weiguoPersons = $objCluster->sMembers('weiguo');

$objCluster->set('person1', 'liubei');
$objCluster->set('person2', 'guanyu');
$objCluster->set('person3', 'zhangfei');
$objCluster->set('person4', 'zhugeliang');
$objCluster->set('person5', 'zhaoyun');
$objCluster->set('person6', 'machao');

$name1 = $objCluster->get('person1');
$name2 = $objCluster->get('person2');
$name3 = $objCluster->get('person3');
$name4 = $objCluster->get('person4');
$name5 = $objCluster->get('person5');
$name6 = $objCluster->get('person6');

echo '<hr/>以下是主节点写入的内容:';
var_dump($name1, $name2, $name3, $name4, $name5, $name6, $wuguoPersons, $weiguoPersons);

echo '<hr/>';

//测试redis事务
try {
    $objCluster->multi();    
    //$objCluster->get('person1'); //加入这一句可能会报错,因为读写没有在一个主节点上操作。
    $objCluster->set('{shuguo}:person7', 'huangzhong');
    $objCluster->set('{shuguo}:person8', 'weiyan');
    $objCluster->get('{shuguo}:person7');
    $objCluster->get('{shuguo}:person8');
    var_dump($objCluster->exec());
    echo '<br/>';
} catch (Exception $e) {
    echo $e->getMessage().'<br/>';
}




//测试从节点读
$objCluster = new RedisCluster(null, ['192.168.56.105:7001', '192.168.56.101:7001', '192.168.56.103:7001']);

$name1 = $objCluster->get('person1');
$name2 = $objCluster->get('person2');
$name3 = $objCluster->get('person3');
$name4 = $objCluster->get('person4');
$name5 = $objCluster->get('person5');
$name6 = $objCluster->get('person6');

echo '<hr/>以下是从节点读取的内容:';
var_dump($name1, $name2, $name3, $name4, $name5, $name6, $wuguoPersons, $weiguoPersons);
echo '<hr/>';

//测试获取主节点
foreach ($objCluster->_masters() as $arrMaster) {
    echo 'master node:' . implode(':', $arrMaster).'<br/>';
}

程序运行结果如下:
以下是主节点写入的内容:string(6) “liubei” string(6) “guanyu” string(8) “zhangfei” string(10) “zhugeliang” string(7) “zhaoyun” string(6) “machao” array(4) { [0]=> string(5) “luxun” [1]=> string(6) “lvmeng” [2]=> string(6) “zhouyu” [3]=> string(7) “sunquan” } array(4) { [0]=> string(9) “zhangliao” [1]=> string(6) “guojia” [2]=> string(6) “simayi” [3]=> string(6) “caocao” } array(4) { [0]=> bool(true) [1]=> bool(true) [2]=> string(10) “huangzhong” [3]=> string(6) “weiyan” }
以下是从节点读取的内容:string(6) “liubei” string(6) “guanyu” string(8) “zhangfei” string(10) “zhugeliang” string(7) “zhaoyun” string(6) “machao” array(4) { [0]=> string(5) “luxun” [1]=> string(6) “lvmeng” [2]=> string(6) “zhouyu” [3]=> string(7) “sunquan” } array(4) { [0]=> string(9) “zhangliao” [1]=> string(6) “guojia” [2]=> string(6) “simayi” [3]=> string(6) “caocao” }
master node:192.168.56.105:7000
master node:192.168.56.101:7000
master node:192.168.56.103:7002
master node:192.168.56.103:7000

与codies有何区别

Codies Redis cluster 备注
Redis版本 2.8.13分支开发 >= 3.0
部署 较复杂。 简单
运维 Dashboard,运维方便。 运维人员手动通过命令操作。
监控 可在Dashboard里监控当前redis-server节点情况,较为便捷。 不提供监控功能。
组织架构 Proxy-Based 类中心化架构,集群管理层与存储层解耦。 P2P模型,gossip协议。去中心化,
伸缩性 动态伸缩。 需要手工增删节点
Server群组主从复制 不负责 负责。
主节点失效处理 不自动选主,交由运维人员处理。 自动选主。
数据迁移 以slot为单位,迁移期间保证可用性。 开源的工具可以在github中获取 redis-migrate-tool支持3.*版本的数据迁移
升级 基于redis 2.8.13分支开发,后续升级不能保证;Redis-server必须是此版本的codis,无法使用新版本redis的增强特性。 Redis官方推出,后续升级可保证。 升级需要重新加载数据,数据量较大的情况下, 分批次升级bin对运维要求比较高。
可靠性 经过线上服务验证,可靠性较高。 现在已经出现3.0/4.0及5.0版本,均支持redis-cluster
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章