1、需求

容量不够,redis如何进行扩容?
并发写操作, redis如何分摊?
另外,主从模式,薪火相传模式,主机宕机,导致ip地址发生变化,应用程序中配置需要修改对应的主机地址、端口等信息。
之前通过代理主机来解决,但是redis3.0中提供了解决方案。就是无中心化集群配置。

2、redis集群

Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。

Redis 集群通过分区(partition)来提供一定程度的可用性(availability):即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。

3、集群搭建

1、配置文件

创建一个文件夹 myredis,将 redis.conf 拷贝过来

  1. mkdir /myredis
  2. cp /etc/redis.conf /myredis

redis.conf 文件

#注释这一句
bind=127.0.0.1
#关闭本机访问保护模式
protected-mode no
#守护进程方式运行
daemonize yes

在单个 redis 服务中 redis6379.conf

#提取公共部分
include /redisCluster/redis.conf
#redis以守护进程方式运行时,指定pid写入文件
pidfile "/var/run/redis_6379.pid"
#设置端口
port 6379
#设置本地数据库文件名
dbfilename "dump6379.rdb"
#打开集群模式
cluster-enabled yes
#设定节点配置文件名
cluster-config-file nodes-6379.conf
#设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换。
cluster-node-timeout 15000

复制多份并修改相应的端口号与文件名

[root@localhost myredis]# cp redis6379.conf redis6380.conf
[root@localhost myredis]# cp redis6379.conf redis6381.conf
[root@localhost myredis]# cp redis6379.conf redis6389.conf
[root@localhost myredis]# cp redis6379.conf redis6390.conf
[root@localhost myredis]# cp redis6379.conf redis6391.conf

可使用查找替换 :%s/6379/6380 来快速更改文件内容

2、启动服务

[root@localhost myredis]# redis-server redis6379.conf 
[root@localhost myredis]# redis-server redis6380.conf 
[root@localhost myredis]# redis-server redis6381.conf 
[root@localhost myredis]# redis-server redis6389.conf 
[root@localhost myredis]# redis-server redis6390.conf 
[root@localhost myredis]# redis-server redis6391.conf

查看启动状况

[root@localhost myredis]# ps -ef|grep redis
root       1655      1  0 04:30 ?        00:00:00 redis-server *:6379 [cluster]
root       1661      1  0 04:30 ?        00:00:00 redis-server *:6380 [cluster]
root       1667      1  0 04:30 ?        00:00:00 redis-server *:6381 [cluster]
root       1673      1  0 04:30 ?        00:00:00 redis-server *:6389 [cluster]
root       1679      1  0 04:30 ?        00:00:00 redis-server *:6390 [cluster]
root       1685      1  0 04:30 ?        00:00:00 redis-server *:6391 [cluster]
root       1691   1538  0 04:31 pts/0    00:00:00 grep --color=auto redis

正常生成了文件

-rw-r--r--. 1 root root   114 May 18 04:30 nodes-6379.conf
-rw-r--r--. 1 root root   114 May 18 04:30 nodes-6380.conf
-rw-r--r--. 1 root root   114 May 18 04:30 nodes-6381.conf
-rw-r--r--. 1 root root   114 May 18 04:30 nodes-6389.conf
-rw-r--r--. 1 root root   114 May 18 04:30 nodes-6390.conf
-rw-r--r--. 1 root root   114 May 18 04:30 nodes-6391.conf

3、合成一个集群

组合之前,请确保所有 redis 实例启动后,nodes-xxxx.conf文件都生成正常。

然后进入到之前安装 redis 的 src 目录下 cd /opt/redis-6.2.1/src/

redis 集群合体

redis-cli --cluster create --cluster-replicas 1 192.168.75.11:6379 192.168.75.11:6380 192.168.75.11:6381 192.168.75.11:6389 192.168.75.11:6390 192.168.75.11:6391

此处不要用127.0.0.1,请用真实IP地址
—replicas 1 采用最简单的方式配置集群,一台主机,一台从机,正好三组。

redis cluster 如何分配这六个节点?

一个集群至少要有三个主节点
选项—cluster-replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。
分配原则尽量保证每个主数据库运行在不同的IP地址,每个从库和主库不在一个IP地址上。

>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.75.11:6390 to 192.168.75.11:6379
Adding replica 192.168.75.11:6391 to 192.168.75.11:6380
Adding replica 192.168.75.11:6389 to 192.168.75.11:6381
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 4175195b77e229388f4c28061dd05796f4d55aa5 192.168.75.11:6379
   slots:[0-5460] (5461 slots) master
M: e109d20ba550d45315965d63b12eb77dd406ed46 192.168.75.11:6380
   slots:[5461-10922] (5462 slots) master
M: 05d00c28d7fe47eb072aa61692d9faeb9d013c0f 192.168.75.11:6381
   slots:[10923-16383] (5461 slots) master
S: cddac624aef4286d6fadee5eb74898b2df97ddb4 192.168.75.11:6389
   replicates 05d00c28d7fe47eb072aa61692d9faeb9d013c0f
S: 8c74d5be27ec4bbf83c1ea9ab28a32124c0230ab 192.168.75.11:6390
   replicates 4175195b77e229388f4c28061dd05796f4d55aa5
S: a614550d3afcc4fa3fc3b302ce631429b093fb68 192.168.75.11:6391
   replicates e109d20ba550d45315965d63b12eb77dd406ed46
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.75.11:6379)
M: 4175195b77e229388f4c28061dd05796f4d55aa5 192.168.75.11:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 8c74d5be27ec4bbf83c1ea9ab28a32124c0230ab 192.168.75.11:6390
   slots: (0 slots) slave
   replicates 4175195b77e229388f4c28061dd05796f4d55aa5
S: a614550d3afcc4fa3fc3b302ce631429b093fb68 192.168.75.11:6391
   slots: (0 slots) slave
   replicates e109d20ba550d45315965d63b12eb77dd406ed46
M: 05d00c28d7fe47eb072aa61692d9faeb9d013c0f 192.168.75.11:6381
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: cddac624aef4286d6fadee5eb74898b2df97ddb4 192.168.75.11:6389
   slots: (0 slots) slave
   replicates 05d00c28d7fe47eb072aa61692d9faeb9d013c0f
M: e109d20ba550d45315965d63b12eb77dd406ed46 192.168.75.11:6380
   slots:[5461-10922] (5462 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.

集群合体成功

什么是slots?

[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

一个 Redis 集群包含16384 个插槽(hash slot),数据库中的每个键都属于这16384 个插槽的其中一个

集群使用公式 CRC16(key) % 16384 来计算键key 属于哪个槽,其中CRC16(key) 语句用于计算键key 的CRC16 校验和。

集群中的每个节点负责处理一部分插槽。举个例子,如果一个集群可以有主节点,其中:
节点 A 负责处理0号至5460号插槽。
节点 B 负责处理5461号至10922号插槽。
节点 C 负责处理10923号至16383号插槽。

4、查看效果

普通方式登录

[root@localhost myredis]# redis-cli -p 6379
127.0.0.1:6379> set k1 v1
(error) MOVED 12706 192.168.75.11:6381

可能直接进入读主机,存储数据时,会出现MOVED重定向操作。所以,应该以集群方式登录。

集群方式登录

[root@localhost myredis]# redis-cli -c -p 6379
127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 192.168.75.11:6381
OK

采用集群策略连接,设置数据会自动切换到相应的写主机

查看集群信息

192.168.75.11:6381> cluster nodes
e109d20ba550d45315965d63b12eb77dd406ed46 192.168.75.11:6380@16380 master - 0 1621327703000 2 connected 5461-10922
05d00c28d7fe47eb072aa61692d9faeb9d013c0f 192.168.75.11:6381@16381 myself,master - 0 1621327703000 3 connected 10923-16383
8c74d5be27ec4bbf83c1ea9ab28a32124c0230ab 192.168.75.11:6390@16390 slave 4175195b77e229388f4c28061dd05796f4d55aa5 0 1621327703453 1 connected
4175195b77e229388f4c28061dd05796f4d55aa5 192.168.75.11:6379@16379 master - 0 1621327703000 1 connected 0-5460
a614550d3afcc4fa3fc3b302ce631429b093fb68 192.168.75.11:6391@16391 slave e109d20ba550d45315965d63b12eb77dd406ed46 0 1621327704457 2 connected
cddac624aef4286d6fadee5eb74898b2df97ddb4 192.168.75.11:6389@16389 slave 05d00c28d7fe47eb072aa61692d9faeb9d013c0f 0 1621327702000 3 connected

4、集群操作

1、在集群中录入值

在 redis-cli 每次录入、查询键值,redis 都会计算出该 key 应该送往的插槽,如果不是该客户端对应服务器的插槽,redis 会报错,并告知应前往的 redis 实例地址和端口。
redis-cli 客户端提供了–c 参数实现自动重定向
redis-cli -c –p 6379 登入后,再录入、查询键值对可以自动重定向。

127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 192.168.75.11:6381
OK
192.168.75.11:6381> set k2 v2
-> Redirected to slot [449] located at 192.168.75.11:6379
OK
192.168.75.11:6379>

不在一个 slot 下的键值,是不能使用 mget , mset 等多键操作

192.168.75.11:6379> mset name manster age 18 gender 1
(error) CROSSSLOT Keys in request don't hash to the same slot
192.168.75.11:6379>

可以通过 {} 来定义组的概念,从而使key中 {} 内相同内容的键值对放到一个 slot 中去。

192.168.75.11:6379> mset  name{user} lucy age{user} 20
-> Redirected to slot [5474] located at 192.168.75.11:6380
OK

2、查询集群中的值

CLUSTER GETKEYSINSLOT <slot><count> 返回 count 个 slot 槽中的键。

192.168.75.11:6380> cluster keyslot k1
(integer) 12706
192.168.75.11:6380> cluster keyslot name
(integer) 5798

只能在相对应的服务上查看,12706 插槽在 6381 服务器上,而现在在 6380 上读取,读取不到

192.168.75.11:6380> cluster countkeysinslot 12706
(integer) 0
192.168.75.11:6379> cluster countkeysinslot 449
(integer) 1

获取插槽中的几个键

192.168.75.11:6379> cluster getkeysinslot 449 2
1) "k2"
192.168.75.11:6379> cluster getkeysinslot 5798 1
(empty array)

5、故障恢复

如果主节点下线?从节点能否自动升为主节点?注意:15秒超时
6379 挂掉

192.168.75.11:6379> shutdown
not connected>
[root@localhost myredis]# redis-cli -c -p 6380
127.0.0.1:6380> cluster nodes
cddac624aef4286d6fadee5eb74898b2df97ddb4 192.168.75.11:6389@16389 slave 05d00c28d7fe47eb072aa61692d9faeb9d013c0f 0 1621333841661 3 connected
e109d20ba550d45315965d63b12eb77dd406ed46 192.168.75.11:6380@16380 myself,master - 0 1621333840000 2 connected 5461-10922
8c74d5be27ec4bbf83c1ea9ab28a32124c0230ab 192.168.75.11:6390@16390 slave 4175195b77e229388f4c28061dd05796f4d55aa5 0 1621333840000 1 connected
05d00c28d7fe47eb072aa61692d9faeb9d013c0f 192.168.75.11:6381@16381 master - 0 1621333841000 3 connected 10923-16383
4175195b77e229388f4c28061dd05796f4d55aa5 192.168.75.11:6379@16379 master,fail? - 1621333825637 1621333823000 1 disconnected 0-5460
a614550d3afcc4fa3fc3b302ce631429b093fb68 192.168.75.11:6391@16391 slave e109d20ba550d45315965d63b12eb77dd406ed46 0 1621333842663 2 connected

到达15秒,6390变为主节点

127.0.0.1:6380> cluster nodes
cddac624aef4286d6fadee5eb74898b2df97ddb4 192.168.75.11:6389@16389 slave 05d00c28d7fe47eb072aa61692d9faeb9d013c0f 0 1621333889740 3 connected
e109d20ba550d45315965d63b12eb77dd406ed46 192.168.75.11:6380@16380 myself,master - 0 1621333887000 2 connected 5461-10922
8c74d5be27ec4bbf83c1ea9ab28a32124c0230ab 192.168.75.11:6390@16390 master - 0 1621333888739 7 connected 0-5460
05d00c28d7fe47eb072aa61692d9faeb9d013c0f 192.168.75.11:6381@16381 master - 0 1621333887000 3 connected 10923-16383
4175195b77e229388f4c28061dd05796f4d55aa5 192.168.75.11:6379@16379 master,fail - 1621333825637 1621333823000 1 disconnected
a614550d3afcc4fa3fc3b302ce631429b093fb68 192.168.75.11:6391@16391 slave e109d20ba550d45315965d63b12eb77dd406ed46 0 1621333889000 2 connected

主节点恢复后,主从关系会如何?主节点回来变成从机。

[root@localhost myredis]# redis-server redis6379.conf 
[root@localhost myredis]# redis-cli -c -p 6380
127.0.0.1:6380> cluster nodes
cddac624aef4286d6fadee5eb74898b2df97ddb4 192.168.75.11:6389@16389 slave 05d00c28d7fe47eb072aa61692d9faeb9d013c0f 0 1621334034239 3 connected
e109d20ba550d45315965d63b12eb77dd406ed46 192.168.75.11:6380@16380 myself,master - 0 1621334033000 2 connected 5461-10922
8c74d5be27ec4bbf83c1ea9ab28a32124c0230ab 192.168.75.11:6390@16390 master - 0 1621334033234 7 connected 0-5460
05d00c28d7fe47eb072aa61692d9faeb9d013c0f 192.168.75.11:6381@16381 master - 0 1621334033000 3 connected 10923-16383
4175195b77e229388f4c28061dd05796f4d55aa5 192.168.75.11:6379@16379 slave 8c74d5be27ec4bbf83c1ea9ab28a32124c0230ab 0 1621334033000 7 connected
a614550d3afcc4fa3fc3b302ce631429b093fb68 192.168.75.11:6391@16391 slave e109d20ba550d45315965d63b12eb77dd406ed46 0 1621334035243 2 connected

如果所有某一段插槽的主从节点都宕掉,redis服务是否还能继续?
如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为yes ,那么,整个集群都挂掉
如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为no ,那么,插槽数据全都不能使用,也无法存储。
redis.conf中的参数cluster-require-full-coverage

6、集群的Jedis开发

即使连接的不是主机,集群会自动切换主机存储。主机写,从机读。
无中心化主从集群。无论从哪台主机写的数据,其他主机上都能读到数据。

public class JedisClusterTest {
  public static void main(String[] args) { 
     Set<HostAndPort>set =new HashSet<HostAndPort>();
     set.add(new HostAndPort("192.168.31.211",6379));
     JedisCluster jedisCluster=new JedisCluster(set);
     jedisCluster.set("k1", "v1");
     System.out.println(jedisCluster.get("k1"));
  }
}

7、优劣

优势

  • 实现扩容
  • 分摊压力
  • 无中心配置相对简单

劣势

  • 多键操作是不被支持的
  • 多键的Redis事务是不被支持的。lua脚本不被支持
  • 由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片的方案想要迁移至redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。