Redis从基础到进阶篇(三)----架构原理与集群演变

目录

一、缓存淘汰策略

1.1 LRU原理

1.2 案例分析

1.3 Redis缓存淘汰策略

1.3.1 设置最⼤缓存

1.3.2 淘汰策略

二、Redis事务

2.1 Redis事务典型应⽤—Redis乐观锁

2.2 Redis事务介绍

2.3 事务命令

2.3.1 MULTI

2.3.2 EXEC

2.3.3 DISCARD

2.3.4 WATCH

2.3.5 UNWATCH

2.4 事务演示

2.5 事务失败处理

2.6 Redis乐观锁

2.7 Redis乐观锁实现秒杀

三、Redis持久化

3.1 RDB⽅式(默认)

3.1.1 触发快照的时机

3.1.2 设置快照规则

3.1.3 原理图

3.2 AOF⽅式

3.2.1 AOF介绍

3.2.2 同步磁盘数据

3.2.3 AOF重写原理(优化AOF⽂件)

3.2.4 AOF⽂件损坏以后如何修复

3.3 如何选择RDB和AOF

3.4 如何选择RDB和AOF(4.0之前的还需要考虑)

3.5 混合持久化⽅式

四、Redis主从复制

4.1 什么是主从复制

4.2 主从配置

4.2.1 主Redis配置

4.2.2 从Redis配置

4.2.3 实现原理

4.2.4 全量同步

4.2.5 增量同步

五、Redis哨兵机制

5.1 简介

5.2 哨兵进程的作用

5.3 故障判定原理分析

5.4 ⾃动故障迁移

5.5 案例演示

六、Redis集群

6.1 Redis的集群策略

6.2 Redis-cluster架构图

6.3 Redis-cluster投票:容错

6.4 安装RedisCluster

6.5 命令客户端连接集群

6.6 查看集群的命令

6.7 维护节点

6.7.1 添加主节点

6.7.2 hash槽重新分配(数据迁移)

6.7.3 添加从节点

6.7.4 删除结点

6.8 Jedis连接集群

6.8.1 代码实现

6.8.2 使⽤spring


一、缓存淘汰策略

1.1 LRU原理

LRU Least recently used ,最近最少使⽤)算法根据数据的历史访问记录来进⾏淘汰数据,其核⼼思 想是“ 如果数据最近被访问过,那么将来被访问的⼏率也更⾼

最常⻅的实现是使⽤⼀个链表保存缓存数据,详细算法实现如下:

                     

1. 新数据插⼊到链表头部;
2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
3. 当链表满的时候,将链表尾部的数据丢弃。

Java中可以使⽤LinkHashMap去实现LRU
利⽤哈希链表实现

1.2 案例分析

让我们以⽤户信息的需求为例,来演示⼀下LRU算法的基本思路:

(1) 假设我们使⽤哈希链表来缓存⽤户信息,⽬前缓存了4个⽤户,这4个⽤户是按照时间顺序依次从链表右端插⼊的。

(2) 此时,业务⽅访问⽤户5,由于哈希链表中没有⽤户5的数据,我们从数据库中读取出来,插⼊到缓存当中。这时候,链表中最右端是最新访问到的⽤户5,最左端是最近最少访问的⽤户1

(3) 接下来,业务⽅访问⽤户2,哈希链表中存在⽤户2的数据,我们怎么做呢?我们把⽤户2从它的前驱节 点和后继节点之间移除,重新插⼊到链表最右端。这时候,链表中最右端变成了最新访问到的⽤户2,最左端仍然是最近最少访问的⽤户1

(4) 接下来,业务⽅请求修改⽤户4的信息。同样道理,我们把⽤户4从原来的位置移动到链表最右侧,并把⽤户信息的值更新。这时候,链表中最右端是最新访问到的⽤户4,最左端仍然是最近最少访问的⽤户1

(5) 后来业务⽅换⼝味了,访问⽤户6,⽤户6在缓存⾥没有,需要插⼊到哈希链表。假设这时候缓存容量已经达到上限,必须先删除最近最少访问的数据,那么位于哈希链表最左端的⽤户1就会被删除掉,然后再把⽤户6插⼊到最右端。

以上,就是 LRU 算法的基本思路。

1.3 Redis缓存淘汰策略

1.3.1 设置最⼤缓存

redis 中,允许⽤户设置最⼤使⽤内存⼤⼩ maxmemory ,默认为 0 ,没有指定最⼤缓存,如果 有新的数据添加,超过最⼤内存,则会使redis 崩溃,所以⼀定要设置。
redis 内存数据集⼤⼩上升到⼀定⼤⼩的时候,就会实⾏数据淘汰策略。

1.3.2 淘汰策略

redis 淘汰策略配置: maxmemory-policy voltile-lru ,⽀持热配置
redis 提供 6 种数据淘汰策略:
1. volatile-lru :从已设置过期时间的数据集( server.db[i].expires )中挑选最近最少使⽤的数据淘汰
2. volatile-ttl :从已设置过期时间的数据集( server.db[i].expires )中挑选将要过期的数据淘汰
3. volatile-random :从已设置过期时间的数据集( server.db[i].expires )中任意选择数据淘汰
4. allkeys-lru :从数据集( server.db[i].dict )中挑选最近最少使⽤的数据淘汰
5. allkeys-random :从数据集( server.db[i].dict )中任意选择数据淘汰
6. no-enviction (驱逐):禁⽌驱逐数据

二、Redis事务

2.1 Redis事务典型应⽤—Redis乐观锁

在⽣产环境⾥,经常会利⽤ redis 乐观锁来实现秒杀, Redis 乐观锁是 Redis 事务的经典应⽤。
秒杀场景描述 :
秒杀活动对稀缺或者特价的商品进⾏定时,定量售卖,吸引成⼤量的消费者进⾏抢购,但⼜只有少部分 消费者可以下单成功。因此,秒杀活动将在较短时间内产⽣⽐平时⼤数⼗倍,上百倍的⻚⾯访问流量和 下单请求流量。
由于秒杀只有少部分请求能够成功,⽽⼤量的请求是并发产⽣的,所以如何确定哪个请求成功了,就是 由redis 乐观锁来实现。具体思路如下:
监控 锁定量,如果该值被修改成功则表示该请求被通过,反之表示该请求未通过。
从监控到修改到执⾏都需要在 redis ⾥操作,这样就需要⽤到 Redis 事务。

2.2 Redis事务介绍

Redis 的事务是通过 MULTI EXEC 、 DISCARD WATCH 这四个命令来完成的。
Redis 的单个命令都是原⼦性的,所以这⾥需要确保事务性的对象是 命令集合
Redis 将命令集合序列化并确保处于同⼀事务的 命令集合连续且不被打断 的执⾏
Redis 不⽀持回滚操作。

2.3 事务命令

2.3.1 MULTI

⽤于标记事务块的开始。
Redis 会将后续的命令逐个放⼊队列中,然后使⽤ EXEC 命令原⼦化地执⾏这个命令序列。
语法:
multi

2.3.2 EXEC

在⼀个事务中执⾏所有先前放⼊队列的命令,然后恢复正常的连接状态
语法:
exec

2.3.3 DISCARD

清除所有先前在⼀个事务中放⼊队列的命令,然后恢复正常的连接状态。
语法:
discard

2.3.4 WATCH

当某个 [ 事务需要按条件执⾏ ] 时,就要使⽤这个命令将给定的 [ 键设置为受监控 ] 的状态。
语法:
watch key [key…]
注意事项: 使⽤该命令可以实现 Redis 乐观锁 。(后⾯实现)

2.3.5 UNWATCH

清除所有先前为⼀个事务监控的键。
语法:
unwatch

2.4 事务演示

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s1 111
QUEUED
127.0.0.1:6379> hset set1 name zhangsan
QUEUED
127.0.0.1:6379> exec
1) OK
2) (integer) 1
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s2 222
QUEUED
127.0.0.1:6379> hset set2 age 20
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
127.0.0.1:6379> watch s1 
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s1 555
QUEUED
127.0.0.1:6379> exec # 此时在没有exec之前,通过另⼀个命令窗⼝对监控的s1字段进⾏修改
(nil)
127.0.0.1:6379> get s1
111

2.5 事务失败处理

(1 )Redis 语法错误

整个事务的命令在队列⾥都清除

(2)Redis 运⾏错误

在队列⾥正确的命令可以执⾏ (弱事务性)
弱事务性 :
1 、在队列⾥正确的命令可以执⾏ (⾮原⼦操作)
2 、不⽀持回滚

(3) Redis 不⽀持事务回滚(为什么呢)

1 、⼤多数事务失败是因为 语法错误或者类型错误 ,这两种错误,在开发阶段都是可以预⻅的
2 Redis 为了 性能⽅⾯ 就忽略了事务回滚。 (回滚记录历史版本)

2.6 Redis乐观锁

乐观锁基于 CAS Compare And Swap )思想(⽐较并替换),是不具有互斥性,不会产⽣锁等待⽽消 耗资源,但是需要反复的重试,但也是因为重试的机制,能⽐较快的响应。因此我们可以利⽤redis 来实 现乐观锁。具体思路如下:
1 、利⽤ redis watch 功能,监控这个 redisKey 的状态值
2 、获取 redisKey 的值
3 、创建 redis 事务
4 、给这个 key 的值 +1
5 、然后去执⾏这个事务,如果 key 的值被修改过则回滚, key 不加 1
public void watch() {try {String watchKeys = "watchKeys";//初始值 value=1jedis.set(watchKeys, 1);//监听key为watchKeys的值jedis.watch(watchkeys);//开启事务Transaction tx = jedis.multi();//watchKeys⾃增加⼀tx.incr(watchKeys);//执⾏事务,如果其他线程对watchKeys中的value进⾏修改,则该事务将不会执⾏//通过redis事务以及watch命令实现乐观锁List<Object> exec = tx.exec();if (exec == null) {System.out.println("事务未执⾏");} else {System.out.println("事务成功执⾏,watchKeys的value成功修改");}} catch (Exception e) {e.printStackTrace();} finally {jedis.close();}
}

2.7 Redis乐观锁实现秒杀

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class SecKill {public static void main(String[] arg) {// 库存keyString redisKey = "stock";ExecutorService executorService = Executors.newFixedThreadPool(20);try {Jedis jedis = new Jedis("127.0.0.1", 6378);// 可以被秒杀的库存的初始值,库存总共20个jedis.set(redisKey, "0");jedis.close();} catch (Exception e) {e.printStackTrace();}for (int i = 0; i < 1000; i++) {executorService.execute(() -> {Jedis jedis1 = new Jedis("127.0.0.1", 6378);try {jedis1.watch(redisKey);String redisValue = jedis1.get(redisKey);int valInteger = Integer.valueOf(redisValue);String userInfo = UUID.randomUUID().toString();// 没有秒完if (valInteger < 20) {Transaction tx = jedis1.multi();tx.incr(redisKey);List<Object> list = tx.exec();// 秒成功 失败返回空list⽽不是空if (list != null && list.size() > 0) {System.out.println("⽤户:" + userInfo + ",秒杀成功!当前成功⼈数:" + (valInteger + 1));}// 版本变化,被别⼈抢了。else {System.out.println("⽤户:" + userInfo + ",秒杀失败");}}// 秒完了else {System.out.println("已经有20⼈秒杀成功,秒杀结束");}} catch (Exception e) {e.printStackTrace();} finally {jedis1.close();}});}executorService.shutdown();}
}

三、Redis持久化

Redis 是⼀个 内存 数据库,为了保证数据的持久性,它提供了两种持久化⽅案:

3.1 RDB⽅式(默认)

RDB ⽅式是通过 快照 snapshotting )完成的,当 符合⼀定条件 Redis 会⾃动将内存中的数据进⾏快 照并持久化到硬盘

3.1.1 触发快照的时机

1. 符合⾃定义配置的快照规则 redis.conf
2. 执⾏ save 或者 bgsave 命令
3. 执⾏ flushall 命令
4. 执⾏主从复制操作 ( 第⼀次 )

3.1.2 设置快照规则

save 多少秒内 数据变了多少
save "" : 不使⽤ RDB 存储
save 900 1 : 表示 15 分钟( 900 秒钟)内⾄少 1 个键被更改则进⾏快照。
save 300 10 : 表示 5 分钟( 300 秒)内⾄少 10 个键被更改则进⾏快照。
save 60 10000 :表示 1 分钟内⾄少 10000 个键被更改则进⾏快照。
过滤条件是或的关系,⽽且是漏⽃型的过滤顺序。

3.1.3 原理图

注意事项!!!
1. Redis 在进⾏快照的过程中不会修改 RDB ⽂件,只有快照操作结束后才会将旧的⽂件替换成新的, 也就是说任何时候 RDB ⽂件都是完整的。
2. 这就使得我们可以通过定时备份 RDB ⽂件来实现 Redis 数据库的备份, RDB ⽂件是 经过压缩的⼆ 进制⽂件 ,占⽤的空间会⼩于内存中的数据,更加利于传输。
RDB优缺点:
缺点: 使⽤ RDB ⽅式实现持久化,⼀旦 Redis 异常退出 ,就会 丢失最后⼀次快照以后更改的所有数 。这个时候我们就需要根据具体的应⽤场景,通过组合设置⾃动快照条件的⽅式来将可能发⽣的 数据损失控制在能够接受范围。如果数据相对来说⽐较重要,希望将损失降到最⼩,则可以使 ⽤ AOF ⽅式进⾏持久化
优点: RDB 可以最⼤化 Redis 的性能:⽗进程在保存 RDB ⽂件时唯⼀要做的就是 fork 出⼀个⼦ 进程,然后这个⼦进程就会处理接下来的所有保存⼯作,⽗进程⽆需执⾏任何磁盘 I/O 操作。同时 这个也是⼀个缺点,如果数据集⽐较⼤的时候, fork 可以能⽐较耗时,造成服务器在⼀段时间内 停⽌处理客户端的请求;

3.2 AOF⽅式

3.2.1 AOF介绍

默认情况下 Redis 没有开启 AOF ( append only file )⽅式的持久化。
开启 AOF 持久化后,每执⾏⼀条会 更改 Redis 中的数据的命令 Redis 就会将该命令写⼊硬盘中的 AOF ⽂件,这⼀过程显然 会降低 Redis 的性能 ,但⼤部分情况下这个影响是能够接受的,另外使 ⽤较快 的硬盘可以提⾼ AOF 的性能
redis.conf
# 可以通过修改redis.conf配置⽂件中的appendonly参数开启
appendonly yes # AOF⽂件的保存位置和RDB⽂件的位置相同,都是通过dir参数设置的。
dir ./ # 默认的⽂件名是appendonly.aof,可以通过appendfilename参数修改
appendfilename appendonly.aof
SET 命令来举例说明 RESP 协议的格式。
redis> SET mykey "Hello"
"OK"
实际发送的请求数据:
*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$5\r\nHello\r\n*3
$3
SET
$5
mykey
$5
Hello

3.2.2 同步磁盘数据

Redis 每次更改数据的时候, aof 机制都会将命令记录到 aof ⽂件,但是实际上由于操作系统的 缓存 机制 ,数据并没有实时写⼊到硬盘,⽽是进⼊ 硬盘缓存 。再通过 硬盘缓存机制 去刷新到保存到⽂件。
filesync 命令:
参数说明:
# 每次执⾏写⼊都会进⾏同步, 这个是最安全但是是效率⽐较低的⽅式
appendfsync always # 每⼀秒执⾏(默认)
appendfsync everysec # 不主动进⾏同步操作,由操作系统去执⾏,这个是最快但是最不安全的⽅式
appendfsync no

3.2.3 AOF重写原理(优化AOF⽂件)

set s1 111set s1 222set s1 333set s1 444set s1 444
Redis 可以在 AOF ⽂件体积变得过⼤时,⾃动地在后台对 AOF 进⾏重写。重写后的新 AOF ⽂ 件包含了恢复当前数据集所需的最⼩命令集合。
AOF ⽂件有序地保存了对数据库执⾏的所有写⼊操作, 这些写⼊操作以 Redis 协( RESP )的格式 保存, 因此 AOF ⽂件的内容⾮常容易被⼈读懂, 对⽂件进⾏分析( parse )也很轻松。
AOF重写过程分析(整个重写操作是绝对安全的):

Redis 在创建新 AOF ⽂件的过程中,会继续将命令追加到现有的 AOF ⽂件⾥⾯,即使重写过程中发 ⽣停机,现有的 AOF ⽂件也不会丢失。 ⽽⼀旦新 AOF ⽂件创建完毕, Redis 就会从旧 AOF ⽂件切换 到新 AOF ⽂件,并开始对新 AOF ⽂件进⾏追加操作。

参数说明:
# 表示当前aof⽂件⼤⼩超过上⼀次aof⽂件⼤⼩的百分之多少的时候会进⾏重写。如果之前没有重写过,以启动时aof⽂件⼤⼩为准
auto-aof-rewrite-percentage 100# 限制允许重写最⼩aof⽂件⼤⼩,也就是⽂件⼤⼩⼩于64mb的时候,不需要进⾏优化
auto-aof-rewrite-min-size 64mb

3.2.4 AOF⽂件损坏以后如何修复

问题描述:
服务器可能在程序正在对 AOF ⽂件进⾏写⼊时停机, 如果停机造成了 AOF ⽂件出错
( corrupt ), 那么 Redis 在重启时会拒绝载⼊这个 AOF ⽂件, 从⽽确保数据的⼀致性不会 被破坏。
当发⽣这种情况时, 可以⽤以下⽅法来修复出错的 AOF ⽂件:
1.为现有的 AOF ⽂件创建⼀个备份。
2.使⽤ Redis 附带的 redis-check-aof 程序,对原来的 AOF ⽂件进⾏修复。
redis-check-aof --fix readonly.aof

3.重启 Redis 服务器,等待服务器载⼊修复后的 AOF ⽂件,并进⾏数据恢复。

3.3 如何选择RDBAOF

⼀般来说 , 如果对数据的安全性要求⾮常⾼的话,应该同时使⽤两种持久化功能。
如果可以承受数分钟以内的数据丢失,那么可以只使⽤ RDB 持久化。
有很多⽤户都只使⽤ AOF 持久化, 但并不推荐这种⽅式: 因为定时⽣成 RDB 快照( snapshot )⾮常便于进⾏数据库备份, 并且 RDB 恢复数据集的速度也要⽐ AOF 恢复的速度要快 。
# 禁⽌ RDB ⽅式
save ""
两种持久化策略可以同时使⽤,也可以使⽤其中⼀种。如果同时使⽤的话, 那么 Redis 重启时,会优先使⽤ AOF ⽂件来还原数据。

3.4 如何选择RDBAOF4.0之前的还需要考虑)

内存数据库 rdb redis database +aof 数据不能丢
缓存服务器 rdb
不建议 只使⽤ aof ( 性能差 )
恢复时: 先 aof rdb

3.5 混合持久化⽅式

Redis 4.0 之后新增的⽅式,混合持久化是结合了 RDB AOF 的优点,在写⼊的时候,先把当前 的数据以 RDB 的形式写⼊⽂件的开头,再将后续的操作命令以 AOF 的格式存⼊⽂件,这样既能保证 Redis 重启时的速度,⼜能减低数据丢失的⻛险。
RDB AOF 持久化各有利弊, RDB 可能会导致⼀定时间内的数据丢失,⽽ AOF 由于⽂件较⼤则会影响 Redis 的启动速度,为了能同时拥有 RDB AOF 的优点, Redis 4.0 之后新增了混合持久化的⽅式,因 此我们在必须要进⾏持久化操作时,应该选择混合持久化的⽅式。
查询是否开启混合持久化可以使⽤ config get aof-use-rdb-preamble 命令,执⾏结果
127.0.0.1:6379> config get aof-use-rdb-preamble
1) "aof-use-rdb-preamble"
2) "yes"
其中 yes 表示已经开启混合持久化, no 表示关闭, Redis 5.0 默认值为 yes 。如果是其他版本的 Redis ⾸先需要检查⼀下,是否已经开启了混合持久化,如果关闭的情况下,可以通过以下两种⽅式开启:
通过命令⾏开启
通过修改 Redis 配置⽂件开启

(1) 通过命令⾏开启

使⽤命令 config set aof-use-rdb-preamble yes
命令⾏设置配置的缺点是重启 Redis 服务之后,设置的配置就会失效。

(2) 通过修改 Redis 配置⽂件开启

Redis 的根路径下找到 redis.conf ⽂件,把配置⽂件中的 aof-use-rdb-preamble no 改为 aof-use-rdb-preamble yes
配置完成之后,需要重启 Redis 服务器,配置才能⽣效,但修改配置⽂件的⽅式,在每次重启 Redis 服务之后,配置信息不会丢失。
需要注意的是,在⾮必须进⾏持久化的业务中,可以关闭持久化,这样可以有效的提升 Redis 的运⾏速度,不会出现间歇性卡顿的困扰。
混合持久化 是 RDB+ 指令
如果 AOF ⽂件过⼤时候,会重写:把当前数据已 RDB 格式保存,后续指令⽤ aof

四、Redis主从复制

4.1 什么是主从复制

主对外从对内,主可写从不可写
主挂了,从不可为主

4.2 主从配置

4.2.1 Redis配置

⽆需特殊配置

4.2.2 Redis配置

修改从服务器上的 redis.conf ⽂件:
# slaveof <masterip> <masterport>
# 表示当前【从服务器】对应的【主服务器】的IP是192.168.10.135,端⼝是6379。
slaveof 127.0.0.1 6379
replicaof <masterip> <masterport>

4.2.3 实现原理

Redis 的主从同步,分为 全量同步 增量同步
只有从机第⼀次连接上主机是 全量同步
断线重连有可能触发 全量同步 也有可能是 增量同步( master 判断 runid 是否⼀致)。

               

除此之外的情况都是 增量同步

4.2.4 全量同步

Redis 的全量同步过程主要分三个阶段:
同步快照阶段: Master 创建并发送 快照 RDB Slave Slave 载⼊并解析快照。 Master 同时将此阶段所产⽣的新的写命令存储到缓冲区。
同步写缓冲阶段: Master Slave 同步存储在缓冲区的写操作命令。
同步增量阶段: Master Slave 同步写操作命令。

4.2.5 增量同步

Redis 增量同步主要指 ** Slave 完成初始化后开始正常⼯作时, Master 发⽣的写操作同步到 Slave` 的过 程**
通常情况下, Master 每执⾏⼀个写命令就会向 Slave 发送相同的 写命令 ,然后 Slave 接收并执⾏。

五、Redis哨兵机制

Redis 主从复制的缺点:没有办法对 master 进⾏动态选举( master 宕机后,需要重新选举 master ),需要使⽤ Sentinel 机制完成动态选举。

5.1 简介

Redis 的哨兵模式到了 2.8 版本之后
Sentinel ( 哨兵 ) 进程是⽤于监控 Redis 集群中 Master 主服务器⼯作的状态
Master 主服务器发⽣故障的时候,可以实现 Master Slave 服务器的切换,保证系统的⾼可⽤( HA

5.2 哨兵进程的作用

监控 ( Monitoring ) :哨兵 ( sentinel ) 会不断地检查你的 Master Slave 是否运作正常。
提醒 ( Notification ) : 当被监控的某个 Redis 节点出现问题时 , 哨兵 ( sentinel ) 可以通过 API 向管理员或者其他应⽤程序发送通知。
⾃动故障迁移 ( Automatic failover ) :当⼀个 Master 不能正常⼯作时,哨兵 ( sentinel ) 会开始⼀次⾃动故障迁移操作

5.3 故障判定原理分析

1. 每个 Sentinel (哨兵)进程以 每秒钟⼀次 的频率向整个集群中的 Master 主服务器, Slave 从服务器以及其他 Sentinel (哨兵)进程发送⼀个 PING 命令。
2. 如果⼀个实例( instance )距离最后⼀次有效回复 PING 命令的时间超过 down-after
milliseconds 选项所指定的值, 则这个实例会被 Sentinel (哨兵)进程标记为 主观下线 SDOWN )。
3. 如果⼀个 Master 主服务器被标记为主观下线( SDOWN ),则正在监视这个 Master 主服务器的 Sentinel (哨兵) 进程要以每秒⼀次的频率 确认 Master 主服务器 的确 进⼊了主观下线状态
4.有⾜够数量的 Sentinel (哨兵) 进程(⼤于等于配置⽂件指定的值)在指定的时间范围内确认Master 主服务器进⼊了主观下线状态( SDOWN ), 则 Master 主服务器会被标记为 客观下线 ODOWN
5. 在⼀般情况下, 每个 Sentinel (哨兵)进程会以每 10 秒⼀次的频率向集群中的所有 Master 主服务器、 Slave 从服务器发送 INFO 命令。
6. Master 主服务器被 Sentinel (哨兵)进程标记为 客观下线( ODOWN 时, Sentinel (哨兵)进程向下线的 Master 主服务器的所有 Slave 从服务器发送 INFO 命令的频率会从 10 秒⼀次改为每秒⼀次。
7. 若没有⾜够数量的 Sentinel (哨兵)进程同意 Master 主服务器下线, Master 主服务器的客观下线状态就会被移除。若 Master 主服务器重新向 Sentinel (哨兵)进程发送 PING 命令返回有效回复, Master 主服务器的主观下线状态就会被移除。

5.4 ⾃动故障迁移

1.它会将失效 Master 的其中⼀个 Slave 升级为新的 Master , 并让失效 Master 的其他 Slave 改为复制新的 Master
2.当客户端试图连接失效的 Master 时,集群也会向客户端返回新 Master 的地址,使得集群可以使⽤现在的 Master 替换失效 Master
3.Master Slave 服务器切换后, Master redis.conf Slave redis.conf
sentinel.conf 的配置⽂件的内容都会发⽣相应的改变,即, Master 主服务器的 redis.conf
配置⽂件中会多⼀⾏ slaveof 的配置, sentinel.conf 的监控⽬标会随之调换。

5.5 案例演示

(1) 修改从机的 sentinel.conf

# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以⾃⼰命名的主节点名字 只能由字⺟A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <master ip> <master port> <quorum>
sentinel monitor mymaster 192.168.10.133 6379 1

(2) 其他配置项说明

sentinel.conf
# 哨兵sentinel实例运⾏的端⼝ 默认26379
port 26379
# 哨兵sentinel的⼯作⽬录
dir /tmp
# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以⾃⼰命名的主节点名字 只能由字⺟A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 1
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置⼀样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
# 这个配置项指定了在发⽣failover主备切换时最多可以有多少个slave同时对新的master进⾏ 同步,这个数字越⼩,完成failover所需的时间就越⻓,但是如果这个数字越⼤,就意味着越 多的slave因为replication⽽不可⽤。可以通过将这个值设为 1 来保证每次只有⼀个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
# 故障转移的超时时间 failover-timeout 可以⽤在以下这些⽅⾯:
#1. 同⼀个sentinel对同⼀个master两次failover之间的间隔时间。
#2. 当⼀个slave从⼀个错误的master那⾥同步数据开始计算时间。直到slave被纠正为向正确的master那⾥同步数据时。
#3.当想要取消⼀个正在进⾏的failover所需要的时间。 
#4.当进⾏failover时,配置所有slaves指向新的master所需的最⼤时间。不过,即使过了这个超时,
slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
# SCRIPTS EXECUTION
#配置当某⼀事件发⽣时所需要执⾏的脚本,可以通过脚本来通知管理员,例如当系统运⾏不正常时发邮件通知相关⼈员。
#对于脚本的运⾏结果有以下规则:
#若脚本执⾏后返回1,那么该脚本稍后将会被再次执⾏,重复次数⽬前默认为10
#若脚本执⾏后返回2,或者⽐2更⾼的⼀个返回值,脚本将不会重复执⾏。
#如果脚本在执⾏过程中由于收到系统中断信号被终⽌了,则同返回值为1时的⾏为相同。
#⼀个脚本的最⼤执⾏时间为60s,如果超过这个时间,脚本将会被⼀个SIGKILL信号终⽌,之后重新执⾏。
#通知型脚本:当sentinel有任何警告级别的事件发⽣时(⽐如说redis实例的主观失效和客观失效等等),将会去调⽤这个脚本,这时这个脚本应该通过邮件,SMS等⽅式去通知系统管理员关于系统不正常运⾏的信息。调⽤该脚本时,将传给脚本两个参数,⼀个是事件的类型,⼀个是事件的描述。
#如果sentinel.conf配置⽂件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执⾏的,否则sentinel⽆法正常启动成功。
#通知脚本
# sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh
# 客户端重新配置主节点参数脚本
# 当⼀个master由于failover⽽发⽣改变时,这个脚本将会被调⽤,通知相关的客户端关于master地址已经发⽣改变的信息。
# 以下参数将会在调⽤脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# ⽬前<state>总是“failover”,
# <role>是“leader”或者“observer”中的⼀个。
# 参数 from-ip, from-port, to-ip, to-port是⽤来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通⽤的,能被多次调⽤,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

(3) 通过 redis-sentinel 启动哨兵服务

./redis-sentinel sentinel.conf

六、Redis集群

6.1 Redis的集群策略

twproxy
codis (豌⾖荚)
代理⽅案

6.2 Redis-cluster架构图

Redis3 以后,官⽅的集群⽅案 Redis-Cluster
Redis3 使⽤ lua 脚本实现
Redis5 直接实现

     

架构细节 :
(1) 所有的 redis 主节点彼此互联 ( PING-PONG 机制 ), 内部使⽤⼆进制协议优化传输速度和带宽 .
(2) 节点的 fail 是通过集群中超过半数的节点检测失效时才⽣效 .
(3) 客户端与 redis 节点直连 , 不需要中间 proxy . 客户端不需要连接集群所有节点 , 连接集群中任何⼀个可⽤节点即可
(4)redis-cluster 把所有的物理节点映射到 [0-16383] slot ,cluster 负责维护 node<->slot<->value
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置⼀个 key-value 时, redis 先对key 使⽤ crc16 算法算出⼀个结果,然后把结果对 16384 求余数,这样每个 key 都会对应⼀个编号在 0-16383 之间的哈希槽, redis 会根据节点数量⼤致均等的将哈希槽映射到不同的节点
示例如下:

6.3 Redis-cluster投票:容错

   

1 、主节点投票,如果超过半数的主都认为某主 down 了,则该主就 down 了(主选择单数)
2 、主节点投票,选出挂了的主的从升级为主
注:
集群挂了的情况:
1 、半数的主挂了,不能投票⽣效,则集群挂了
2 、挂了的主机的从也挂了,造成 slot 槽分配不连续( 16384 不能完全分配),集群就挂了

6.4 安装RedisCluster

Redis 集群最少需要三台主服务器,三台从服务器。
端⼝号分别为: 7001~7006

(1) 第⼀步:创建7001实例,并编辑redis.conf⽂件,修改port7001

注意:创建实例,即拷⻉单机版安装时,⽣成的 bin ⽬录,为 7001 ⽬录。

(2) 第⼆步:修改redis.conf配置⽂件,打开cluster-enable yes

(3) 第三步:复制7001,创建7002~7006实例,注意端⼝修改

(4) 第四步:创建start.sh,启动所有的实例

cd 7001
./redis-server redis.conf
cd ..
cd 7002
./redis-server redis.conf
cd ..
cd 7003
./redis-server redis.conf
cd ..
cd 7004
./redis-server redis.conf
cd ..
cd 7005
./redis-server redis.conf
cd ..
cd 7006
./redis-server redis.conf
cd ..
chomd u+x start.sh

(5) 第五步:创建Redis集群

[root@localhost 7001]# ./redis-cli --cluster create 127.0.0.1:7001
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006
--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:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7006 to 127.0.0.1:7002
Adding replica 127.0.0.1:7004 to 127.0.0.1:7003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: af559fc6c82c83dc39d07e2dfe59046d16b6a429 127.0.0.1:7001slots:[0-5460] (5461 slots) master
M: 068b678923ad0858002e906040b0fef6fff8dda4 127.0.0.1:7002slots:[5461-10922] (5462 slots) master
M: d277cd2984639747a17ca79428602480b28ef070 127.0.0.1:7003slots:[10923-16383] (5461 slots) master
S: 51c3ebdd0911dd6564040c7e20b9ae69cabb0425 127.0.0.1:7004replicates af559fc6c82c83dc39d07e2dfe59046d16b6a429
S: e7b1f1962de2a1ffef2bf1ac5d94574b2e4d67d8 127.0.0.1:7005replicates 068b678923ad0858002e906040b0fef6fff8dda4
S: 78dfe773eaa817fb69a405a3863f5b8fcf3e172f 127.0.0.1:7006replicates d277cd2984639747a17ca79428602480b28ef070
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:7001)
M: af559fc6c82c83dc39d07e2dfe59046d16b6a429 127.0.0.1:7001slots:[0-5460] (5461 slots) master1 additional replica(s)
M: d277cd2984639747a17ca79428602480b28ef070 127.0.0.1:7003slots:[10923-16383] (5461 slots) master1 additional replica(s)
S: e7b1f1962de2a1ffef2bf1ac5d94574b2e4d67d8 127.0.0.1:7005slots: (0 slots) slavereplicates 068b678923ad0858002e906040b0fef6fff8dda4
M: 068b678923ad0858002e906040b0fef6fff8dda4 127.0.0.1:7002slots:[5461-10922] (5462 slots) master1 additional replica(s)
S: 51c3ebdd0911dd6564040c7e20b9ae69cabb0425 127.0.0.1:7004slots: (0 slots) slavereplicates af559fc6c82c83dc39d07e2dfe59046d16b6a429
S: 78dfe773eaa817fb69a405a3863f5b8fcf3e172f 127.0.0.1:7006slots: (0 slots) slavereplicates d277cd2984639747a17ca79428602480b28ef070
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
[root@localhost-0723 redis]#

6.5 命令客户端连接集群

命令:
./redis-cli -h 127.0.0.1 -p 7001 -c
注意: -c 表示是以 redis 集群⽅式进⾏连接
[root@localhost redis-cluster]# cd 7001
[root@localhost 7001]# ./redis-cli -h 127.0.0.1 -p 7001 -c
127.0.0.1:7001> set name1 aaa
-> Redirected to slot [12933] located at 127.0.0.1:7003
OK
127.0.0.1:7003>

6.6 查看集群的命令

(1) 查看集群状态

127.0.0.1:7003> 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:3
cluster_stats_messages_sent:926
cluster_stats_messages_received:926

(2) 查看集群中的节点:

127.0.0.1:7003> cluster nodes
d277cd2984639747a17ca79428602480b28ef070 127.0.0.1:7003@17003 myself,master - 0
1570457306000 3 connected 10923-16383
af559fc6c82c83dc39d07e2dfe59046d16b6a429 127.0.0.1:7001@17001 master - 0
1570457307597 1 connected 0-5460
e7b1f1962de2a1ffef2bf1ac5d94574b2e4d67d8 127.0.0.1:7005@17005 slave
068b678923ad0858002e906040b0fef6fff8dda4 0 1570457308605 5 connected
068b678923ad0858002e906040b0fef6fff8dda4 127.0.0.1:7002@17002 master - 0
1570457309614 2 connected 5461-10922
51c3ebdd0911dd6564040c7e20b9ae69cabb0425 127.0.0.1:7004@17004 slave
af559fc6c82c83dc39d07e2dfe59046d16b6a429 0 1570457307000 4 connected
78dfe773eaa817fb69a405a3863f5b8fcf3e172f 127.0.0.1:7006@17006 slave
d277cd2984639747a17ca79428602480b28ef070 0 1570457309000 6 connected
127.0.0.1:7003>

6.7 维护节点

集群创建成功后可以继续向集群中添加节点

6.7.1 添加主节点

先创建 7007 节点
添加 7007 结点作为新节点 , 并启动
执⾏命令:
[root@localhost 7007]# ./redis-cli --cluster add-node 127.0.0.1:7007
127.0.0.1:7001
>>> Adding node 127.0.0.1:7007 to cluster 127.0.0.1:7001
>>> Performing Cluster Check (using node 127.0.0.1:7001)
M: af559fc6c82c83dc39d07e2dfe59046d16b6a429 127.0.0.1:7001slots:[0-5460] (5461 slots) master1 additional replica(s)
M: d277cd2984639747a17ca79428602480b28ef070 127.0.0.1:7003slots:[10923-16383] (5461 slots) master1 additional replica(s)
S: e7b1f1962de2a1ffef2bf1ac5d94574b2e4d67d8 127.0.0.1:7005slots: (0 slots) slavereplicates 068b678923ad0858002e906040b0fef6fff8dda4
M: 068b678923ad0858002e906040b0fef6fff8dda4 127.0.0.1:7002slots:[5461-10922] (5462 slots) master1 additional replica(s)
S: 51c3ebdd0911dd6564040c7e20b9ae69cabb0425 127.0.0.1:7004slots: (0 slots) slavereplicates af559fc6c82c83dc39d07e2dfe59046d16b6a429
S: 78dfe773eaa817fb69a405a3863f5b8fcf3e172f 127.0.0.1:7006
slots: (0 slots) slavereplicates d277cd2984639747a17ca79428602480b28ef070
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 127.0.0.1:7007 to make it join the cluster.
[OK] New node added correctly.
查看集群结点发现 7007 已添加到集群中
127.0.0.1:7001> cluster nodes
d277cd2984639747a17ca79428602480b28ef070 127.0.0.1:7003@17003 master - 0
1570457568602 3 connected 10923-16383
50b073163bc4058e89d285dc5dfc42a0d1a222f2 127.0.0.1:7007@17007 master - 0
1570457567000 0 connected
e7b1f1962de2a1ffef2bf1ac5d94574b2e4d67d8 127.0.0.1:7005@17005 slave
068b678923ad0858002e906040b0fef6fff8dda4 0 1570457569609 5 connected
068b678923ad0858002e906040b0fef6fff8dda4 127.0.0.1:7002@17002 master - 0
1570457566000 2 connected 5461-10922
51c3ebdd0911dd6564040c7e20b9ae69cabb0425 127.0.0.1:7004@17004 slave
af559fc6c82c83dc39d07e2dfe59046d16b6a429 0 1570457567000 4 connected
af559fc6c82c83dc39d07e2dfe59046d16b6a429 127.0.0.1:7001@17001 myself,master - 0
1570457567000 1 connected 0-5460
78dfe773eaa817fb69a405a3863f5b8fcf3e172f 127.0.0.1:7006@17006 slave
d277cd2984639747a17ca79428602480b28ef070 0 1570457567593 6 connected

6.7.2 hash槽重新分配(数据迁移)

添加完主节点需要对主节点进⾏ hash 槽分配,这样该主节才可以存储数据。
查看集群中槽占⽤情况
cluster nodes
redis 集群有 16384 个槽,集群中的每个结点分配⾃已槽,通过查看集群结点可以看到槽占⽤情况。
127.0.0.1:7001> cluster nodes
d277cd2984639747a17ca79428602480b28ef070 127.0.0.1:7003@17003 master - 0
1570457568602 3 connected 10923-16383
50b073163bc4058e89d285dc5dfc42a0d1a222f2 127.0.0.1:7007@17007 master - 0
1570457567000 0 connected
e7b1f1962de2a1ffef2bf1ac5d94574b2e4d67d8 127.0.0.1:7005@17005 slave
068b678923ad0858002e906040b0fef6fff8dda4 0 1570457569609 5 connected
068b678923ad0858002e906040b0fef6fff8dda4 127.0.0.1:7002@17002 master - 0
1570457566000 2 connected 5461-10922
51c3ebdd0911dd6564040c7e20b9ae69cabb0425 127.0.0.1:7004@17004 slave
af559fc6c82c83dc39d07e2dfe59046d16b6a429 0 1570457567000 4 connected
af559fc6c82c83dc39d07e2dfe59046d16b6a429 127.0.0.1:7001@17001 myself,master - 0
1570457567000 1 connected 0-5460
78dfe773eaa817fb69a405a3863f5b8fcf3e172f 127.0.0.1:7006@17006 slave
d277cd2984639747a17ca79428602480b28ef070 0 1570457567593 6 connected
给刚添加的 7007 结点分配槽
(1)第⼀步:连接上集群(连接集群中任意⼀个可⽤结点都⾏)
[root@localhost 7007]# ./redis-cli --cluster reshard 127.0.0.1:7007
>>> Performing Cluster Check (using node 127.0.0.1:7007)
M: 50b073163bc4058e89d285dc5dfc42a0d1a222f2 127.0.0.1:7007slots: (0 slots) master
S: 51c3ebdd0911dd6564040c7e20b9ae69cabb0425 127.0.0.1:7004slots: (0 slots) slavereplicates af559fc6c82c83dc39d07e2dfe59046d16b6a429
S: 78dfe773eaa817fb69a405a3863f5b8fcf3e172f 127.0.0.1:7006slots: (0 slots) slavereplicates d277cd2984639747a17ca79428602480b28ef070
S: e7b1f1962de2a1ffef2bf1ac5d94574b2e4d67d8 127.0.0.1:7005slots: (0 slots) slavereplicates 068b678923ad0858002e906040b0fef6fff8dda4
M: af559fc6c82c83dc39d07e2dfe59046d16b6a429 127.0.0.1:7001slots:[0-5460] (5461 slots) master1 additional replica(s)
M: 068b678923ad0858002e906040b0fef6fff8dda4 127.0.0.1:7002slots:[5461-10922] (5462 slots) master1 additional replica(s)
M: d277cd2984639747a17ca79428602480b28ef070 127.0.0.1:7003slots:[10923-16383] (5461 slots) master1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

(2) 第⼆步:输⼊要分配的槽数量

How many slots do you want to move (from 1 to 16384)? 3000
输⼊: 3000 ,表示要给⽬标节点分配 3000 个槽

(3) 第三步:输⼊接收槽的结点id

What is the receiving node ID?
输⼊: 50b073163bc4058e89d285dc5dfc42a0d1a222f2
PS:这⾥准备给7007分配槽,通过cluster nodes查看7007结点id为:
50b073163bc4058e89d285dc5dfc42a0d1a222f2

(4) 第四步:输⼊源结点id

Please enter all the source node IDs.Type 'all' to use all the nodes as source nodes for the hash slots.Type 'done' once you entered all the source nodes IDs.
输⼊: all

(5) 第五步:输⼊yes开始移动槽到⽬标结点id

输⼊: yes
Moving slot 11899 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11900 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11901 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11902 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11903 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11904 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11905 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11906 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11907 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11908 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11909 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11910 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11911 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11912 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11913 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11914 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11915 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11916 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11917 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11918 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11919 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11920 from 127.0.0.1:7003 to 127.0.0.1:7007:
Moving slot 11921 from 127.0.0.1:7003 to 127.0.0.1:7007:

(6) 查看结果

127.0.0.1:7001> cluster nodes
d277cd2984639747a17ca79428602480b28ef070 127.0.0.1:7003@17003 master - 0
1570458285557 3 connected 11922-16383
50b073163bc4058e89d285dc5dfc42a0d1a222f2 127.0.0.1:7007@17007 master - 0
1570458284000 7 connected 0-998 5461-6461 10923-11921
e7b1f1962de2a1ffef2bf1ac5d94574b2e4d67d8 127.0.0.1:7005@17005 slave
068b678923ad0858002e906040b0fef6fff8dda4 0 1570458283000 5 connected
068b678923ad0858002e906040b0fef6fff8dda4 127.0.0.1:7002@17002 master - 0
1570458284546 2 connected 6462-10922
51c3ebdd0911dd6564040c7e20b9ae69cabb0425 127.0.0.1:7004@17004 slave
af559fc6c82c83dc39d07e2dfe59046d16b6a429 0 1570458283538 4 connected
af559fc6c82c83dc39d07e2dfe59046d16b6a429 127.0.0.1:7001@17001 myself,master - 0
1570458283000 1 connected 999-5460
78dfe773eaa817fb69a405a3863f5b8fcf3e172f 127.0.0.1:7006@17006 slave
d277cd2984639747a17ca79428602480b28ef070 0 1570458284000 6 connected

6.7.3 添加从节点

添加 7008 从结点,将 7008 作为 7007 的从结点
命令:
./redis-cli --cluster add-node 新节点的ip和端⼝ 旧节点ip和端⼝ --cluster-slave --cluster-master-id 主节点id
例如:
./redis-cli --cluster add-node 127.0.0.1:7008 127.0.0.1:7007 --cluster-slave --cluster-master-id 50b073163bc4058e89d285dc5dfc42a0d1a222f2
50b073163bc4058e89d285dc5dfc42a0d1a222f2 7007 结点的 id ,可通过 cluster nodes 查看。
[root@localhost 7008]# ./redis-cli --cluster add-node 127.0.0.1:7008
127.0.0.1:7007 --cluster-slave --cluster-master-id
50b073163bc4058e89d285dc5dfc42a0d1a222f2
>>> Adding node 127.0.0.1:7008 to cluster 127.0.0.1:7007
>>> Performing Cluster Check (using node 127.0.0.1:7007)
M: 50b073163bc4058e89d285dc5dfc42a0d1a222f2 127.0.0.1:7007slots:[0-998],[5461-6461],[10923-11921] (2999 slots) master
S: 51c3ebdd0911dd6564040c7e20b9ae69cabb0425 127.0.0.1:7004slots: (0 slots) slavereplicates af559fc6c82c83dc39d07e2dfe59046d16b6a429
S: 78dfe773eaa817fb69a405a3863f5b8fcf3e172f 127.0.0.1:7006slots: (0 slots) slavereplicates d277cd2984639747a17ca79428602480b28ef070
S: e7b1f1962de2a1ffef2bf1ac5d94574b2e4d67d8 127.0.0.1:7005slots: (0 slots) slavereplicates 068b678923ad0858002e906040b0fef6fff8dda4
M: af559fc6c82c83dc39d07e2dfe59046d16b6a429 127.0.0.1:7001slots:[999-5460] (4462 slots) master1 additional replica(s)
M: 068b678923ad0858002e906040b0fef6fff8dda4 127.0.0.1:7002slots:[6462-10922] (4461 slots) master1 additional replica(s)
M: d277cd2984639747a17ca79428602480b28ef070 127.0.0.1:7003slots:[11922-16383] (4462 slots) master1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 127.0.0.1:7008 to make it join the cluster.
Waiting for the cluster to join
.....
>>> Configure node as replica of 127.0.0.1:7007.
[OK] New node added correctly.
注意:如果原来该结点在集群中的配置信息已经⽣成到 cluster-config-file 指定的配置⽂件中(如果
cluster-config-file 没有指定则默认为 nodes.conf ),这时可能会报错:
[ERR] Node XXXXXX is not empty. Either the node already knows other nodes
(check with CLUSTER NODES) or contains some key in database 0
解决⽅法是删除⽣成的配置⽂件 nodes.conf ,删除后再执⾏ ./redis-cli --cluster add-node 指令
查看集群中的结点,刚添加的 7008 7007 的从节点:
[root@localhost 7008]# ./redis-cli -h 127.0.0.1 -p 7001 -c
127.0.0.1:7001> cluster nodes
d277cd2984639747a17ca79428602480b28ef070 127.0.0.1:7003@17003 master - 0
1570458650720 3 connected 11922-16383
c3272565847bf9be8ae0194f7fb833db40b98ac4 127.0.0.1:7008@17008 slave
50b073163bc4058e89d285dc5dfc42a0d1a222f2 0 1570458648710 7 connected
50b073163bc4058e89d285dc5dfc42a0d1a222f2 127.0.0.1:7007@17007 master - 0
1570458649000 7 connected 0-998 5461-6461 10923-11921
e7b1f1962de2a1ffef2bf1ac5d94574b2e4d67d8 127.0.0.1:7005@17005 slave
068b678923ad0858002e906040b0fef6fff8dda4 0 1570458650000 5 connected
068b678923ad0858002e906040b0fef6fff8dda4 127.0.0.1:7002@17002 master - 0
1570458649715 2 connected 6462-10922
51c3ebdd0911dd6564040c7e20b9ae69cabb0425 127.0.0.1:7004@17004 slave
af559fc6c82c83dc39d07e2dfe59046d16b6a429 0 1570458648000 4 connected
af559fc6c82c83dc39d07e2dfe59046d16b6a429 127.0.0.1:7001@17001 myself,master - 0
1570458650000 1 connected 999-5460
78dfe773eaa817fb69a405a3863f5b8fcf3e172f 127.0.0.1:7006@17006 slave
d277cd2984639747a17ca79428602480b28ef070 0 1570458651725 6 connected
127.0.0.1:7001>

6.7.4 删除结点

命令:
./redis-cli --cluster del-node 127.0.0.1:7008
41592e62b83a8455f07f7797f1d5c071cffedb50
删除已经占有 hash 槽的结点会失败,报错如下:
[ERR] Node 127.0.0.1:7008 is not empty! Reshard data away and try again.
需要将该结点占⽤的 hash 槽分配出去(参考 hash 槽重新分配章节)。

6.8 Jedis连接集群

需要开启防⽕墙,或者直接关闭防⽕墙。
service iptables stop

6.8.1 代码实现

创建 JedisCluster 类连接 redis 集群
@Test
public void testJedisCluster() throws Exception {//创建⼀连接,JedisCluster对象,在系统中是单例存在Set<HostAndPort> nodes = new HashSet<>();nodes.add(new HostAndPort("192.168.10.133", 7001));nodes.add(new HostAndPort("192.168.10.133", 7002));nodes.add(new HostAndPort("192.168.10.133", 7003));nodes.add(new HostAndPort("192.168.10.133", 7004));nodes.add(new HostAndPort("192.168.10.133", 7005));nodes.add(new HostAndPort("192.168.10.133", 7006));JedisCluster cluster = new JedisCluster(nodes);//执⾏JedisCluster对象中的⽅法,⽅法和redis⼀⼀对应。cluster.set("cluster-test", "my jedis cluster test");String result = cluster.get("cluster-test");System.out.println(result);//程序结束时需要关闭JedisCluster对象cluster.close();
}

6.8.2 使⽤spring

配置 applicationContext.xml
<!-- 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"><!-- 最⼤连接数 --><property name="maxTotal" value="30" /><!-- 最⼤空闲连接数 --><property name="maxIdle" value="10" /><!-- 每次释放连接的最⼤数⽬ --><property name="numTestsPerEvictionRun" value="1024" /><!-- 释放连接的扫描间隔(毫秒) --><property name="timeBetweenEvictionRunsMillis" value="30000" /><!-- 连接最⼩空闲时间 --><property name="minEvictableIdleTimeMillis" value="1800000" /><!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最⼤空闲连接数 时直接释放 --><property name="softMinEvictableIdleTimeMillis" value="10000" /><!-- 获取连接时的最⼤等待毫秒数,⼩于零:阻塞不确定的时间,默认-1 --><property name="maxWaitMillis" value="1500" /><!-- 在获取连接的时候检查有效性, 默认false --><property name="testOnBorrow" value="true" /><!-- 在空闲时检查有效性, 默认false --><property name="testWhileIdle" value="true" /><!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true --><property name="blockWhenExhausted" value="false" />
</bean>
<!-- redis集群 -->
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster"><constructor-arg index="0"><set><bean class="redis.clients.jedis.HostAndPort"><constructor-arg index="0" value="192.168.101.3"></constructor-arg><constructor-arg index="1" value="7001"></constructor-arg></bean><bean class="redis.clients.jedis.HostAndPort"><constructor-arg index="0" value="192.168.101.3"></constructor-arg><constructor-arg index="1" value="7002"></constructor-arg></bean><bean class="redis.clients.jedis.HostAndPort"><constructor-arg index="0" value="192.168.101.3"></constructor-arg><constructor-arg index="1" value="7003"></constructor-arg></bean><bean class="redis.clients.jedis.HostAndPort"><constructor-arg index="0" value="192.168.101.3"></constructor-arg><constructor-arg index="1" value="7004"></constructor-arg></bean><bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.101.3"></constructor-arg><constructor-arg index="1" value="7005"></constructor-arg></bean><bean class="redis.clients.jedis.HostAndPort"><constructor-arg index="0" value="192.168.101.3"></constructor-arg><constructor-arg index="1" value="7006"></constructor-arg></bean></set></constructor-arg><constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
</bean>
Ø 测试代码
private ApplicationContext applicationContext;@Beforepublic void init() {applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");}// redis集群@Testpublic void testJedisCluster() {JedisCluster jedisCluster = (JedisCluster) applicationContext.getBean("jedisCluster");jedisCluster.set("name", "zhangsan");String value = jedisCluster.get("name");System.out.println(value);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/102621.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实时操作系统Freertos开坑学习笔记:(八):信号量、事件标志组、任务通知机制

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、信号量的简介1.信号量与队列的区别&#xff1f; 二、二值信号量及其实例1.什么是二值信号量2.二值信号量相关API函数3.二值信号量实例 三、计数型信号量四、…

vscode宏键绑定

开发语言php 实现输入[ 得到 [];的效果 [win]ctrlp,[mac]superp 输入>keyboard 选择 在json文件里增加(目前有缺陷,sublime的设置是比较完美的.或者phpstorm默认不需要配置): {"key": "[","command": "editor.action.insertSnippet&…

Spark2x原理剖析(二)

一、概述 基于社区已有的JDBCServer基础上&#xff0c;采用多主实例模式实现了其高可用性方案。集群中支持同时共存多个JDBCServer服务&#xff0c;通过客户端可以随机连接其中的任意一个服务进行业务操作。即使集群中一个或多个JDBCServer服务停止工作&#xff0c;也不影响用…

【设计模式】一、设计模式七大原则

文章目录 设计模式概述设计模式七大原则设计模式的目的设计模式七大原则1. 单一职责原则2. 接口隔离原则3. 依赖倒转(倒置)原则4. 里氏替换原则5. 开闭原则&#xff08;Open-Closed Principle简称OCP原则&#xff09;6. 迪米特法则7. 合成复用原则&#xff08;Composite Reuse …

【教程】安防监控/视频存储/视频汇聚平台EasyCVR接入智能分析网关V4的操作步骤

TSINGSEE青犀AI边缘计算网关硬件 —— 智能分析网关目前有5个版本&#xff1a;V1、V2、V3、V4、V5&#xff0c;每个版本都能实现对监控视频的智能识别和分析&#xff0c;支持抓拍、记录、告警等&#xff0c;每个版本在算法模型及性能配置上略有不同。硬件可实现的AI检测包括&am…

【Two Stream network (Tsn)】(二) 阅读笔记

贡献 将深度神经网络应用于视频动作识别的难点&#xff0c;是如何同时利用好静止图像上的 appearance information以及物体之间的运动信息motion information。本文主要有三点贡献&#xff1a; 1.提出了一种融合时间流和空间流的双流网络&#xff1b; 2.证明了直接在光流上训…

生物通路数据库收录1600+整合的经典通路

生物通路数据库为科学家提供了关于生物通路的大量信息和资源&#xff0c;特别是在数据整合、信息检索、数据可视化分析、数据交互、生物学研究等方面&#xff0c;积极推动了生物学研究和科学的发展。 世界各地正在创建各种类型的通路数据库&#xff0c;每个数据库都反映了其创…

C++将派生类赋值给基类

在 C/C++ 中经常会发生数据类型的转换,例如将 int 类型的数据赋值给 float 类型的变量时,编译器会先把 int 类型的数据转换为 float 类型再赋值;反过来,float 类型的数据在经过类型转换后也可以赋值给 int 类型的变量。 数据类型转换的前提是,编译器知道如何对数据进行取舍…

bean的管理-bean的获取

获取bean 默认情况下&#xff0c;在Spring项目启动时&#xff0c;会把bean都创建好&#xff08;但是还会受到作用域及延迟初始化的影响&#xff09;放在IOC容器中&#xff0c;如果想主动获取这些bean&#xff0c;可以通过如下方式 根据name获取bean Object getBean&#xff08…

Text-to-SQL小白入门(四)指令进化大模型WizardLM

摘要 本文主要对大模型WizardLM的基本信息进行了简单介绍&#xff0c;展示了WizardLM取得的优秀性能&#xff0c;分析了论文的核心——指令进化方法。 论文概述 基本信息 英文标题&#xff1a;WizardLM: Empowering Large Language Models to Follow Complex Instructions中…

HR:“我们不收有外包工作经验的项目经理”

大家好&#xff0c;我是老原。 现在&#xff0c;越来越多的项目经理小白以合同工的形式卷入大厂。 一部分是想要转行到项目经理&#xff0c;另一部分是想去某个大厂工作&#xff0c;但是碍于现在够不着这个岗位或者意向大厂的招聘标准&#xff0c;就会选择进入外包公司。 本…