单台redis容量限制,如何进行扩容?继续加内存、加硬件么?单台redis并发写量太大有性能瓶颈,如何解决?redis3.0中提供了集群可以解决这些问题。
redis集群是对redis的水平扩容,即启动N个redis节点,将整个数据分布存储在这个N个节点中,每个节点存储总数据的1/N。
如下图:由3台master和3台slave组成的redis集群,每台master承接客户端三分之一请求和写入的数据,当master挂掉后,slave会自动替代master,做到高可用。
配置一个3主3从的集群,每个主下面挂一个slave,master挂掉后,slave会被提升为master。
为了方便,我们在一台机器上进行模拟,我的机器ip是:192.168.1.100,通过端口来区分6个不同的节点,配置信息如下:
#创建/opt/cluster目录,本次所有操作,均在cluster目录进行。
# 方便演示,停止所有的redis
kill -9 redis的id
mkdir /opt/cluster
cd /opt/cluster/
#我测试的Linux环境,redis.conf在/usr/local/redis/redis-6.2.1/中
cp /usr/local/redis/redis-6.2.1/redis.conf /opt/cluster/
#在/opt/cluster目录创建redis-6379.conf文件,内容如下
#注意192.168.1.100是我测试机器的ip,大家需要替换为自己的
include /opt/cluster/redis.conf
daemonize yes
bind 192.168.1.100
dir /opt/cluster/
port 6379
dbfilename dump_6379.rdb
pidfile /var/run/redis_6379.pid
logfile "./6379.log"
cluster-enabled yes
cluster-config-file node-6379.conf
cluster-node-timeout 15000
#在/opt/cluster目录创建redis-6380.conf文件,和上面master的类似,只是将6379换成6380了
include /opt/cluster/redis.conf
daemonize yes
bind 192.168.1.100
dir /opt/cluster/
port 6380
dbfilename dump_6380.rdb
pidfile /var/run/redis_6380.pid
logfile "./6380.log"
cluster-enabled yes
cluster-config-file node-6380.conf
cluster-node-timeout 15000
#在/opt/cluster目录创建redis-6381.conf文件
include /opt/cluster/redis.conf
daemonize yes
bind 192.168.1.100
dir /opt/cluster/
port 6381
dbfilename dump_6381.rdb
pidfile /var/run/redis_6381.pid
logfile "./6381.log"
cluster-enabled yes
cluster-config-file node-6381.conf
cluster-node-timeout 15000
#在/opt/cluster目录创建redis-6389.conf文件,内容如下
include /opt/cluster/redis.conf
daemonize yes
bind 192.168.1.100
dir /opt/cluster/
port 6389
dbfilename dump_6389.rdb
pidfile /var/run/redis_6389.pid
logfile "./6389.log"
cluster-enabled yes
cluster-config-file node-6389.conf
cluster-node-timeout 15000
#在/opt/cluster目录创建redis-6390.conf文件,内容如下
include /opt/cluster/redis.conf
daemonize yes
bind 192.168.1.100
dir /opt/cluster/
port 6390
dbfilename dump_6390.rdb
pidfile /var/run/redis_6390.pid
logfile "./6390.log"
cluster-enabled yes
cluster-config-file node-6390.conf
cluster-node-timeout 15000
#在/opt/cluster目录创建redis-6391.conf文件,内容如下
include /opt/cluster/redis.conf
daemonize yes
bind 192.168.1.100
dir /opt/cluster/
port 6391
dbfilename dump_6391.rdb
pidfile /var/run/redis_6391.pid
logfile "./6391.log"
cluster-enabled yes
cluster-config-file node-6391.conf
cluster-node-timeout 15000
redis-server /opt/cluster/redis-6379.conf
redis-server /opt/cluster/redis-6380.conf
redis-server /opt/cluster/redis-6381.conf
redis-server /opt/cluster/redis-6389.conf
redis-server /opt/cluster/redis-6390.conf
redis-server /opt/cluster/redis-6391.conf
#可以通过 ps -ef | grep redis 查看启动情况
#ps -ef | grep redis信息省略
稍后我们会将6个实例合并到一个集群,在组合之前,我们要确保6个redis实例启动后,nodes-xxxx.conf文件都生成正常,如下:
cd /opt/cluster/
ll
#可以通过上述命令,查看到/opt/cluster/目录下
#如果启动一切正常,会出现6个nodes-xxxx.conf文件
#nodes-xxxx.conf分别对应端口6379、6380、6381、6389、6390、6391的文件
#当然还可以看到相应的log文件和redis-xxxx.conf文件
#执行下面命令,将6个redis合体
#我测试的Linux环境,redis目录是/usr/local/redis/redis-6.2.1/
/usr/local/redis/redis-6.2.1/redis-6.2.1/src/redis-cli --cluster create --cluster-replicas 1 192.168.1.100:6379 192.168.1.100:6380 192.168.1.100:6381 192.168.1.100:6389 192.168.1.100:6390 192.168.1.100:6391
--cluster-replicas 1
:表示采用最简单的方式配置集群,即每个master配1个slave,6个节点就形成了3主3从执行过程如下,期间会让我们确定是否同样这样的分配方式,输入:yes,然后等几秒,集群合体成功
#执行完合成集群命令省略
#中间会有确认环境
Can I set the above configuration? (type 'yes' to accept):
#输入yes将继续执行
#等待执行完毕
#需要使用redis-cli -c命令连接集群中6个节点中任何一个节点都可以
#注意和之前的连接参数有点不同redis-cli命令后面多了一个-c参数,表示采用集群的方式连接
#连上以后,然后使用cluster nodes可以查看集群节点信息,如下
192.168.1.100:6379> cluster nodes
#信息展示省略,执行完cluster nodes,按照之前的配置会出现6条信息
截图解释一下,如下:
集群中的每个节点都会生成一个ID,这个ID信息会被写到node-xxxx.conf文件中。因为节点的ip和端口可能会发生变化,但是节点的ID是不会变的,其他节点可以通过其他节点的ID来认识各个节点。
#连接6379这个节点,然后执行一个set操作(注意这里我用的是redis-cli -c命令)
redis-cli -c -h 192.168.1.100 -p 6379
192.168.1.100:6379> set name zhangsan
-> Redirected to slot [5798] located at 192.168.1.100:6380
OK
#在6379上操作的,但是请求被转发到了6380这个节点去处理了
#这里就是我们后面要说的slot的知识了,后面会提及
一个集群至少有3个主节点,因为新master的选举需要大于半数的集群master节点同意才能选举成功,如果只有两个master节点,当其中一个挂了,是达不到选举新master的条件的。
选项--cluster-replicas 1
表示我们希望为集群中的每个主节点创建一个从节点。
分配原则尽量保证每个主库运行在不同的ip,每个主库和从库不在一个ip上,这样才能做到高可用。
上面“将6个节点合成一个集群”的过程中:
Redis集群内部划分了16384个slots(插槽),合并的时候,会将每个slots映射到一个master上面,比如上面3个master和slots的关系如下:
redis主节点 | 槽位范围 |
---|---|
master1(端口:6379) | [0-5460],插槽的位置从0开始的,0表示第1个插槽 |
master2(端口:6380) | [5461-10922] |
master3(端口:6381) | [10923-16383] |
slave1,slave2,slave3 | 从节点没有槽位,slave是用来对master做替补的 |
而数据库中的每个key都属于16384个slots中的其中1个,当通过key读写数据的时候,redis需要先根据key计算出key对应的slots,然后根据slots和master的映射关系找到对应的redis节点,key对应的数据就在这个节点上面。
集群中使用公式CRC16(key)%16384
计算key属于哪个槽
#在redis-cli每次录入、查询键值,redis都会计算key对应的插槽,如果不是当前redis节点的插槽,redis会报错,并告知应前往的redis实例地址和端口,效果如下
#我们连接了6379这个实例来操作k1,这个节点发现k1的槽位在6381上面,返回了错误信息,怎么办呢?
redis-cli -h 192.168.1.100 -p 6379
192.168.1.100:6379> set k1 v1
(error) MOVED 12706 192.168.1.100:6381
#使用redis-cli客户端提供了-c参数可以解决这个问题
#表示以集群方式执行,执行命令的时候当前节点处理不了的时候,会自动将请求重定向到目标节点,被重定向到6381了
redis-cli -c -h 192.168.1.100 -p 6379
192.168.1.100:6379> set k1 v1
-> Redirected to slot [12706] located at 192.168.1.100:6381
OK
192.168.1.100:6381>
#同样,执行get会被重定向
redis-cli -c -h 192.168.1.100 -p 6379
192.168.1.100:6379> get k1
-> Redirected to slot [12706] located at 192.168.1.100:6381
"v1"
192.168.1.100:6381>
#不在一个slot下面,不能使用mget、mset等多键操作
192.168.1.100:6381> mset k1 v1 k2 v2
(error) CROSSSLOT Keys in request don't hash to the same slot
192.168.1.100:6381> mget k1 k2
(error) CROSSSLOT Keys in request don't hash to the same slot
#可以通过{}来定义组的概念,从而使key中{}内相同的键值放到一个slot中去
192.168.1.100:6381> mset k1{g1} v1 k2{g1} v2 k3{g1} v3
OK
192.168.1.100:6381> mget k1{g1} k2{g1} k3{g1}
1) "v1"
2) "v2"
3) "v3"
192.168.1.100:6381> cluster keyslot k1{g1}
(integer) 13519
192.168.1.100:6381> cluster countkeysinslot 13519
(integer) 3
192.168.1.100:6381> cluster getkeysinslot 13519 3
1) "k1{g1}"
2) "k2{g1}"
3) "k3{g1}"
如果主节点下线,从节点是否能够提升为主节点?(注意:要等10+秒)
#连接master1,然后将master1停掉
redis-cli -c -h 192.168.1.100 -p 6379
192.168.1.100:6379> shutdown
not connected>
#连接master2,看下集群节点的信息
redis-cli -c -h 192.168.1.100 -p 6380
192.168.1.100:6380> cluster nodes
输出如下:
slave1(6389)确实变成master了,而它原来的master:master1(6379)下线了。
继续:
#启动6379
redis-server /opt/cluster/redis-6379.conf
#连接master1
redis-cli -c -h 192.168.1.100 -p 6379
192.168.1.100:6379> cluster nodes
执行结果如下:
6379变成slave了,挂在了6389下面了。
如果某一段插槽的主从都宕机了,redis服务是否还能继续?
这个时候要看cluster-require-full-coverage
参数的值了:
1)引入redis的maven配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
application.properties中配置redis cluster信息
spring.redis.cluster.nodes=192.168.1.100:6379,192.168.1.100:6380,192.168.1.100:6381,192.168.1.100:6389,192.168.1.100:6390,192.168.1.100:6391
spring.redis.timeout=60000
其它的操作,基本就差不多了,不多做阐述了。