redis 集群部署及使用

说明


  redis 常用集群方案一般分为两种:主从(哨兵)模式 和 redis cluster 方案,因为 cluster亦支持并且官方推荐将node配置成主从结构,即一个master主节点,挂n个slave从节点,所以本方案针对的所有说的集群方案均是指cluster方案。

  redis-3.0.0开始支持集群,redis-4.0.0开始支持module,redis-5.0.0开始支持类似于kafka那样的消息队列。 本文参考 官方文档 而成,不适用redis-5.0.0以下版本,原因是从redis-5.0.0版本开始,redis-trib.rb的功能被redis-cli替代了。redis-trib.rb 是官方提供的Redis Cluster的管理工具, 无需额外下载,默认位于源码包的src目录下,但因该工具是用ruby开发的,所以需要准备相关的依赖环境,而5.0以后的 redis-cli 则不需要任何依赖。

  redis 最新为 redis-5.0.5下载地址 ,当前案例以此版本为主,5.0以上没有任何区别。

cd redis-5.0.5
make

redis cluster 介绍


  Redis Cluster是一种服务器Sharding技术,3.0版本开始正式提供。Redis Cluster中,Sharding采用slot(槽)的概念,一共分成16384个槽,这有点儿类pre sharding思路。对于每个进入Redis的键值对,根据key进行散列,分配到这16384个slot中的某一个中。使用的hash算法也比较简单,就是CRC16后16384取模。

  Redis集群中的每个node(节点)负责分摊这16384个slot中的一部分,也就是说,每个slot都对应一个node负责处理。当动态添加或减少node节点时,需要将16384个槽做个再分配,槽中的键值也要迁移。
Redis集群,要保证16384个槽对应的node都正常工作,如果某个node发生故障,那它负责的slots也就失效,整个集群将不能工作。

  为了增加集群的可访问性,官方推荐的方案是将node配置成主从结构,即一个master主节点,挂n个slave从节点。这时,如果主节点失效,Redis Cluster会根据选举算法从slave节点中选择一个上升为主节点,整个集群继续对外提供服务,Redis Cluster本身提供了故障转移容错的能力。

  Redis Cluster的新节点识别能力、故障判断及故障转移能力是通过集群中的每个node都在和其它nodes进行通信,这被称为集群总线(cluster bus)。它们使用特殊的端口号,即对外服务端口号加10000。例如如果某个node的端口号是6379,那么它与其它nodes通信的端口号是16379。nodes之间的通信采用特殊的二进制协议。

  对客户端来说,整个cluster被看做是一个整体,客户端可以连接任意一个node进行操作,就像操作单一Redis实例一样,当客户端操作的key没有分配到该node上时,Redis会返回转向指令,指向正确的node。


cluster集群部署

  根据cluster的选举机制和主从备份的实现,redis要求至少三主三从共6个节点才能组成redis集群,测试环境可一台物理上启动6个redis节点,但生产环境至少要准备2~3台物理机。

服务端口

IP地址

配置文件名

6381

127.0.0.1

redis-6381.conf

6382

127.0.0.1

redis-6382.conf

6383

127.0.0.1

redis-6383.conf

6384

127.0.0.1

redis-6384.conf

6385

127.0.0.1

redis-6385.conf

6386

127.0.0.1

redis-6386.conf

  启动对应服务:

cd redis-5.0.5
./src/redis-server redis-6381.conf
./src/redis-server redis-6382.conf
./src/redis-server redis-6383.conf
./src/redis-server redis-6384.conf
./src/redis-server redis-6385.conf
./src/redis-server redis-6386.conf

redis 配置修改

  redis.config 配置支持集群,需要修改部分配置项,所有节点(不分主从)修改都是一致的,修改配置完成后启动对应的所有节点即可,和启动普通单例没有任何的区别。

  推荐配置分成两部分:一是公共配置,另一个与端口相关的配置。公共配置文件名可命名为 redis.conf,而端口相关的配置文件名可命令为redis-port.conf , 如 redis-6381.conf 。redis-port.conf 通过 include 的方式包含 redis.conf,如:include /data/redis/conf/redis.conf。

  redis-6381.conf配置如下:

#bind 127.0.0.1
port 6381
daemonize yes
pidfile /var/run/redis_6381.pid
dbfilename dump-6381.rdb
appendonly yes
appendfilename "appendonly-6381.aof"

  下表配置项,黑色加粗 为最小修改项,其它可根据实际需求修改:

配置项

说明

port

指定端口,默认值:6381

cluster-enabled

开启集群,去掉#: cluster-enabled yes

cluster-config-file

集群的配置,去掉#,首次启动会自动生成,命名修改: nodes-{port}.conf

cluster-node-timeout

请求超时 默认15秒,可自行设置

appendonly

指定是否在每次更新操作后进行日志记录,默认不开启,即aof模式备份数据

include

指定包含其它的配置文件

daemonize

redis 是否后台运行,默认值:no

pidfile

daemonize方式运行时,pid可以通过pidfile指定: /var/run/redis_6381.pid

requirepass

对集群设置密码

masterauth

如果设置,requirepass和masterauth都需要设置,并且每个节点的密码要一致

  以上修改 6 份实例,然后正常启动即可。可以写一个启动脚本,批量启动:start-redis-cluster.sh,内容如下:

#!/bin/sh

cd /Users/taofadeng/java/redis-5.0.5
./src/redis-server redis-6381.conf
./src/redis-server redis-6382.conf
./src/redis-server redis-6383.conf
./src/redis-server redis-6384.conf
./src/redis-server redis-6385.conf
./src/redis-server redis-6386.conf

创建集群

  创建redis集群命令(三主三从,每个主一个从,注意redis-5.0.0版本开始才支持“–cluster”,redis-cli --cluster 为 redis-5.0.0/src 下的 redis-cli ,老版本不支持,之前的版本会报错。)

cd /Users/taofadeng/java/redis-5.0.5
./src/redis-cli -h 127.0.0.1 -p 6381 --cluster create 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385 127.0.0.1:6386 --cluster-replicas 1

  如果配置项 cluster-enabled 的值不为yes,则执行时会报错“[ERR] Node 192.168.0.251:6381 is not configured as a cluster node.”。这个时候需改为yes,然后重启 redis-server 进程,之后才可以重新执行 redis-cli 创建集群。

redis-cli的参数说明:

1) create

  表示创建一个redis集群。

2) --cluster-replicas 1

  表示为集群中的每一个主节点指定一个从节点,即一比一的复制。

  运行过程中,会有个提示,输入**yes **回车即可。从屏幕输出,可以很容易地看出哪些是主(master)节点,哪些是从(slave)节点:

$ ./src/redis-cli -h 127.0.0.1 -p 6381 --cluster create 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385 127.0.0.1:6386 --cluster-replicas 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 127.0.0.1:6385 to 127.0.0.1:6381
Adding replica 127.0.0.1:6386 to 127.0.0.1:6382
Adding replica 127.0.0.1:6384 to 127.0.0.1:6383
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 2c87d2cc4d37a7b47a391416bc4615204401a6ec 127.0.0.1:6381
   slots:[0-5460] (5461 slots) master
M: 18bef0e1c021dd22c6b1d8b4337d7d93a91786ad 127.0.0.1:6382
   slots:[5461-10922] (5462 slots) master
M: e50862a4a1679401e16f66c31e5504d3fabcea6a 127.0.0.1:6383
   slots:[10923-16383] (5461 slots) master
S: 3e3338c486f63e5423cfe453f1989e1115be9687 127.0.0.1:6384
   replicates e50862a4a1679401e16f66c31e5504d3fabcea6a
S: e97780f06131637052e02264eada233677b13a70 127.0.0.1:6385
   replicates 2c87d2cc4d37a7b47a391416bc4615204401a6ec
S: 1cf6973e6e738976e8c4645384d0f804880fba6e 127.0.0.1:6386
   replicates 18bef0e1c021dd22c6b1d8b4337d7d93a91786ad
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 127.0.0.1:6381)
M: 2c87d2cc4d37a7b47a391416bc4615204401a6ec 127.0.0.1:6381
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: e97780f06131637052e02264eada233677b13a70 127.0.0.1:6385
   slots: (0 slots) slave
   replicates 2c87d2cc4d37a7b47a391416bc4615204401a6ec
M: 18bef0e1c021dd22c6b1d8b4337d7d93a91786ad 127.0.0.1:6382
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 3e3338c486f63e5423cfe453f1989e1115be9687 127.0.0.1:6384
   slots: (0 slots) slave
   replicates e50862a4a1679401e16f66c31e5504d3fabcea6a
M: e50862a4a1679401e16f66c31e5504d3fabcea6a 127.0.0.1:6383
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: 1cf6973e6e738976e8c4645384d0f804880fba6e 127.0.0.1:6386
   slots: (0 slots) slave
   replicates 18bef0e1c021dd22c6b1d8b4337d7d93a91786ad
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
taofadengdeMacBook-Pro:redis-5.0.5 taofadeng$

ps aux|grep redis

$ ps aux|grep redis
taofadeng         2269   0.4  0.0  4311908   2704   ??  Ss   11:55上午   0:01.34 ./src/redis-server 127.0.0.1:6383 [cluster] 
taofadeng         2271   0.4  0.0  4311928   2516   ??  Ss   11:55上午   0:01.33 ./src/redis-server 127.0.0.1:6384 [cluster] 
taofadeng         2265   0.4  0.0  4313960   2708   ??  Ss   11:55上午   0:01.29 ./src/redis-server 127.0.0.1:6381 [cluster] 
taofadeng         2267   0.4  0.0  4311908   2696   ??  Ss   11:55上午   0:01.29 ./src/redis-server 127.0.0.1:6382 [cluster] 
taofadeng         2275   0.4  0.0  4311928   2528   ??  Ss   11:55上午   0:01.29 ./src/redis-server 127.0.0.1:6386 [cluster] 
taofadeng         2273   0.3  0.0  4311928   2516   ??  Ss   11:55上午   0:01.26 ./src/redis-server 127.0.0.1:6385 [cluster]

集群测试

  客户端测试,6个节点等价,在单机版redis基础上指定参数“-c”,链接仍意一个节点即可: ./src/redis-cli -p 6381 -c

$ ./src/redis-cli -p 6381 -c
127.0.0.1:6381> get name
-> Redirected to slot [5798] located at 127.0.0.1:6382
(nil)
127.0.0.1:6382> set name qgutech
OK
127.0.0.1:6382>

$ ./src/redis-cli -p 6385 -c
127.0.0.1:6385> get name
-> Redirected to slot [5798] located at 127.0.0.1:6382
"qgutech"
127.0.0.1:6382>

查看当前集群信息 : cluster info

127.0.0.1:6380> 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:6
cluster_my_epoch:2
cluster_stats_messages_ping_sent:860
cluster_stats_messages_pong_sent:902
cluster_stats_messages_meet_sent:4
cluster_stats_messages_sent:1766
cluster_stats_messages_ping_received:900
cluster_stats_messages_pong_received:864
cluster_stats_messages_meet_received:2
cluster_stats_messages_received:1766
127.0.0.1:6380>

查看集群里有多少个节点: cluster nodes

127.0.0.1:6382> 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:6
cluster_my_epoch:2
cluster_stats_messages_ping_sent:602
cluster_stats_messages_pong_sent:582
cluster_stats_messages_meet_sent:2
cluster_stats_messages_sent:1186
cluster_stats_messages_ping_received:578
cluster_stats_messages_pong_received:604
cluster_stats_messages_meet_received:4
cluster_stats_messages_received:1186
127.0.0.1:6382>

添加一个新主(master)节点

  假设要添加新的节点“192.168.0.251:6390”,先以单机版配置和启动好6390,然后执行命令(“127.0.0.1:6381”为集群中任一可用的节点)

redis-cli --cluster add-node 192.168.0.251:6390 127.0.0.1:6381

添加一个新从(slave)节点

以添加“192.168.0.251:6390”为例:

redis-cli --cluster add-node 192.168.0.251:6390 127.0.0.1:6381--cluster-slave

  “192.168.0.251:6390” 为新添加的从节点,“127.0.0.1:6381”可为集群中已有的任意节点,这种方法随机为6390指定一个master,如果想明确指定master,假设目标master的ID为“3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e”,则:

redis-cli --cluster add-node 192.168.0.251:6390 127.0.0.1:6381--cluster-slave --cluster-master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e

删除节点

从集群中删除一个节点命令格式:

redis-cli --cluster del-node 127.0.0.1:7000 `<node-id>`

“127.0.0.1:7000”为集群中任意一个非待删除节点,“node-id”为待删除节点的ID。如果待删除的是master节点,则在删除之前需要将该master负责的slots先全部迁到其它master。

$ ./redis-cli --cluster del-node 192.168.0.251:6381 082c079149a9915612d21cca8e08c831a4edeade
>>> Removing node 082c079149a9915612d21cca8e08c831a4edeade from cluster 192.168.0.251:6381
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.

如果删除后,其它节点还看得到这个被删除的节点,则可通过FORGET命令解决,需要在所有还看得到的其它节点上执行:

CLUSTER FORGET `<node-id>`

FORGET做两件事:

  1. 从节点表剔除节点;
  2. 在60秒的时间内,阻止相同ID的节点加进来。

  其他更多cluster 指令请自行查找和测试。

整合rspringboot2.x的几种方式

  • pom 依赖:
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-autoconfigure</artifactId>
 </dependency>

方式一 不指定redis连接池

系统默认连接池

  • yml配置文件:
spring:
  redis:
    cluster:
      nodes:
        - 127.0.0.1:6381
        - 127.0.0.1:6382
        - 127.0.0.1:6383
        - 127.0.0.1:6384
        - 127.0.0.1:6385
        - 127.0.0.1:6386
      max-redirects: 3  # 获取失败 最大重定向次数
    pool:
      max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
      max-idle: 10    # 连接池中的最大空闲连接
      max-wait: -1   # 连接池最大阻塞等待时间(使用负值表示没有限制)
      min-idle:  5     # 连接池中的最小空闲连接
    timeout: 6000  # 连接超时时长(毫秒)

  这种方式 redisTemplate 可直接使用默认,

  在使用的地方直接注入即可

@Autowired
private RedisTemplate<String, Object> redisTemplate;

方式二 使用jedis连接池

yml配置文件:

spring:
  redis:
    password:    # 密码(默认为空)
    timeout: 6000ms  # 连接超时时长(毫秒)
    cluster:
      nodes:
        - 127.0.0.1:6381
        - 127.0.0.1:6382
        - 127.0.0.1:6383
        - 127.0.0.1:6384
        - 127.0.0.1:6385
        - 127.0.0.1:6386
    jedis:
      pool:
        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms      # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 10      # 连接池中的最大空闲连接
        min-idle: 5       # 连接池中的最小空闲连接

  连接池注入配置信息

@Configuration
public class RedisConfig {
   @Autowired
   private RedisConnectionFactory factory;

   @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }
}

  在使用的地方直接注入即可

@Autowired
 private RedisTemplate<String, Object> redisTemplate;

方式三 使用lettuce连接池(推荐)

yml配置文件:

spring:
  redis:
    timeout: 6000ms
    password: 
    cluster:
      max-redirects: 3  # 获取失败 最大重定向次数 
      nodes:
        - 127.0.0.1:6381
        - 127.0.0.1:6382
        - 127.0.0.1:6383
        - 127.0.0.1:6384
        - 127.0.0.1:6385
        - 127.0.0.1:6386 
    lettuce:
      pool:
        max-active: 1000  #连接池最大连接数(使用负值表示没有限制)
        max-idle: 10 # 连接池中的最大空闲连接
        min-idle: 5 # 连接池中的最小空闲连接
        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)

  连接池注入配置信息

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {    
    @Bean
    public RedisTemplate<String, Object> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

  在使用的地方直接注入即可

@Autowired
 private RedisTemplate<String, Object> redisTemplate;

  至此,基本集群使用就全部完成,至于yml 配置调优参考 springcloud 即可。