开启生长之旅!这是我参与「日新方案 12 月更文应战」的第十一天,点击检查活动概况
引言
Redis是一款优异的键值对、内存非联系型数据库,单机节点下的Redis存在无法确保高可用、容量缺乏等问题
上篇文章介绍的岗兵主要能够确保主从架构下Redis的可用性,但是依然存在容量缺乏、推举新的主节点时不能拜访Redis的问题,集群可水平扩展的功能处理容量缺乏的问题而且能够确保高可用
本篇文章将环绕Redis集群浅显易懂的介绍集群的原理、怎么运用集群、运用集群需求留意的当地,了解集群是怎么支持水平扩展的以及怎么确保高可用
学习本篇文章内容之前,需求了解耐久化以及主从复制的机制
集群原理
分片
Redis集群将数据空间分为16384个哈希槽slots,散布到各个主节点中,集群中的每个主节点担任一部分的哈希槽
需求留意的是运用集群后每个主节点只要一个数据库(单机节点情况下是能够设置多个数据库的)
当客户端对key进行读写时,通过CRC16校验后对16384取模来决定出Key地点槽【哈希槽 =CRC16(key) % 16384】,然后在去办理这个槽的主节点中读/写Key(各个redis节点之间通讯保存这些槽编号信息)
主从–高可用
每个主节点办理部分的哈希槽,如果主节点产生宕机则这部分槽相关的数据就不行用了
为了提供可用性,需求有从节点来冗余数据确保可用性,因而能够把集群cluster了解成包括多个主从架构,每个主从架构担任办理一部分的哈希槽
主节点间互相发送消息维持心跳的一起交流信息,当节点发现某节点不呼应时(或许下线),播送给其他主节点,其他主节点收到后与不呼应的节点通讯,当大多数主节点接收不届时(承认下线),播送信息给这个节点的一切从节点,从节点收到后根据raft算法推选新主节点
raft推举算法:
- 从节点收到后推举自己为新主节点播送给其他从节点
- 其他节点接到后,如果该节点还在自转则会投票给它,如果该节点现已推举别的节点了就不会呼应
- 收到推举票后如果超越一定数量则成为新主节点,如果最高票持平则重复步骤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
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
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
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
6379主机失败,而6383成为新的master
再发动6379主机,6379变成了6383的从机
一起也会更新其他节点中,这俩个节点联系变更的信息
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设计与完成》