关于java:深入学习Redis三

5次阅读

共计 15344 个字符,预计需要花费 39 分钟才能阅读完成。

Redis 长久化

什么是长久化

Redis 所有数据保留在内存中,对数据的更新将 异步 地保留到磁盘上。

长久化的形式

快照

  • MySQL Dump
  • Redis RDB

日志

  • MySQL binlog
  • Redis AOF

RDB

什么是 RDB

RDB 长久化是指在指定的工夫距离内将内存中的数据集快照写入磁盘。

也是 默认 的长久化形式,这种形式是就是将内存中数据以快照的形式写入到二进制文件中, 默认的文件名为 dump.rdb。

在 Redis 运行时,RDB 程序将以后内存中的数据库快照保留到磁盘文件中,在 Redis 重启动时,RDB 程序能够通过载入 RDB 文件来还原数据库的状态。

工作形式

当 Redis 须要保留 dump.rdb 文件时,服务器执行以下操作:

  1. Redis 调用 forks。同时领有父过程和子过程。
  2. 子过程将数据集写入到一个长期 RDB 文件中。
  3. 当子过程实现对新 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

savebgsave 比照

命令 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 十分重型的操作,当数据量较大时,会对主从节点和网络造成很大的开销。

  1. Redis 外部会收回一个同步命令,刚开始是 psync 命令,psync ? -1示意要求 master 主机同步数据。
  2. 主机会向从机发送 runid(redis-cli info server)和 offset,因为 slave 并没有对应的 offset,所以是全量复制。
  3. 从机会保留主机的根本信息save masterinfo
  4. 主节点收到全量复制的命令后,执行bgsave(异步执行),在后盾生成 RDB 文件(快照),并应用一个缓冲区(称为复制缓冲区)记录从当初开始执行的所有命令。
  5. 主机 send RDB 发送 RDB 文件给从机。
  6. 发送缓冲区数据。
  7. 刷新旧的数据,从节点在载入主节点的数据之前要先将老数据革除。
  8. 加载 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

局部复制

  1. 如果网络抖动(连贯断开 connection lost)
  2. 主机 master 还是会写 replbackbuffer(复制缓冲区)
  3. 从机 slave 会持续尝试连贯主机
  4. 从机 slave 会把本人以后 runid 和偏移量传输给主机 master,并且执行 pysnc 命令同步
  5. 如果 master 发现你的偏移量是在缓冲区的范畴内,就会返回 continue 命令
  6. 同步了 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 节点。

故障转移过程

  1. 多个 Sentinel 发现并确认 master 有问题。
  2. 选举出一个 Sentinel 作为领导。
  3. 选出一个 slave 作为 master。
  4. 告诉其余 slave 成为新的 master 的 salve。
  5. 告诉客户端主从变动。
  6. 期待老的 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

客户端连贯

既然曾经实现高可用为什么不间接直连?

高可用波及的是服务高可用、实现主动的故障转移;故障转移后客户端无奈感知将无奈保障失常的应用。

须要保障的是 服务高可用 客户端高可用

客户端实现基本原理

  1. 获取所有的 Sentinel 的节点和 MasterName,遍历 Sentinel 汇合失去一个可用的 Sentinel 节点。

  2. 向可用的 Sentinel 节点发送 sentinel 的 get-master-addr-by-name 的申请,参数 masterName,获取 master 节点信息。

  3. 客户端获取失去 master 节点后会执行一次 role 或者 role replication 来验证是否是 master 节点。

  4. 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 会主动实现这个,不须要手动实现。

具体步骤如下:

  1. 在从节点列表中选出一个节点作为新的主节点,抉择办法如下:

    • 过滤不衰弱(主观下线、断线)、5 秒内没有回复过 Sentinel 节点、与主节点失联超过 down-after-milliseconds 设置的。
    • 抉择 slave-priority(从节点优先级)最高的节点列表,如果存在则返回,不存在则持续。
    • 抉择复制偏移量最大的从节点(复制的最残缺),如果存在则返回,不存在则持续。
    • 抉择 runid 最小的从节点。
  2. sentinel 领导者节点会对第一步选出来的从节点执行 slaveof no one 命令让其成为主节点。
  3. sentinel 领导者节点会向残余的从节点发送命令,让它们成为新主节点的从节点,复制规定和 parallel-syncs 参数无关。
  4. sentinel 节点汇合会将原来的主节点更新为从节点,并放弃着对其关注,当其复原后命令它去复制新的主节点。

高可用读写拆散

咱们先理解一下目前从节点的作用:

  • 当主节点呈现故障时,作为主节点的后备“顶”上来实现故障转移,Redis Sentinel 曾经实现了该性能的自动化,实现了真正的高可用。
  • 扩大主节点的读能力,尤其是在读多写少的场景十分实用。

然而目前模型中,从节点并不是高可用的

  • 如果 slave- 1 节点呈现故障,首先客户端 client- 1 将与其失联,其次 sentinel 节点只会对该节点做主观下线,因为 Redis Sentinel 的故障转移是针对主节点的
  • 所以很多时候,Redis Sentinel 中的从节点仅仅是作为主节点一个热备,不让它参加客户端的读操作,就是为了保障整体高可用性,但实际上这种应用办法还是有一些节约,尤其是在有很多从节点或者的确须要读写拆散的场景,所以如何实现从节点的高可用是十分有必要的。

思路

Redis Sentinel 在 对各个节点的监控中,如果有对应事件的产生,都会收回相应的事件音讯

  • +switch-master:切换主节点(原来的从节点降职为主节点)。
  • +convert-to-slave:切换从节点(原来的主节点降级为从节点)。
  • +sdown:主观下线,阐明可能某个从节点可能不可用(因为对从节点不会做主观下线),所以在实现客户端时能够采纳本身策略来实现相似主观下线的性能。
  • +reboot:重新启动了某个节点,如果它的角色是 slave,那么阐明增加了某个从节点。

所以在设计 Redis Sentinel 的从节点高可用时,只有可能实时把握所有从节点的状态,把所有从节点看做一个资源池,无论是上线还是下线从节点,客户端都能及时感知到(将其从资源池中增加或者删除),这样从节点的高可用指标就达到了。

正文完
 0