Redis学习-Redis Cluster模式

Redis学习-Redis Cluster模式

Redis哨兵模式的缺点

在redis哨兵模式中,我们为主从架构添加了哨兵节点,来监视主从节点的状态,并在发生故障时为主从架构自动进行主从切换,来达到高可用的效果,但是哨兵模式依然存在一些缺点:

  1. 哨兵模式master节点只有一个,写数据压力大。
  2. 所有slave节点备份的都是全部数据,冗余性高,占用内存和磁盘空间

因此,redis官方发布了redis-cluster集群方案,并且在新版本(这里用的目前最新的5.0.6版本)的redis中支持redis-cli自动构建cluster,十分方便,下面先来简单介绍redis-cluster模式架构

Redis Cluster模式架构

架构图

redis-cluster架构图

在redis cluster模式下,至少需要六个节点才可启动,启动后可以手动指定三个master节点,三个slave节点,也可以使用redis-cli自动指定。所有节点之间通过gossip协议进行通信。

分槽slot

前面说了,redis哨兵模式下,所有的slave节点都保存着完整且相同的数据备份,从而造成内存和磁盘空间使用较大的情况。redis cluster的分槽便可以解决这个问题,当然使用slot还有一些其它的优点,我们在后面进行读写测试的时候会说到。

在redis cluster模式下,约定由16384个slot,我们在进行数据写入的时候会将写入的key按照hash函数CRC16(key) mod 16384进行计算,按照得到的结果找到相应的slot,将数据写入,这里我们可以类比一下HashMap的put操作。put操作会对key进行hash操作,然后根据hashcode找到数组中相应的位置,存进去。

对于我们三主三从的集群来说,这16384个槽会按照master节点的数量进行分配。下面是我使用redis-cli进行自动分配得到的结果

Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383

这样就可以将数据分散存储在整个redis cluster中,每个slave节点只备份自己的master节点所分配的槽中的数据

docker搭建简单的redis cluster

拉取镜像并创建容器

docker pull redis:5.0.6
docker run --name redis7001 -p 7001:6379 -v $PWD/data-7001:/data -v /etc/localtime:/etc/localtime -d redis:5.0.6 redis-server --appendonly yes --protected-mode no --cluster-enabled yes

docker run --name redis7002 -p 7002:6379 -v $PWD/data-7002:/data -v /etc/localtime:/etc/localtime -d redis:5.0.6 redis-server --appendonly yes --protected-mode no --cluster-enabled yes

docker run --name redis7003 -p 7003:6379 -v $PWD/data-7003:/data -v /etc/localtime:/etc/localtime -d redis:5.0.6 redis-server --appendonly yes --protected-mode no --cluster-enabled yes

docker run --name redis7004 -p 7004:6379 -v $PWD/data-7004:/data -v /etc/localtime:/etc/localtime -d redis:5.0.6 redis-server --appendonly yes --protected-mode no --cluster-enabled yes

docker run --name redis7005 -p 7005:6379 -v $PWD/data-7005:/data -v /etc/localtime:/etc/localtime -d redis:5.0.6 redis-server --appendonly yes --protected-mode no --cluster-enabled yes

docker run --name redis7006 -p 7006:6379 -v $PWD/data-7006:/data -v /etc/localtime:/etc/localtime -d redis:5.0.6 redis-server --appendonly yes --protected-mode no --cluster-enabled yes

查看容器ip

(base) [root@iZgslo8f7ncru8Z redis_cluster]# docker inspect redis7001|grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.10",
                    "IPAddress": "172.17.0.10",
(base) [root@iZgslo8f7ncru8Z redis_cluster]# docker inspect redis7002|grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.11",
                    "IPAddress": "172.17.0.11",
(base) [root@iZgslo8f7ncru8Z redis_cluster]# docker inspect redis7003|grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.12",
                    "IPAddress": "172.17.0.12",
(base) [root@iZgslo8f7ncru8Z redis_cluster]# docker inspect redis7004|grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.13",
                    "IPAddress": "172.17.0.13",
(base) [root@iZgslo8f7ncru8Z redis_cluster]# docker inspect redis7005|grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.14",
                    "IPAddress": "172.17.0.14",
(base) [root@iZgslo8f7ncru8Z redis_cluster]# docker inspect redis7006|grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.15",
                    "IPAddress": "172.17.0.15",

使用redis-cli创建redis cluster

root@b30efdc32611:/data# redis-cli --cluster create 172.17.0.10:6379 172.17.0.11:6379 172.17.0.12:6379 172.17.0.13:6379 172.17.0.14:6379 172.17.0.15:6379 --cluster-replicas 1

--cluster-replicas 1:为每个master节点指定1个从节点

执行命令返回如下信息:

>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.17.0.14:6379 to 172.17.0.10:6379
Adding replica 172.17.0.15:6379 to 172.17.0.11:6379
Adding replica 172.17.0.13:6379 to 172.17.0.12:6379
... 
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 172.17.0.10:6379)
...
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

...处我省略了一些信息

在执行命令的过程中,要求用户手动输入yes来同意redis-cli自动分配的方案

从返回信息中我们可以看到哪些节点被分配为master,哪些被分配为slave节点,并且可以看到master分配slot的情况

到这里我们的redis cluster就创建好了,是不是觉得比sentinel模式还要简单?

查看节点及集群状态

后面我们要想看集群节点状态可以在redis-cli使用cluster nodes命令,同样可以看到节点的状态

172.17.0.12:6379> cluster nodes
5f1fe8cdeaa30865a5957c057e1731d15296b22f 172.17.0.15:6379@16379 slave bba3dd73a57c6781aa691dc8babc0c834c22d6d1 0 1570018571000 2 connected
8dfb5fc5757673a4e5b6e6cac74ae189c086f3b0 172.17.0.14:6379@16379 master - 0 1570018573967 7 connected 0-5460
42ba36a70ece66d2879915bb4ccc9948aea45190 172.17.0.12:6379@16379 myself,master - 0 1570018570000 3 connected 10923-16383
289843119765866c00dbfaa4089a06ba13a0251c 172.17.0.13:6379@16379 slave 42ba36a70ece66d2879915bb4ccc9948aea45190 0 1570018572965 4 connected
bba3dd73a57c6781aa691dc8babc0c834c22d6d1 172.17.0.11:6379@16379 master - 0 1570018571964 2 connected 5461-10922
73ff60442b302cb5b4c451a41ba90c503c9f0532 172.17.0.10:6379@16379 slave 8dfb5fc5757673a4e5b6e6cac74ae189c086f3b0 0 1570018572000 7 connected

若想要查看集群状态,可以再redis-cli使用命令cluster info

172.17.0.12:6379> 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:7
cluster_my_epoch:3
...

测试集群使用

我们随便找一个节点,准备使用redis-cli来执行一个set操作

root@b30efdc32611:/data# redis-cli
127.0.0.1:6379> set k1 v1
(error) MOVED 12706 172.17.0.12:6379
127.0.0.1:6379> get k1
(error) MOVED 12706 172.17.0.12:6379

what?为什么既存不进去,当然也取不到值

还是因为刚刚我们说的slot分槽的原因,因为k1经过计算后不存储在当前节点上,所以既不能写也不能读

这样也太不方便了吧?有什么方法解决呢?

A:使用redis-cli -c命令,添加参数-c,客户端即可帮助我们将不在本节点的请求转发给相应的节点

root@b30efdc32611:/data# redis-cli -c
127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 172.17.0.12:6379
OK
172.17.0.12:6379> get k1
"v1"

从返回的信息我们可以看到,请求被转发到了172.17.0.12:6379节点上,这样就方便了很多。

性能测试

这里我们测试redis单节点、sentinel模式、cluster模式下每秒的读写次数

但是因为我的所有节点都在一台服务器上,与实际分服务器部署使用时肯定还是有差别的

另外就是,测试使用的sentinel节点是使用的redis3.2版本,与单节点和cluster模式是用的5.0.6版本,可能在版本优化上会有稍许差别

测试使用redis-cli自带的redis-benchmark命令

单节点

root@d31327959339:/data# redis-benchmark -q
PING_INLINE: 46360.68 requests per second
PING_BULK: 39510.08 requests per second
SET: 46750.82 requests per second
GET: 47370.91 requests per second
INCR: 47036.69 requests per second
LPUSH: 38197.10 requests per second
RPUSH: 44822.95 requests per second
LPOP: 46146.75 requests per second
RPOP: 46125.46 requests per second
SADD: 39808.91 requests per second
HSET: 45850.53 requests per second
SPOP: 47551.12 requests per second
LPUSH (needed to benchmark LRANGE): 46490.00 requests per second
LRANGE_100 (first 100 elements): 20665.43 requests per second
LRANGE_300 (first 300 elements): 9594.17 requests per second
LRANGE_500 (first 450 elements): 7155.63 requests per second
LRANGE_600 (first 600 elements): 5685.37 requests per second
MSET (10 keys): 37750.09 requests per second

sentinel模式

(base) [root@iZgslo8f7ncru8Z redis_cluster]# docker exec -it redis-6380 /bin/bash
root@c90995ac2b06:/data# redis-benchmark -q
PING_INLINE: 47664.44 requests per second
PING_BULK: 48828.12 requests per second
SET: 48285.85 requests per second
GET: 47551.12 requests per second
INCR: 48685.49 requests per second
LPUSH: 48638.13 requests per second
RPUSH: 48567.27 requests per second
LPOP: 48875.86 requests per second
RPOP: 48709.21 requests per second
SADD: 48661.80 requests per second
HSET: 48216.01 requests per second
SPOP: 49236.83 requests per second
LPUSH (needed to benchmark LRANGE): 48449.61 requests per second
LRANGE_100 (first 100 elements): 21934.63 requests per second
LRANGE_300 (first 300 elements): 9806.81 requests per second
LRANGE_500 (first 450 elements): 7103.28 requests per second
LRANGE_600 (first 600 elements): 5531.89 requests per second
MSET (10 keys): 46728.97 requests per second

cluster模式

(base) [root@iZgslo8f7ncru8Z ~]# docker exec -it redis7003 /bin/bash
root@e750b7fe44f0:/data# redis-benchmark -q
PING_INLINE: 49043.65 requests per second
PING_BULK: 50251.26 requests per second
SET: 40535.06 requests per second
GET: 48661.80 requests per second
INCR: 48076.93 requests per second
LPUSH: 47984.64 requests per second
RPUSH: 47732.70 requests per second
LPOP: 48146.37 requests per second
RPOP: 46446.82 requests per second
SADD: 47169.81 requests per second
HSET: 46860.36 requests per second
SPOP: 47348.48 requests per second
LPUSH (needed to benchmark LRANGE): 47846.89 requests per second
LRANGE_100 (first 100 elements): 47393.37 requests per second
LRANGE_300 (first 300 elements): 46992.48 requests per second
LRANGE_500 (first 450 elements): 46533.27 requests per second
LRANGE_600 (first 600 elements): 47258.98 requests per second
MSET (10 keys): 25342.12 requests per second

从结果看,我们发现:

  1. 一主二从的sentinel模式整体比单节点读写能力明显要优秀一些
  2. cluster模式与sentinel模式相比,由于中间加了分槽操作,在进行读写操作的时候都会慢一点,但是几乎可以说是忽略不计了
  3. 我们看LRANGE操作,不管范围大小,cluster模式下,读取十分稳定,而单点模式和sentinel模式下随着范围的变大,性能急剧下降
  4. MSET操作。cluster模式下进行mset操作会比setinel模式下慢很多,接近50%,推测是因为mset操作的原子性与cluster模式下分槽共同造成的。

本节就到这里了。

留个坑,后面写cluster模式下的故障转移问题

附录

参考文献

Redis配置文件总结

Redis cluster命令

Docker创建redis5.0.3集群

redis 系列(四)- Redis Cluster

某位大佬写的redis cluster源码解析

redis cluster 扩容与数据迁移

你可能感兴趣的:(Redis学习-Redis Cluster模式)