开启生长之旅!这是我参与「日新方案 12 月更文应战」的第十一天,点击检查活动概况

引言

Redis是一款优异的键值对、内存非联系型数据库,单机节点下的Redis存在无法确保高可用、容量缺乏等问题

上篇文章介绍的岗兵主要能够确保主从架构下Redis的可用性,但是依然存在容量缺乏、推举新的主节点时不能拜访Redis的问题,集群可水平扩展的功能处理容量缺乏的问题而且能够确保高可用

本篇文章将环绕Redis集群浅显易懂的介绍集群的原理、怎么运用集群、运用集群需求留意的当地,了解集群是怎么支持水平扩展的以及怎么确保高可用

学习本篇文章内容之前,需求了解耐久化以及主从复制的机制

集群原理

分片

Redis集群将数据空间分为16384个哈希槽slots,散布到各个主节点中,集群中的每个主节点担任一部分的哈希槽

需求留意的是运用集群后每个主节点只要一个数据库(单机节点情况下是能够设置多个数据库的)

深入浅出Redis(八):Redis的集群模式

当客户端对key进行读写时,通过CRC16校验后对16384取模来决定出Key地点槽【哈希槽 =CRC16(key) % 16384】,然后在去办理这个槽的主节点中读/写Key(各个redis节点之间通讯保存这些槽编号信息)

主从–高可用

每个主节点办理部分的哈希槽,如果主节点产生宕机则这部分槽相关的数据就不行用了

为了提供可用性,需求有从节点来冗余数据确保可用性,因而能够把集群cluster了解成包括多个主从架构,每个主从架构担任办理一部分的哈希槽

主节点间互相发送消息维持心跳的一起交流信息,当节点发现某节点不呼应时(或许下线),播送给其他主节点,其他主节点收到后与不呼应的节点通讯,当大多数主节点接收不届时(承认下线),播送信息给这个节点的一切从节点,从节点收到后根据raft算法推选新主节点

raft推举算法:

  1. 从节点收到后推举自己为新主节点播送给其他从节点
  2. 其他节点接到后,如果该节点还在自转则会投票给它,如果该节点现已推举别的节点了就不会呼应
  3. 收到推举票后如果超越一定数量则成为新主节点,如果最高票持平则重复步骤1

还不了解的同学能够观看动画:raft算法动态展示

集群中默许情况下运用异步复制数据,即主节点处理客户端写指令时,并不等候从节点同步数据再呼应,功能与强一致性不兼得

重定向

当运用指令行进入某主节点中恳求写指令,该写指令或许地点的槽并不是当时主节点的,主节点会呼应MOVED指令告知该Key应该被哪个主节点处理

127.0.0.1:6379> set name cl
(error) MOVED 5798 127.0.0.1:6380

当运用redis-cli -c进入客户端时,产生这种情况则会主动将Key重定向到对应主节点进行处理

水平扩展/缩短会导致节点的槽交给其他节点办理,这就会引起地点槽的Key产生搬迁(搬迁到新的节点中)

水平扩容/缩容

当产生水平扩展添加主节点时,会将其他主节点担任办理的哈希槽分配给新加入的主节点,删去节点类似,总要满足办理16384个槽,且集群中最少要求三个主节点

搬迁是同步堵塞的,如果要搬迁大Key将会产生卡顿,因而要尽量的削减大Key

如果产生搬迁时,Key现已到达了新的节点,但是还未搬迁完,槽与对应节点办理联系还未产生改动,这种情况下返回MOVED指令就会产生循环重定向(A:现已搬迁了你去找B,B:还未搬迁,你去找A),这种情况下会返回给客户端ACKING指令

ACKING指令能在数据搬迁时,避免产生循环重定向

运用集群

集群最少要求三个主节点,所以咱们建立三主三从的集群

主节点端口号:6379,6380,6381

从节点端口号:6382,6383,6384

都在本地一台机器上进行模仿

1. 编写配置文件

#general
daemonize yes
loglevel verbose
#logfile "6379.log"
databases 16
#bind 47.108.181.237
port 6379
​
#暗码
requirepass cl192243051
masterauth cl192243051
​
#rdb
dir /usr/local/redis/redis-6.0.6/data
dbfilename dump-6379.rdb
rdbcompression yes
rdbchecksum yes
save 60 2
​
#aof
appendonly yes
appendfilename appendonly-6379.aof
appendfsync everysec
​
#memory
maxmemory-policy noeviction
​
#cluster 集群配置文件主要是这里
cluster-enabled yes #开启集群
cluster-config-file nodes-6379.conf #该节点产生的文件
cluster-node-timeout 10000 #如果该节点的master超时多少秒没反应就测验引荐自己当master#封闭protected-mode模式 答应外网拜访
protected-mode no

当编写好模板配置文件后,其他配置文件也是一致的只需求改变端口号

运用指令将redis-6379.conf文件中6379替换为6380生成新文件redis-6380.conf

sed "s/6379/6380/g" redis-6379.conf > redis-6380.conf

2. 发动一切节点

redis-server redis-6379.conf

深入浅出Redis(八):Redis的集群模式

3.建立集群指令

#如果有暗码运用参数-a
#--cluster-replicas 1 表示每个主节点带着一个从节点
#后面跟一切节点的 IP:端口号(先主节点后从节点)
#本地拜访版
redis-cli --cluster create --cluster-replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384
​
#外网拜访版
redis-cli --cluster create --cluster-replicas 1 -a 暗码 47.108.181.237:6379 47.108.181.237:6380 47.108.181.237:6381 47.108.181.237:6382 47.108.181.237:6383 47.108.181.237:6384

深入浅出Redis(八):Redis的集群模式

4. 客户端测验

客户端运用redis-cli 操作不在当时节点办理槽的key会呼应moved信息

当集群模式时,进入客户端运用redis-cli -c 这样它会重定向到对应的节点中

写操作

127.0.0.1:6379> set name cl
(error) MOVED 5798 127.0.0.1:6380
​
[root@Tcl ~]# redis-cli -c
127.0.0.1:6379> set name cl
-> Redirected to slot [5798] located at 127.0.0.1:6380
OK
127.0.0.1:6380> 

读操作

故意不去6380端口

[root@Tcl ~]# redis-cli -c -p 6381
127.0.0.1:6381> get name
-> Redirected to slot [5798] located at 127.0.0.1:6380
"cl"
127.0.0.1:6380> 

检查节点信息

在客户端运用指令cluster nodes能够检查节点信息

127.0.0.1:6380> cluster nodes
3c0b7cbc00846b8cca43dd94c55a0005d4d3113b 127.0.0.1:6380@16380 myself,master - 0 1638608629000 2 connected 5461-10922
207460275205f58d47dbf3528bc3c1dedd3ce59d 127.0.0.1:6379@16379 master - 0 1638608631377 1 connected 0-5460
d0eeaf81fcdcbaeee2f99c6598e00b239d796bea 127.0.0.1:6384@16384 slave 3c0b7cbc00846b8cca43dd94c55a0005d4d3113b 0 1638608630375 2 connected
86fcc49d8090bfcfea7a40241c6a78c4bcbc617a 127.0.0.1:6381@16381 master - 0 1638608629375 3 connected 10923-16383
449bceec97e103eafdfebade77decd92081a798b 127.0.0.1:6383@16383 slave 207460275205f58d47dbf3528bc3c1dedd3ce59d 0 1638608628372 1 connected
28f122d37e1bce60749326761a6ec7adc92e834b 127.0.0.1:6382@16382 slave 86fcc49d8090bfcfea7a40241c6a78c4bcbc617a 0 1638608631000 3 connected

深入浅出Redis(八):Redis的集群模式

6379主节点的从节点是6383

6380主节点的从节点是6364

6381主节点的从节点是6382

5. 模仿主从切换

现在模仿6379主机宕机,超时10s后它的从节点6383检测到主节点没呼应,会产生主从切换

127.0.0.1:6380> cluster nodes
3c0b7cbc00846b8cca43dd94c55a0005d4d3113b 127.0.0.1:6380@16380 myself,master - 0 1638609049000 2 connected 5461-10922
207460275205f58d47dbf3528bc3c1dedd3ce59d 127.0.0.1:6379@16379 master,fail - 1638608946026 1638608941010 1 disconnected
d0eeaf81fcdcbaeee2f99c6598e00b239d796bea 127.0.0.1:6384@16384 slave 3c0b7cbc00846b8cca43dd94c55a0005d4d3113b 0 1638609052351 2 connected
86fcc49d8090bfcfea7a40241c6a78c4bcbc617a 127.0.0.1:6381@16381 master - 0 1638609051000 3 connected 10923-16383
449bceec97e103eafdfebade77decd92081a798b 127.0.0.1:6383@16383 master - 0 1638609051347 7 connected 0-5460
28f122d37e1bce60749326761a6ec7adc92e834b 127.0.0.1:6382@16382 slave 86fcc49d8090bfcfea7a40241c6a78c4bcbc617a 0 1638609049343 3 connected

深入浅出Redis(八):Redis的集群模式

6379主机失败,而6383成为新的master

再发动6379主机,6379变成了6383的从机

一起也会更新其他节点中,这俩个节点联系变更的信息

深入浅出Redis(八):Redis的集群模式

6. spring boot整合jedis cluster

<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>3.7.0</version>
</dependency>
@Configuration
public class JedisClusterConfig {
​
  @Value("${spring.redis.cluster.nodes}")
  private String clusterNodes;
  @Value("${spring.redis.timeout}")
  private int timeout;
  @Value("${spring.redis.jedis.pool.max-idle}")
  private int maxIdle;
  @Value("${spring.redis.jedis.pool.max-wait}")
  private long maxWaitMillis;
  @Value("${spring.redis.maxAttempts}")
  private int maxAttempts;
  @Value("${spring.redis.password}")
  private String password;
​
  @Bean
  public JedisCluster getJedisCluster() {
    String[] cNodes = clusterNodes.split(",");
    Set<HostAndPort> nodes = new HashSet<HostAndPort>();
    // 分割出集群节点
    for (String node : cNodes) {
      String[] hp = node.split(":");
      nodes.add(new HostAndPort(hp[0], Integer.parseInt(hp[1])));
     }
    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    jedisPoolConfig.setMaxIdle(maxIdle);
    jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
    // 创立集群对象
    JedisCluster jedisCluster = new JedisCluster(nodes, timeout, timeout, maxAttempts, password, jedisPoolConfig);
    return jedisCluster;
   }
}

接下来能够运用jediscluster调用api操作redis集群

集群留意事项

  • 当有业务需求运用Set对象操作交集、并集时,要求key需求在相同的主节点中,运用{}规范命名前缀,计算槽时只要括号中的内容才会被哈希({}前缀相同,它们就会被分配到相同的槽中,由相同主节点处理)
  • mset、mget、业务等操作只要槽都被相同节点办理时才干运用,能够运用{}相同前缀处理
  • 集群下每个节点只要一个数据库

总结

本篇文章环绕Redis集群浅显易懂的解析集群原理、运用集群以及留意事项

集群通过分片的策略,由多个节点办理集群中的16384个哈希槽,查询时先CRC16(key)% 16384计算key地点哈希槽,再去办理该哈希槽的主节点处理

为了确保集群的可用性,运用从节点冗余备份主节点数据,当产生承认下线时根据raft算法推举从节点产生主从切换,主从之间数据同步默许是异步的,功能和一致性不行兼得

由于Key不一定由当时服务端节点办理,服务端会运用MOVED指令重定向到办理key地点槽的节点

当产生水平扩容/缩容时,需求其他节点迁出/迁入部分办理的哈希槽,搬迁是同步堵塞的,如果有大Key要搬迁则会产生卡顿,运用ACKING指令避免搬迁时产生循环重定向

最后

  • 参考资料

    • 《Redis深度历险》
    • 《Redis设计与完成》