Java组件总目录
Redis 使用及哨兵集群
一 Redis基本数据类型使用场景
redis:应用的场景高并发
所有数据都是保存在内存中。存取速度快,适合于做缓存。
一些重要数据一定要保存到关系型数据库中,例如mysql。
适合做缓存的: string, hash
1 String
应用: JSONstring 缓存功能 计数器 共享用户Session 分布式锁 setnx
2 List 存储列表结构
粉丝列表,文章评论列表, lrange 基于Redis实现简单的高性能分页, 简单的消息队列
3 Hash
应用: 爆品 商品 秒杀仓库 秒杀商品 商品1 库存量
4 Set
基于Redis进行全局的Set去重 共同好友 你可能认识
5 Zset
排行榜,有权重的消息队列 热搜 前面是名称 后面试热度值
二级目录
三级目录
二 Redis 的补充数据类型
1 BitMap
BitMap 就是通过一个 bit 位来表示某个元素对应的值或者状态, 其中的 key 就是对应元素本身,实际上底层也是通过对字符串的操作来实现。Redis 从 2.2 版本之后新增了setbit, getbit, bitcount 等几个bitmap 相关命令。虽然是新命令,但是本身都是对字符串的操作,我们先来看看语法
# 其中 offset 必须是数字,value 只能是 0 或者 1,offset参数要求大于或等于 0,
# 并且小于 2^32(4,294,967,296)这将位图限制在 512MB
SETBIT key offset value
设置最后一个可能的位时(偏移量等于 2^32-1)和存储在键中的字符串值尚不保存字符串值,或持有一个小字符串值,Redis 需要分配所有中间内存,这些中间内存可能会阻塞服务器一段时间。完成第一次分配后,对同一key的 SETBIT的后续调用将无内存分配开销。
通过 bitcount可以很快速的统计,比传统的关系型数据库效率高很多。
BITOP operation destkey key [key …]
对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种:
2 HyperLogLog
Redis 在 2.8.9 版本添加了 HyperLogLog 结构。Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以
HyperLogLog 不能像集合那样,返回输入的各个元素。
这个数据结构的命令有三个:PFADD、PFCOUNT、PFMERGE
用途:记录网站IP注册数,每日访问的IP数,页面实时UV、在线用户人数
局限性:只能统计数量,没有办法看具体信息。
3 Geospatial
底层数据结构 Zset。可以用来保存地理位置,并作位置距离计算或者根据半径计算位置等。有没有想过用Redis来实现附近的人?或者计算最优地图路径?Geo本身不是一种数据结构,它本质上还是借助于Sorted Set(ZSET)
Redis GEO 操作方法有:
- geoadd:添加地理位置的坐标。
- geopos:获取地理位置的坐标。
- geodist:计算两个位置之间的距离。
- georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
- georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
- geohash:返回一个或多个位置对象的 geohash 值。
三 Redis 消息模式 (了解)
消息模式差于专门的消息系统 Kafka、 RabbitMQ。消息有可能丢失, 可用于能够接受消息丢失的场景。如 过滤等。
3.1 队列模式
使用list类型的lpush和rpop实现消息队列。
注意事项:
- 消息接收方如果不知道队列中是否有消息,会一直发送rpop命令,如果这样的话,会每一次都建立一次连接,这样显然不好。
- 可以使用brpop命令,它如果从队列中取不出来数据,会一直阻塞,在一定范围内没有取出则 返回null
缺点:
- 做消费者确认ACK麻烦,不能保证消费者消费消息后是否成功处理的问题(宕机或处理异常等),通常需要维护一个Pending列表,保证消息处理确认。
- 不能做广播模式,如pub/sub,消息发布/订阅模型
- 不能重复消费,一旦消费就会被删除
- 不支持分组消费
3.2 发布订阅模式
- SUBSCRIBE,用于订阅信道
- PUBLISH,向信道发送消息
- UNSUBSCRIBE,取消订阅
此模式允许生产者只生产一次消息,由中间件负责将消息复制到多个消息队列,每个消息队列由对应的消费组消费。 理解为群发消息。
四 Redis Stream
Redis 5.0 全新的数据类型:streams,官方把它定义为:以更抽象的方式建模日志的数据结构。由于推出比较迟,应用相对较少。Redis的streams主要是一个append only(AOF)的数据结构,至少在概念上它是一种在内存中表示的抽象数据类型,只不过它们实现了更强大的操作,以克服日志文件本身的限制。
如果你了解MQ,那么可以把streams当做基于内存的MQ。如果你还了解kafka,那么甚至可以把streams当做基于内存的kafka。listpack存储信息,Rax组织listpack 消息链表listpack是对ziplist的改进,它比ziplist少了一个定位最后一个元素的属性。
streams数据结构本身非常简单,但是streams依然是Redis到目前为止最复杂的类型,其原因是实现的一些额外的功能:一系列的阻塞操作允许消费者等待生产者加入到streams的新数据。另外还有一个称为Consumer Groups的概念,Consumer Group概念最先由kafka提出,Redis有一个类似实现,和 kafka的Consumer Groups的目的是一样的:允许一组客户端协调消费相同的信息流!
一个组可以有多个消费者,加快了消息的消费。
4.1 消息队列相关命令:
- XADD - 添加消息到末尾
- XTRIM - 对流进行修剪,限制长度
- XDEL - 删除消息
- XLEN - 获取流包含的元素数量,即消息长度
- XRANGE - 获取消息列表,会自动过滤已经删除的消息
- XREVRANGE - 反向获取消息列表,ID 从大到小
- XREAD - 以阻塞或非阻塞方式获取消息列表
4.2 消费者组相关命令:
- XGROUP CREATE - 创建消费者组
- XREADGROUP GROUP - 读取消费者组中的消息
- XACK - 将消息标记为"已处理"
- XGROUP SETID - 为消费者组设置新的最后递送消息ID
- XGROUP DELCONSUMER - 删除消费者
- XGROUP DESTROY - 删除消费者组
- XPENDING - 显示待处理消息的相关信息
- XCLAIM - 转移消息的归属权
- XINFO - 查看流和消费者组的相关信息;
- XINFO GROUPS - 打印消费者组的信息;
- XINFO STREAM - 打印流信息
五 Redis 事务
严格意义上说 redis事务只是个批处理 有隔离性 但是没有原子性。
- Redis 的事务是通过 MULTI(事务开始) 、 EXEC(事务执行,并关闭) 、 DISCARD(清空事务,并关闭) 和 WATCH(设置个事务监控的键, 键改变时不执行事务,增加一个事务执行的开关) 、UNWATCH (清除监控的键)这五个命令来完成的。
- Redis 的单个命令都是原子性的,所以这里需要确保事务性的对象是命令集合。 Redis 将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
- Redis 不支持回滚操作。
5.1 事务失败处理
Redis 语法错误
语法错误会将整个事务的命令在队列里都清除。
Redis 运行错误
在队列里正确的命令可以执行 (弱事务性)
- 1、在队列里正确的命令可以执行 (非原子操作)
- 2、不支持回滚
Redis 不支持事务回滚(为什么呢)
- 1、大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的。
- 2、 Redis 为了性能方面就忽略了事务回滚。 (Mysql 使用redo-log 实现回滚)
六 Redis 和lua 整合
Redis整合lua是对Redis事务的补充。lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
6.1 Redis中使用lua的好处
- 减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行
- 原子操作,redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。换句话说,编写脚
本的过程中无需担心会出现竞态条件。 隔离性 - 复用性,客户端发送的脚本会永远存储在redis中,这意味着其他客户端可以复用这一脚本来完成同样的逻辑
6.2 EVAL命令
在redis客户端中,执行以下命令:
EVAL script numkeys key [key ...] arg [arg ...]
eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
命令说明:
-
script参数:是一段Lua脚本程序,它会被运行在Redis服务器上下文中,这段脚本不必(也不应该)定义为一个Lua函数。
-
numkeys参数:用于指定键名参数的个数。
-
key [key …]参数: 从EVAL的第三个参数开始算起,使用了numkeys个键(key),表示在脚本中所用到的那些Redis键(key),这些键名参数可以在Lua中通过全局变量KEYS数组,用1为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
-
arg [arg …]参数:可以在Lua中通过全局变量ARGV数组访问,访问的形式和KEYS变量类似(ARGV[1] 、 ARGV[2] ,诸如此类)。
6.3 lua 脚本调用Redis 命令
redis.call();
返回值就是redis命令执行的返回值。如果出错,返回错误信息,不继续执行
redis.pcall();
返回值就是redis命令执行的返回值,如果出错了 记录错误信息,继续执行
注意事项: 在脚本中,使用return语句将返回值返回给客户端,如果没有return,则返回nil
redis-cli --eval
可以使用redis-cli --eval命令指定一个lua脚本文件去执行。
脚本文件(redis.lua),内容如下:
local num = redis.call('GET', KEYS[1]);
if not num then
return 0;
else
local res = num * ARGV[1];
redis.call('SET',KEYS[1], res);
return res;
end
在redis客户机,执行脚本命令:
# 之前设置在redis中设置过 a 为 1
[root@localhost bin]# ./redis-cli --eval redis.lua a , 8
(integer) 8
[root@localhost bin]# ./redis-cli --eval redis.lua lua:incrbyml , 8
(integer) 0
# incr 递增数字
[root@localhost bin]# ./redis-cli incr lua:incrbyml
(integer) 1
[root@localhost bin]# ./redis-cli --eval redis.lua lua:incrbyml , 8
(integer) 8
- –eval:告诉redis客户端去执行后面的lua脚本
- redis.lua:具体的lua脚本文件名称
- lua:incrbymul : lua脚本中需要的key,具体的key值
- 8:lua脚本中需要的value
上面命令中keys和values中间需要使用逗号隔开,并且逗号两边都要有空格。 lua:incrbyml , 8
执行.lua脚本 不需要写key的个数
七 Redis 持久化(重点)
7.1 RDB
RDB 是 Redis 默认采用的持久化方式。
RDB 方式是通过快照( snapshotting )完成的,当符合一定条件时 Redis 会自动将内存中的数据进行快照并持久化到硬盘。 (将当前内存的数据保存。)
触发RDB快照的时机:
- 符合指定配置的快照规则
- 执行save或bgsave命令 save主线程去快照 bgsave 调用异步线程去快照
主线程是单线程 4.0 I/O操作 已经有多线程概念 - 执行flushall 或flushdb
- 执行主从复制操作
设置快照规则:
save 多少秒内 数据变了多少
save “” : 不使用RDB存储
save 900 1 : 表示15分钟(900秒钟)内至少1个键被更改则进行快照。
save 300 10 : 表示5分钟(300秒)内至少10个键被更改则进行快照。
save 60 10000 :表示1分钟内至少10000个键被更改则进行快照。
RDB快照的实现原理
- Redis 调用系统中的 fork 函数复制一份当前进程的副本(子进程)
- 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件。
- 当子进程写入完所有数据后会用该临时文件替换旧的 RDB 文件,至此,一次快照操作完成。
fork 是操作系统的函数 linux/ uinx *nux, fork-调用一个子进程,制作快照,生成RDB文件, 替换掉原来的RDB文件.
注意事项
- Redis 在进行快照的过程中不会修改 RDB 文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时候 RDB 文件都是完整的。
- 这就使得我们可以通过定时备份 RDB 文件来实现 Redis 数据库的备份, RDB 文件是 经过压缩的二进制文件 ,占用的空间会小于内存中的数据,更加利于传输。
RDB优缺点:
- 缺点:使用 RDB 方式实现持久化,一旦 Redis 异常退出,就会丢失最后一次快照以后更改的所有数
据。这个时候我们就需要根据具体的应用场景,通过组合设置自动快照条件的方式来将可能发生的
数据损失控制在能够接受范围。如果数据相对来说比较重要,希望将损失降到最小,则可以使用
AOF 方式进行持久化 - 优点: RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进
程,然后这个子进程就会处理接下来的所有保存工作,父进程无需执行任何磁盘 I/O 操作。同时这
个也是一个缺点,如果数据集比较大的时候, fork 可以能比较耗时,造成服务器在一段时间内停
止处理客户端的请求;
7.2 AOF
1 AOF介绍
默认情况下 Redis 没有开启 AOF ( append only file )方式的持久化。
开启 AOF 持久化后,每执行一条会更改 Redis 中的数据的命令, Redis 就会将该命令写入硬盘中的 AOF文件,这一过程显然会降低 Redis 的性能,但大部分情况下这个影响是能够接受的,另外使用较快的硬盘可以提高 AOF 的性能。
# 可以通过修改redis.conf配置文件中的appendonly参数开启
appendonly yes
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的。
dir ./
# 默认的文件名是appendonly.aof,可以通过appendfilename参数修改
appendfilename appendonly.aof
2 同步磁盘数据
Redis 每次更改数据的时候, aof 机制都会将命令记录到 aof 文件,但是实际上由于操作系统的缓存机制,数据并没有实时写入到硬盘,而是进入硬盘缓存。再通过硬盘缓存机制去刷新到保存到文件。
最开始的数据它是通过读取内存中的数据,转换成命令。后边的就是每次的更新指令,进行存储。
# 每次执行写入都会进行同步, 这个是最安全但是是效率比较低的方式
appendfsync always
# 每一秒执行(默认)
appendfsync everysec
# 不主动进行同步操作,由操作系统去执行,这个是最快但是最不安全的方式
appendfsync no
3 AOF重写原理(优化AOF文件)
Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写。重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。AOF进行优化时候,并不是读取原来的AOF文件 ,而是读取redis db 进行转化。
AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议(RESP)的格式
保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析( parse )也很轻松。
4 如何选择RDB和AOF
- 内存数据库 rdb(redis database)+aof 数据不能丢
- 缓存服务器 rdb
- 不建议 只使用 aof (性能差)
- 恢复时: 先aof再rdb
7.3 混合持久化方式
RDB 和 AOF 持久化各有利弊,RDB 可能会导致一定时间内的数据丢失,而 AOF 由于文件较大则会影响Redis 的启动速度,为了能同时拥有 RDB 和 AOF 的优点,Redis 4.0 之后新增了混合持久化的方式,因此我们在必须要进行持久化操作时,应该选择混合持久化的方式。
混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,又能减低数据丢失的风险。
查询是否开启混合持久化可以使用 config get aof-use-rdb-preamble 命令,其中 yes 表示已经开启混合持久化,no 表示关闭。
需要注意的是,在非必须进行持久化的业务中,可以关闭持久化,这样可以有效的提升 Redis 的运行速度,不会出现间歇性卡顿的困扰。
八 Redis 主从复制
8.1 什么是主从复制
持久化保证了即使 Redis 服务重启也不会丢失数据,因为 Redis 服务重启后会将硬盘上持久化的数据恢复到内存中,但是当 Redis 服务器的硬盘损坏了可能会导致数据丢失,不过通过 Redis 的主从复制机制就可以避免这种单点故障,如下图:
- 主 Redis 中的数据有两个副本( replication )即从 redis1 和从 redis2 ,即使一台 Redis 服务
器宕机其它两台 Redis 服务也可以继续提供服务。 - 主 Redis 中的数据和从 Redis 上的数据保持实时同步,当主 Redis 写入数据时通过主从复制机制
会复制到两个从 Redis 服务上。 - 只有一个主 Redis ,可以有多个从 Redis 。
- 主从复制不会阻塞 master ,在同步数据时, master 可以继续处理 client 请求。
- 一个 Redis 可以即是主又是从服务器。
8.2 实现原理
1 数据同步
Redis在2.8及以上版本使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制
- 全量复制:一般用于初次复制场景,Redis早期支持的复制功能只有全量复制,它会把主节点全部
数据一次性发送给从节点,当数据量较大时,会对主从节点和网络造成很大的开销。 - 部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节
点后,如果条件允许,主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以
有效避免全量复制的过高开销。
部分复制是对老版复制的重大优化,有效避免了不必要的全量复制操作。因此当使用复制功能时,尽量采用2.8以上版本的Redis。
psync命令运行需要以下组件支持:
- 主Redis的复制偏移量(replication offset)和从Redis的复制偏移量。
- 主Redis的复制积压缓冲区(replication backlog)。
- Redis的运行ID(run ID)。
2 runid
Redis 服务器的随机标识符(用于 Sentinel 和集群),重启后就会改变;当复制时发现和之前的 run_id 不同时,将会对数据全量同步。
3 复制偏移量
通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。
-
参与复制的主从节点都会维护自身复制偏移量。主节点(master)在处理完写入命令后,会把命令
的字节长度做累加记录,统计信息在info relication中的master_repl_offset指标中: -
从节点(slave)每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移
量,统计指标如下: -
从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在info relication中 的slave_repl_offset指标中:
4 全量同步
Redis 的全量同步过程主要分三个阶段:
- 同步快照阶段: Master 创建并发送快照给 Slave , Slave 载入并解析快照。 Master 同时将此阶段所产生的新的写命令存储到缓冲区。
- 同步写缓冲阶段: Master 向 Slave 同步存储在缓冲区的写操作命令。
- 同步增量阶段: Master 向 Slave 同步写操作命令。
5 增量同步
- Redis 增量同步主要指 Slave 完成初始化后开始正常工作时, Master 发生的写操作同步到
Slave 的过程。 - 通常情况下, Master 每执行一个写命令就会向 Slave 发送相同的写命令,然后 Slave 接收并执
行。
8.3 主从配置
8.3.1 安装
# 第一步:安装 C 语言需要的 GCC 环境
yum install -y gcc-c++
yum install -y wget
# 第二步:下载并解压缩 Redis 源码压缩包
wget http://download.redis.io/releases/redis-5.0.4.tar.gz
tar -zxf redis-5.0.4.tar.gz
# 编译 Redis 源码,进入 redis-3.2.9 目录,执行编译命令
cd redis-5.0.4
make
# 安装 Redis ,需要通过 PREFIX 指定安装路径
make install PREFIX=/kkb/server/redis
# 将配置文件复制过来
cp /root/redis-5.0.4/redis.conf /kkb/server/redis/bin/
# 配置监听ip和端口 守护模式运行
bind
daemonize yes
# 先打开服务端
./redis-server ./redis.conf
# 在打开客户端
/kkb/server/redis/bin/redis-cli
# 设置客户端 访问 服务端的 ip 和端口号
redis-cli -h 192.168.56.102 -p 6379
8.3.2 配置说明
- 主Redis配置 无需配置
- 修改从服务器上的 redis.conf 文件:
# replicaof <masterip> <masterport>
# 表示当前【从服务器】对应的【主服务器】的IP是192.168.10.135,端口是6379。
# 4.0之前只能slaveof 4.0之后默认replicaof,slaveof都起作用
info 在客户端可以查看相关信息
slaveof 192.168.133.154 6379
replicaof 192.168.133.154 6379
九 Redis 哨兵机制
9.1 Redis Sentinel 工作原理分析
1 为什么要用到哨兵
哨兵(Sentinel)主要是为了解决在主从复制架构中出现宕机的情况,主要分为两种情况:
-
1).从Redis宕机
这个相对而言比较简单,在Redis中从库重新启动后会自动加入到主从架构中,自动完成同步数据。在Redis2.8版本后,主从断线后恢复的情况下实现增量复制。 -
2).主Redis宕机
这个相对而言就会复杂一些,需要以下2步才能完成
a. 在从数据库中执行SLAVEOF NO ONE命令,断开主从关系并且提升为主库继续服务
b. 第二步,将主库重新启动后,执行SLAVEOF命令,将其设置为其他库的从库,这时数据就能更新回来由于这个手动完成恢复的过程其实是比较麻烦的并且容易出错,所以Redis提供的哨兵(sentinel)的功能来解决。
2 哨兵机制(sentinel)的高可用
Sentinel(哨兵)是Redis 的高可用性解决方案:由一个或多个Sentinel 实例 组成的Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。 原有主节点恢复将降为从节点。
3 哨兵的定时监控
任务1:每个哨兵节点每10秒会向主节点和从节点发送info命令获取最新拓扑结构图,哨兵配置时只要配置对主节点的监控即可,通过向主节点发送info,获取从节点的信息,并当有新的从节点加入时可以马上感知到。
任务2:每个哨兵节点每隔2秒会向redis数据节点的指定频道上发送该哨兵节点对于主节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其它哨兵节点的信息及对主节点的判断,其实就是通过消息publish和subscribe来完成的。
任务3:每隔1秒每个哨兵会向主节点、从节点及其余哨兵节点发送一次ping命令做一次心跳检测,这个也是哨兵用来判断节点是否正常的重要依据。
防止哨兵节点挂机,选用3个哨兵节点。
主观下线:所谓主观下线,就是单个sentinel认为某个服务下线(有可能是接收不到订阅,之间的网络不通等等原因)。SDOWN
sentinel会以每秒一次的频率向所有与其建立了命令连接的实例(master,从服务,其他sentinel)发ping命令,通过判断ping回复是有效回复,还是无效回复来判断实例时候在线(对该sentinel来说是“主观在线”)。
sentinel配置文件中的down-after-milliseconds设置了判断主观下线的时间长度,如果实例在down-after-milliseconds毫秒内,返回的都是无效回复,那么sentinel回认为该实例已(主观)下线,修改其flags状态为SRI_S_DOWN。如果多个sentinel监视一个服务,有可能存在多个sentinel的down-after-milliseconds配置不同,这个在实际生产中要注意。
客观下线:当主观下线的节点是主节点时,此时该哨兵3节点会通过指令sentinel is-masterdown-by-addr寻求其它哨兵节点对主节点的判断,如果其他的哨兵也认为主节点主观线下了,则当认为主观下线的票数超过了quorum(选举)个数,此时哨兵节点则认为该主节点确实有问题,这样就客观下线了,大部分哨兵节点都同意下线操作,也就说是客观下线 ODOWN 。
4 哨兵lerder选举流程
如果主节点被判定为客观下线之后,就要选取一个哨兵节点来完成后面的故障转移工作,选举出一个
leader的流程如下:
- 每个在线的哨兵节点都可以成为领导者,当它确认(比如哨兵3)主节点下线时,会向其它哨兵发is-master-down-by-addr命令,征求判断并要求将自己设置为领导者,由领导者处理故障转移;
- 当其它哨兵收到此命令时,可以同意或者拒绝它成为领导者;
- 如果哨兵3发现自己在选举的票数num 大于等于 (sentinels)/2+1时,将成为领导者,如果没有超过,继续选举…………
如果Sentinel收到其他Sentinel的投票请求,在以下两种情况下会把自己的票投给请求的Sentinel实例:
- Sentinel的配置纪元小于或等于发送投票请求的Sentinel配置纪元(当前选举的轮数)。
- 在当前配置纪元中这是收到的第一个投票请求。
5 自动故障转移机制
在从节点中选择新的主节点
sentinel状态数据结构中保存了主服务的所有从服务信息,领头sentinel按照如下的规则从从服务列表中
挑选出新的主服务
- 过滤掉主观下线的节点
- 选择slave-priority/ replica-priority最高的节点,(replica-priority 0的不选择)如果由则返回没有
就继续选择 - 选择出复制偏移量最大的系节点,因为复制偏移量越大则数据复制的越完整,如果由就返回了,没
有就继续 - 选择run_id最小的节点
更新主从状态
- 通过slaveof no one命令,让选出来的从节点成为主节点;并通过slaveof命令让其他节点成为其从节点。
- 将已下线的主节点设置成新的主节点的从节点,当其回复正常时,复制新的主节点,变成新的主节点的从节点
同理,当已下线的服务重新上线时,sentinel会向其发送slaveof命令,让其成为新主的从
9.2 哨兵进程的作用
- 监控( Monitoring ): 哨兵( sentinel ) 会不断地检查你的 Master 和 Slave 是否运作正常。
- 提醒( Notification ): 当被监控的某个 Redis 节点出现问题时, 哨兵( sentinel ) 可以通过 API向管理员或者其他应用程序发送通知。
- 自动故障迁移( Automatic failover ):当一个 Master 不能正常工作时,哨兵( sentinel ) 会开始一次自动故障迁移操作
推荐的sentinel 集群是1主 2或多从,3哨兵;sentinel的启动 是redis-sentinel
9.3 哨兵配置
sentinel.conf
# 设置端口
port 26379
# 是否守护进程启动
daemonize no
# 守护进程运行的时候需要保留pidfile
pidfile /var/run/redis-sentinel.pid
# 日志文件
logfile "/root/log/sentinel.log"
## sentinel monitor master-group-name hostname port quorum
sentinel monitor mymaster 127.0.0.1 6379 3
# down-after-milliseconds,超过多少毫秒跟一个redis实例断了连接(ping不通),哨兵就可能认为这个redis实例挂了
sentinel down-after-milliseconds mymaster 30000
# parallel-syncs,新的master别切换之后,同时有多少个slave被切换到去连接新master,重新做同步,数字越低,花费的时间越多
# 比如:master宕机了,4个slave中有1个切换成了master,剩下3个slave就要挂到新的master上面去
# 这个时候,如果parallel-syncs是1,那么3个slave,一个一个地挂接到新的master上面去,1个挂接完,而且从新的master sync完数据之后,再挂接下一个。
# 如果parallel-syncs是3,那么一次性就会把所有slave挂接到新的master上去
sentinel parallel-syncs mymaster 1
#failover-timeout,执行故障转移的timeout超时时长,Default is 3 minutes.
sentinel failover-timeout mymaster 180000
quorum的解释如下:
-
(1)至少多少个哨兵要一致同意,master进程挂掉了,或者slave进程挂掉了,或者要启动一个故障转移操作,quorum是用来识别故障的,真正执行故障转移的时候,还是要在哨兵集群执行选举,选举一个哨兵进程出来执行故障转移操作 。
-
(2)假设有5个哨兵,quorum设置了2,那么如果5个哨兵中的2个都认为master挂掉了; 2个哨兵中的一个就会做一个选举,选举一个哨兵出来,执行故障转移; 如果5个哨兵中有3个哨兵都是运行的, 那么故障转移才会被允许执行。
原文是:Note that whatever is the ODOWN quorum, a Sentinel will require to be selected by the majority of the known Sentinels in order to start a failover, so no failover can be performed in minority.
如果有一个sentinel:
实际情况,其实单纯从代码的情况,其实1个Sentinel就能完成主观下线(sdown,Subjectively Down),客观下线(odown, Objectively Down) 的判断,自动发现 Sentinel 和从服务器,并且完成故障转移。
如果有2个sentinel
2 个的一半以上 2;
如果哨兵集群仅仅部署了个2个哨兵实例,quorum=1。s1和s2中只要有1个哨兵认为master宕机就可以还行切换,同时s1和s2中会选举出一个哨兵来执行故障转移。同时这个时候,需要majority,也就是大多数哨兵都是运行的,2个哨兵的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=3),如果一个节点挂了那么哨兵也就挂了,哨兵只剩下一个,那么就无法完成故障转移。
如果是经典的3节点哨兵集群
Configuration: quorum = 2。
majority=2(majority不能配置,由redis自行计算所得。超过一半的最小值)
如果M1所在机器宕机了,那么三个哨兵还剩下2个,S2和S3可以一致认为master宕机,然后选举
出一个来执行故障转移。同时3个哨兵的majority是2,所以还剩下的2个哨兵运行着,就可以允许
执行故障转移。
这就是经典的sentinel3个节点的集群。节省资源的同时又满足了高可用。