Redis长久化
什么是长久化
Redis所有数据保留在内存中,对数据的更新将异步地保留到磁盘上。
长久化的形式
快照
- MySQL Dump
- Redis RDB
日志
- MySQL binlog
- Redis AOF
RDB
什么是RDB
RDB长久化是指在指定的工夫距离内将内存中的数据集快照写入磁盘。
也是默认
的长久化形式,这种形式是就是将内存中数据以快照的形式写入到二进制文件中,默认的文件名为dump.rdb。
在 Redis 运行时, RDB 程序将以后内存中的数据库快照保留到磁盘文件中, 在 Redis 重启动时, RDB 程序能够通过载入 RDB 文件来还原数据库的状态。
工作形式
当 Redis 须要保留 dump.rdb 文件时, 服务器执行以下操作:
- Redis 调用forks。同时领有父过程和子过程。
- 子过程将数据集写入到一个长期 RDB 文件中。
- 当子过程实现对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
这种工作形式使得 Redis 能够从写时复制(copy-on-write)机制中获益。
三种触发机制
save命令
save
命令执行一个同步操作,以RDB文件的形式保留所有数据的快照。
127.0.0.1:6379> save
OK
须要留神的是save
命令是同步命令。如果数据过多,会造成阻塞。
另外须要留神的是,执行save
命令会笼罩之前的RDB文件。
bgsave命令
bgsave
命令执行一个异步操作,以RDB文件的形式保留所有数据的快照。
127.0.0.1:6379> bgsave
Background saving started
Redis应用Linux零碎的fock()
生成一个子过程来将DB数据保留到磁盘,主过程持续提供服务以供客户端调用。
如果操作胜利,能够通过客户端命令LASTSAVE
来查看操作后果。
LASTSAVE
将返回最近一次 Redis 胜利将数据保留到磁盘上的工夫,以 UNIX 工夫戳格局示意。
127.0.0.1:6379> LASTSAVE
(integer) 1609294414
save
与bgsave
比照
命令 | save | bgsave |
---|---|---|
IO类型 | 同步 | 异步 |
阻塞 | 是 | 是(阻塞产生在fock(),通常十分快) |
复杂度 | O(n) | O(n) |
长处 | 不会耗费额定的内存 | 不阻塞客户端命令 |
毛病 | 阻塞客户端命令 | 须要fock子过程,耗费内存 |
主动保留
咱们能够通过配置文件对 Redis 进行设置, 让它在“ N 秒内数据集至多有 M 个改变”这一条件被满足时, 主动进行数据集保留操作。
相干配置
# RDB主动长久化规定
# 当 900 秒内有至多有 1 个键被改变时,主动进行数据集保留操作
save 900 1
# 当 300 秒内有至多有 10 个键被改变时,主动进行数据集保留操作
save 300 10
# 当 60 秒内有至多有 10000 个键被改变时,主动进行数据集保留操作
save 60 10000
# RDB长久化文件名
dbfilename dump-<port>.rdb
# 数据长久化文件存储目录
dir /var/lib/redis
# bgsave产生谬误时是否进行写入,默认为yes
stop-writes-on-bgsave-error yes
# rdb文件是否应用压缩格局
rdbcompression yes
# 是否对rdb文件进行校验和测验,默认为yes
rdbchecksum yes
长处
- 适宜大规模的数据恢复。
- 如果业务对数据完整性和一致性要求不高,RDB是很好的抉择。
毛病
- 数据的完整性和一致性不高,因为RDB可能在最初一次备份时宕机了。
- 备份时占用内存,因为Redis 在备份时会独立创立一个子过程,将数据写入到一个临时文件,最初再将临时文件替换之前的备份文件。所以要思考到大略两倍的数据膨胀性。
- 针对RDB不适宜实时长久化的问题,Redis提供了AOF长久化形式来解决。
AOF
AOF(append only file)长久化:以独立日志的形式记录每次写命令,重启时再从新执行AOF文件中命令达到复原数据的目标。AOF的次要作用是解决了数据长久化的实时性,目前曾经是Redis长久化的支流形式。
AOF创立原理
AOF复原原理
三种策略
always
每次有新命令追加到 AOF 文件时就执行一次 fsync
,十分慢,也十分平安。
everysec
每秒 fsync 一次:足够快,并且在故障时只会失落 1 秒钟的数据。
举荐(并且也是默认)为每秒 fsync 一次, 这种 fsync 策略能够兼顾速度和安全性。
no
将数据交给操作系统来解决,由操作系统来决定什么时候同步数据。
三种比照
命令 | always | everysec | no |
---|---|---|---|
长处 | 不失落数据 | 每秒一次fsync,可能会失落一秒数据 | 省心 |
毛病 | IO开销较大 | 可能会失落一秒数据 | 不可控 |
AOF重写
因为 AOF 的运作形式是一直地将命令追加到文件的开端, 所以随着写入命令的一直减少, AOF 文件的体积也会变得越来越大。
所以Redis会将曾经过期、反复的命令最终改写为失效的命令。
能够在不打断服务客户端的状况下, 对 AOF 文件进行重建(rebuild)。执行 bgrewriteaof 命令, Redis 将生成一个新的 AOF 文件, 这个文件蕴含重建以后数据集所需的起码命令。
AOF重写的作用
- 缩小磁盘占用量
- 减速数据恢复
AOF重写实现的两种形式
BGREWRITEAOF
执行一个AOF文件重写操作。重写会创立一个以后 AOF 文件的体积优化版本。
即便BGREWRITEAOF执行失败,也不会有任何数据失落,因为旧的 AOF 文件在BGREWRITEAOF胜利之前不会被批改。
重写操作只会在没有其余长久化工作在后盾执行时被触发,也就是说:
- 如果 Redis 的子过程正在执行快照的保留工作,那么 AOF 重写的操作会被预约(scheduled),等到保留工作实现之后再执行 AOF 重写。在这种状况下, BGREWRITEAOF 的返回值依然是
OK
。 - 如果曾经有别的 AOF 文件重写在执行,那么BGREWRITEAOF 返回一个谬误,并且这个新的 BGREWRITEAOF 申请也不会被预约到下次执行。
从 Redis 2.4 开始, AOF 重写由 Redis 自行触发,BGREWRITEAOF 仅仅用于手动触发重写操作。
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started
AOF重写配置
配置名称 | 含意 |
---|---|
auto-aof-rewrite-min-size | aof文件重写须要的大小 |
auto-aof-rewrite-percentage | aof文件增长率 |
统计名称 | 含意 |
---|---|
aof_current_size | AOF文件以后尺寸(字节) |
aof_base_size | AOF文件上次启动和重写时的尺寸(字节) |
主动触发机会,同时满足的状况下:
- aof_current_size > auto-aof-rewrite-min-size
- (aof_current_size – aof_base_size) * 100 / aof_base_size > auto-aof-rewrite-percentage
AOF重写流程
AOF相干配置
# 开启AOF长久化形式
appendonly yes
# AOF长久化文件名
appendfilename appendonly-<port>.aof
# 每秒把缓冲区的数据同步到磁盘,同步策略
appendfsync everysec
# 数据长久化文件存储目录
dir /var/lib/redis
# 是否在执行重写时不同步数据到AOF文件
no-appendfsync-on-rewrite yes
# 触发AOF文件执行重写的最小尺寸
auto-aof-rewrite-min-size 64mb
# 触发AOF文件执行重写的增长率
auto-aof-rewrite-percentage 100
AOF的长处
- AOF能够更好的爱护数据不失落,个别AOF会以每隔1秒,通过后盾的一个线程去执行一次
fsync
操作,如果redis过程挂掉,最多失落1秒的数据。 - AOF以
appen-only
的模式写入,所以没有任何磁盘寻址的开销,写入性能十分高。 - AOF日志文件的命令通过十分可读的形式进行记录,这个非常适合做灾难性的误删除紧急复原。
AOF的毛病
- 对于同一份文件AOF文件比RDB数据快照要大。
- AOF开启后反对写的QPS会比RDB反对的写的QPS低,因为AOF个别会配置成每秒
fsync
操作,每秒的fsync
操作还是很高的。 - 数据恢复比较慢,不适宜做冷备。
RDB和AOF
命令 | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
复原速度 | 快 | 慢 |
数据安全性 | 丢数据 | 依据策略决定 |
轻重 | 重 | 轻 |
如何抉择
- 不要仅仅应用RDB这样会失落很多数据。
- 也不要仅仅应用AOF,因为这会有两个问题,第一通过AOF做冷备没有RDB做冷备复原的速度快;第二RDB每次简略粗犷生成数据快照,更加强壮。
- 综合AOF和RDB两种长久化形式,用AOF来保证数据不失落,作为复原数据的第一抉择;用RDB来做不同水平的冷备,在AOF文件都失落或损坏不可用的时候,能够应用RDB进行疾速的数据恢复。
主从复制
理解主从复制之前,咱们先看看单机有什么问题?
- 单机故障,比方CPU坏了,内存坏了,宕机。
- 容量瓶颈。
- QPS瓶颈,尽管Redis官网说能够达到10w QPS,如果咱们想要达到 100w QPS,单机显然是无奈做到的。
什么是主从复制
主从复制,是用来建设一个和主数据库齐全一样的数据库环境,称为从数据库,主数据库个别是准实时的业务数据库。在最罕用的mysql数据库中,反对单项、异步赋值。在赋值过程中,一个服务器充当主服务器,而另外一台服务器充当从服务器;此时主服务器会将更新信息写入到一个特定的二进制文件中。
并会保护文件的一个索引用来跟踪日志循环。这个日志能够记录并发送到从服务器的更新中去。当一台从服务器连贯到主服务器时,从服务器会告诉主服务器从服务器的日志文件中读取最初一次胜利更新的地位。而后从服务器会接管从哪个时刻起产生的任何更新,而后锁住并等到主服务器告诉新的更新。
主从复制的作用
- 确保数据安全;做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库持续工作,防止数据的失落。
- 晋升I/O性能;随着日常生产中业务量越来越大,I/O拜访频率越来越高,单机无奈满足,此时做多库的存储,无效升高磁盘I/O拜访的频率,进步了单个设施的I/O性能。
- 读写拆散;使数据库能反对更大的并发。
同样也反对一主多从。
简略演示:
小总结
- 一个master能够有多个slave
- 一个slave只能有一个master
- 数据流向是单向的,master到slave
主从复制的两种实现形式
slaveof命令
127.0.0.1:6379>slaveof ip port
勾销复制
须要留神的是断开主从复制后,依然会保留master之前给它同步的数据。
127.0.0.1:6379>slaveof no one
配置文件
#配置主节点的ip和port
slaveof ip port
#从节点只读,防止主从数据不统一
slave-read-only yes
两种形式比照
形式 | 命令 | 配置 |
---|---|---|
长处 | 无需重启 | 对立配置 |
毛病 | 不便于管理 | 须要重启 |
实操
首先筹备两台centos,
我这里主节点是: 192.168.3.155
从节点:192.168.3.156
咱们应用配置文件的形式,在redis.conf
中配置 slaveof 192.168.3.155 6379
。
留神防火墙开启6379端口。
启动主和从节点后,咱们能够应用 info replication
查看。
192.168.3.155:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.3.156,port=6379,state=online,offset=15,lag=1
master_repl_offset:15
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:14
192.168.3.156:6379> info replication
# Replication
role:slave
master_host:192.168.3.155
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:15
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
咱们接着能够测试一下,在主节点退出测试数据,看看从节点是否能够获取。
192.168.3.155:6379> set jack hello
OK
192.168.3.156:6379> get jack
"hello"
全量复制
用于首次复制或其它无奈进行局部复制的状况,将主节点中的所有数据都发送给从节点,是一个IE十分重型的操作,当数据量较大时,会对主从节点和网络造成很大的开销。
- Redis 外部会收回一个同步命令,刚开始是
psync
命令,psync ? -1
示意要求 master 主机同步数据。 - 主机会向从机发送
runid
(redis-cli info server)和offset
,因为 slave 并没有对应的 offset,所以是全量复制。 - 从机会保留主机的根本信息
save masterinfo
。 - 主节点收到全量复制的命令后,执行
bgsave
(异步执行),在后盾生成RDB文件(快照),并应用一个缓冲区(称为复制缓冲区)记录从当初开始执行的所有命令。 - 主机send RDB发送RDB文件给从机。
- 发送缓冲区数据。
- 刷新旧的数据,从节点在载入主节点的数据之前要先将老数据革除。
- 加载RDB文件将数据库状态更新至主节点执行
bgsave
时的数据库状态和缓冲区数据加载。
复制偏移量
从节点(slave)每秒钟上报本身的复制偏移量给主节点,因为主节点也会保留从节点的复制偏移量,slave_repl_offset
指标。统计指标如下:
192.168.3.156:6379> info replication
# Replication
role:slave
master_host:192.168.3.155
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:15
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
参加复制的主从节点都会保护本身复制偏移量。主节点(master)在解决完写入命令后,会把命令的字节长度做累加记录,统计信息会在info replication
中的master_repl_offset
指标中。slave0
记录了从节点信息。
192.168.3.155:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.3.156,port=6379,state=online,offset=15,lag=1
master_repl_offset:15
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:14
从节点在接管到主节点发送的命令后,也会累加记录本身的偏移量。统计信息在info replication
中的slave_repl_offset
。
局部复制
- 如果网络抖动(连贯断开 connection lost)
- 主机master 还是会写 replbackbuffer(复制缓冲区)
- 从机slave 会持续尝试连贯主机
- 从机slave 会把本人以后 runid 和偏移量传输给主机 master,并且执行 pysnc 命令同步
- 如果 master 发现你的偏移量是在缓冲区的范畴内,就会返回 continue 命令
- 同步了 offset 的局部数据,所以局部复制的根底就是偏移量 offset。
如何抉择
从节点将offset发送给主节点后,主节点依据offset和缓冲区大小决定是否执行局部复制。
如果offset偏移量之后的数据,依然都在复制积压缓冲区里,则执行局部复制。
如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤压),则执行全量复制。
主从节点首次复制时,主节点将本人的runid发送给从节点,从节点将这个runid保存起来,当断线重连时,从节点会将这个runid发送给主节点,主节点依据runid判断是否进行局部复制。
如果从节点保留的runid与主节点当初的runid雷同,阐明主从节点之前同步过,主节点会持续尝试应用局部复制(到底能不能局部复制还要看offset和复制积压缓冲区的状况)。
如果从节点保留的runid与主节点当初的runid不同,阐明从节点在断线前同步的Redis节点并不是以后的主节点,只能进行全量复制。
runid能够通过 info server
命令来查看。
192.168.3.156:6379> info server
# Server
redis_version:3.0.7
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:3fdf3aafcf586962
redis_mode:standalone
os:Linux 3.10.0-1127.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:11306
run_id:116ef394d1999f8807f1d30d1bf0dc79aa8d865d
tcp_port:6379
uptime_in_seconds:3442
uptime_in_days:0
hz:10
lru_clock:14271160
config_file:/usr/local/redis-3.0.7/redis.conf
主从复制的问题
- 主从复制,主挂掉后须要手工来操作麻烦。
- 写能力和存储能力受限 (主从复制只是备份,单节点存储能力) 。
如果这个时候Master断掉了,那么主从复制也就断掉了。那么这个时候写就失败了。
在这个时候咱们只能抉择一个从节点执行slaveof no one
。而后让其它从节点抉择新的主节点。
Redis Sentinel 架构
在主从复制的根底上,减少了多个 Redis Sentinel 节点,这些Sentinel节点不存储数据。在Redis产生故障时候会主动进行故障转移解决,而后告诉客户端。
一套Redis Sentinel集群能够监控多套Redis主从,每一套Redis主从通过master-name作为标识。客户端不间接连贯Redis服务,而连贯Redis Sentinel。
在Redis Sentinel中分明哪个是master节点。
故障转移过程
- 多个Sentinel发现并确认master有问题。
- 选举出一个Sentinel作为领导。
- 选出一个slave作为master。
- 告诉其余slave成为新的master的salve。
- 告诉客户端主从变动。
- 期待老的master复活成为新的master的slave。
装置与配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
在master节点减少 daemonize yes
配置后。咱们进行启动
redis-sentinel sentinel.conf
咱们执行查看命令是否曾经启动。能够看到26379端口曾经启动。
[root@localhost redis]# ps -ef | grep redis-sentinel
root 11056 1 0 18:38 ? 00:00:00 redis-sentinel *:26379 [sentinel]
root 11064 9916 0 18:41 pts/0 00:00:00 grep --color=auto redis-sentinel
随后咱们进行连贯,能够应用info
命令查看信息
[root@localhost redis]# redis-cli -p 26379
127.0.0.1:26379> info
# Server
redis_version:3.0.7
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:311215fe18f833b6
redis_mode:sentinel
os:Linux 3.10.0-1127.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:11056
run_id:855df973568ff3604a9a373a799c24601b15822a
tcp_port:26379
uptime_in_seconds:260
uptime_in_days:0
hz:17
lru_clock:14279821
config_file:/usr/local/redis-3.0.7/sentinel.conf
# Sentinel
sentinel_masters:1 # 一个master
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=1 #两个从节点
咱们再去查看sentinel的配置文件,能够看到曾经发生变化,从节点曾经配置在外面。
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel known-slave mymaster 192.168.3.156 6379
sentinel known-slave mymaster 192.168.3.157 6379
而后咱们在其它的从节点上配置sentinel.conf
文件
减少上面代码:
daemonize yes
# 配置master信息
sentinel monitor mymaster 192.168.3.155 6379 2
而后启动。
再执行info
命令能够查看当初曾经有三个sentinels
节点。
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
客户端连贯
既然曾经实现高可用为什么不间接直连?
高可用波及的是服务高可用、实现主动的故障转移;故障转移后客户端无奈感知将无奈保障失常的应用。
须要保障的是服务高可用 和 客户端高可用。
客户端实现基本原理
- 获取所有的Sentinel的节点和MasterName,遍历Sentinel汇合失去一个可用的Sentinel节点。
- 向可用的Sentinel节点发送sentinel的
get-master-addr-by-name
的申请,参数masterName,获取master节点信息。 - 客户端获取失去master节点后会执行一次
role
或者role replication
来验证是否是master节点。 - master节点发生变化,sentinel是感知的(所有的故障发现、转移是由sentinel做的)。
sentinel怎么告诉client的呢?
外部是一个公布订阅的模式,client订阅sentinel的某一个频道,该频道里由谁是master的信息,如果由变动sentinel就在频道里publish一条音讯,client订阅就能够获取到信息,通过新的master信息进行连贯。
残缺流程图如下:
JedisSentinelPool sentinelPool = new JedisSentinelPool(masterName, sentinelSet, poolConfig, timeout);
Jedis jedis = null;
try {
jedis = redisSentinelPool.getResource();
} catch(Exception e) {
logger.error(e.getMessage(), e);
}finally {
if(jedis != null) {
jedis.close();
}
}
jedis
- JedisSentinelPool不是连贯Sentinel节点汇合的连接池。
- 实质上还是连贯master。
- 只是跟JedisPool进行辨别。
故障转移演练
目前还是ip 192.168.3.155
主节点。
156和157是从节点,启动了三个sentinel。
/**
* @author 又坏又迷人
* 公众号: Java菜鸟程序员
* @date 2020/12/31
* @Description:
*/
public class RedisSentinelTest {
private static Logger logger = LoggerFactory.getLogger(RedisSentinelTest.class);
public static void main(String[] args) {
String masterName = "mymaster";
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.3.155:26379");
sentinels.add("192.168.3.156:26379");
sentinels.add("192.168.3.157:26379");
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels);
int count = 0;
while (true) {
count++;
Jedis jedis = null;
try {
jedis = jedisSentinelPool.getResource();
int index = new Random().nextInt(10000);
String key = "k-" + index;
String value = "v-" + index;
jedis.set(key, value);
if (count % 100 == 0) {
logger.info("{} value is {}", key, jedis.get(key));
}
TimeUnit.MILLISECONDS.sleep(10);
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
}
咱们先失常的启动。
11:06:55.050 [main] INFO RedisSentinelTest - k-6041 value is v-6041
11:06:56.252 [main] INFO RedisSentinelTest - k-3086 value is v-3086
11:06:57.467 [main] INFO RedisSentinelTest - k-3355 value is v-3355
11:06:58.677 [main] INFO RedisSentinelTest - k-6767 value is v-6767
这个时候咱们间接强制进行master节点,也就是155节点。
192.168.3.155:6379> info server
# Server
redis_version:3.0.7
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:3fdf3aafcf586962
redis_mode:standalone
os:Linux 3.10.0-1127.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:4129
run_id:3212ce346ed95794f31dc30d87ed2a4020d3b252
tcp_port:6379
uptime_in_seconds:1478
uptime_in_days:0
hz:10
lru_clock:15496249
config_file:/usr/local/redis-3.0.7/redis.conf
取得process_id:4129
咱们间接kill -9 4129
,杀掉此过程
之后咱们查看是否还存在此过程。
ps -ef | grep redis-server | grep 6379
发现没有后,咱们查看Java控制台。在肯定工夫后,就实现故障转移。程序还是能够失常执行。
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused (Connection refused)
at redis.clients.jedis.Connection.connect(Connection.java:207)
at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:93)
at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1767)
at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:106)
at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:868)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:435)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
at redis.clients.util.Pool.getResource(Pool.java:49)
... 2 common frames omitted
Caused by: java.net.ConnectException: Connection refused (Connection refused)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at redis.clients.jedis.Connection.connect(Connection.java:184)
... 9 common frames omitted
十二月 31, 2020 11:14:13 上午 redis.clients.jedis.JedisSentinelPool initPool
信息: Created JedisPool to master at 192.168.3.156:6379
11:14:13.146 [main] INFO RedisSentinelTest - k-7996 value is v-7996
11:14:14.339 [main] INFO RedisSentinelTest - k-9125 value is v-9125
11:14:15.597 [main] INFO RedisSentinelTest - k-2589 value is v-2589
这样就主动实现故障转移了。
主观下线和主观下线
-
主观下线:每个sentinel节点对redis节点失败的认识。
sentinel down-after-millseconds masterName timeout
- 每个sentinel节点每秒会对redis节点进行ping,当超过timeout毫秒之后还没失去pong,则认为redis节点下线。
-
主观下线:所有sentinel节点对redis节点失败达成共识。
sentinel monitor masterName ip port quorum
- 大于等于quorum个sentinel主观认为redis节点失败下线。
- 通过
sentinel is-master-down-by-addr
提出本人认为redis master下线。
领导者选举
- 起因:只有sentinel节点实现故障转移
-
选举:通过
sentinel is-master-down-by-addr
命令心愿胜利领导者。- 每个主观下线的节点向其它的sentinel节点发送命令,要求将它设置为领导者。
- 收到命令的sentinel节点如果没有批准过其它节点发送的命令,那么将批准该节点,否则回绝。
- 如果该sentinel节点发现自己的票数曾经超过sentinel汇合半数且超过
quorum
,那么将成为领导者。 - 如果此过程多个sentinel成为领导者,那么将期待一段时间后从新进行选举。
故障转移
故障转移就是当master宕机,抉择一个适合的slave节点来降级为master节点的操作,sentinel会主动实现这个,不须要手动实现。
具体步骤如下:
-
在从节点列表中选出一个节点作为新的主节点,抉择办法如下:
- 过滤不衰弱(主观下线、断线)、5秒内没有回复过Sentinel节点、与主节点失联超过
down-after-milliseconds
设置的。 - 抉择slave-priority(从节点优先级)最高的节点列表,如果存在则返回,不存在则持续。
- 抉择复制偏移量最大的从节点(复制的最残缺),如果存在则返回,不存在则持续。
- 抉择runid最小的从节点。
- 过滤不衰弱(主观下线、断线)、5秒内没有回复过Sentinel节点、与主节点失联超过
- sentinel领导者节点会对第一步选出来的从节点执行
slaveof no one
命令让其成为主节点。 - sentinel领导者节点会向残余的从节点发送命令,让它们成为新主节点的从节点,复制规定和
parallel-syncs
参数无关。 - sentinel节点汇合会将原来的主节点更新为从节点,并放弃着对其关注,当其复原后命令它去复制新的主节点。
高可用读写拆散
咱们先理解一下目前从节点的作用:
- 当主节点呈现故障时,作为主节点的后备“顶”上来实现故障转移,Redis Sentinel曾经实现了该性能的自动化,实现了真正的高可用。
- 扩大主节点的读能力,尤其是在读多写少的场景十分实用。
然而目前模型中,从节点并不是高可用的。
- 如果slave-1节点呈现故障,首先客户端client-1将与其失联,其次sentinel节点只会对该节点做主观下线,因为Redis Sentinel的故障转移是针对主节点的。
- 所以很多时候,Redis Sentinel中的从节点仅仅是作为主节点一个热备,不让它参加客户端的读操作,就是为了保障整体高可用性,但实际上这种应用办法还是有一些节约,尤其是在有很多从节点或者的确须要读写拆散的场景,所以如何实现从节点的高可用是十分有必要的。
思路
Redis Sentinel在对各个节点的监控中,如果有对应事件的产生,都会收回相应的事件音讯。
- +switch-master:切换主节点(原来的从节点降职为主节点)。
- +convert-to-slave:切换从节点(原来的主节点降级为从节点)。
- +sdown:主观下线,阐明可能某个从节点可能不可用(因为对从节点不会做主观下线),所以在实现客户端时能够采纳本身策略来实现相似主观下线的性能。
- +reboot:重新启动了某个节点,如果它的角色是slave,那么阐明增加了某个从节点。
所以在设计Redis Sentinel的从节点高可用时,只有可能实时把握所有从节点的状态,把所有从节点看做一个资源池,无论是上线还是下线从节点,客户端都能及时感知到(将其从资源池中增加或者删除),这样从节点的高可用指标就达到了。
发表回复