Linux——redis主从复制、集群模式、哨兵模式
-
主从复制
- 部署至少两个redis的实例 // 提供数据冗余和备份
- 两个独立服务器
- 两个虚拟机
- 两个容器
- 一个redis的master 节点可以有多个redis的replica 从节点, 而从节点也可以成为其他从节点的主节点 // 方便对于主复制架构进行扩展
- 提供数据灾备,当redis 的主节点宕机的情况下,尽快恢复数据 // 可以进行故障转移
- 不支持自动的故障转移 // 缺陷
- 部署至少两个redis的实例 // 提供数据冗余和备份
-
集群模式
- 一种特殊的redis实例运行方式,在这种方式下,处于同一个redis集群中的实例,将通过集群端口互相交换状态,以维持集群的高可用和数据完整
- hash slot将数据分散到集群中的每一个节点
- 集群模式支持为数据存储的节点设置数据副本,这样在主节点出现故障的情况下,从节点将自动切换为主节点;
- 集群模式下,架构的维护也很方便,加入新的主节点时,需要从原有的主节点中划分一些hash slot给新的节点,保证新的主节点能够在集群中完成数据保存和协议;如果是一个从节点,那么就只需要指定从节点的主节点即可
- 集群模式提供了相对完整高可用以及自动故障转移的机制
-
哨兵模式
- 一般搭配主从复制机制一起部署
- 功能:
- 监控:监控redis 主从架构下,所有的redis实例是否正常
- 发送提示信息:在发现异常情况后,向系统管理员或者API 发送提示信息
- 自动故障转移:在发现master 异常后,哨兵将从所有的从节点中选择一个成为新的主节点,然后通过所有的从节点新的主节点信息,而无需手动干预,在主节点排除异常之后,哨兵进程将原来的主节点作为从节点计入所监控的主从架构
- 配置更新:哨兵还可以更新所监控redis实例的配置
哨兵模式的工作机制:
比如说目前使用一个哨兵集群监控一个由三个redis实例构成的主从复制架构
哨兵集群:哨兵1 哨兵2 哨兵3
主从架构: master1 replica1 replica2
判断主节点异常时,分为主观宕机和客观宕机:
主观宕机: 哨兵发送的状态监测没有收到主节点的响应,此时哨兵认为主节点异常,此时的异常,并不会触发自动故障转移机制,会有哨兵集群进行仲裁来判断主节点是否真的不可用。
客观宕机:如果某一个哨兵进程判断主节点主观宕机,那么在哨兵集群内发起仲裁,必须是超过指定数量的哨兵同意主节点宕机,才会开始故障转移的过程,此时认为主节点客观宕机。
哨兵1 发现 master1 宕机,则通知剩下哨兵实例,判断master1 是不是真的宕机,假设仲裁数为2,那么只要哨兵2 或者 哨兵3 任意一个同意 master1 宕机,那么就认为master 客观宕机,开始故障转移。
故障转移: 在判定主节点客观宕机之后,哨兵进行故障转移,但是为了避免哨兵集群中各个实例在新的主节点选择上可能出现的不同选择,所以先在哨兵集群内选择(Raft算法)一个节点作为故障转移的仲裁者和执行者。选举出来的哨兵在剩余的从节点中选择一个从节点,成为新的主节点,并且将主节点的信息同步给剩下的从节点。
哨兵集群内开始投票,哪一个实例应该成为下一步的执行实例,所有的哨兵给其他实例,发送投票信息,一个哨兵实例只能投一票,且这一票只能投给它收到的第一个投票信息的发送方。
哨兵最小的部署数量为3个实例或者3个不同的节点;哨兵和redis 数据实例分别部署到不同的节点;哨兵模式也不能完全保证数据没有任何的丢失,完全保证高可用。
sentinel monitor mymaster 127.0.0.1 6379 2
//monitor配置主节点监控sentinel monitor 自定义的master 名称 主节点ip 主节点端口 仲裁数 (关于客观宕机的最低数量)
sentinel down-after-milliseconds mymaster 60000
// 主节点宕机或者异常的超时时间
sentinel failover-timeout mymaster 180000
// 故障转移的超时时间
sentinel parallel-syncs mymaster 1
//是否会进行并发同步 后面设置的是,可以进行并发同步的从节点的数量,
实验环境下部署一主二从+3哨兵
5000 - 5002 端口对应 哨兵
6379 端口对应redis 主节点
6380 6381 端口对应redis 从节点
为了加快实验进度,只监听本地环回
实验过程
先设置主从复制:
[root@redis-servers ~]# ls -d /redis-cluster/
/redis-cluster/
[root@redis-servers ~]# cd /redis-cluster/
[root@redis-servers redis-cluster]# mkdir 5000 5001 5002 6379 6380 6381
[root@redis-servers redis-cluster]# mv 5000 5000-s
[root@redis-servers redis-cluster]# mv 5001 5001-s
[root@redis-servers redis-cluster]# mv 5002 5002-s
[root@redis-servers redis-cluster]# vim 6379/redis.conf
[root@redis-servers 6379]# cat redis.conf
port 6379
bind 127.0.0.1
dbfilename "6379.rdb"
dir "/redis-cluster/6379"
save 3600 1
appendonly yes
appendfilename "6379.aof"
logfile "/redis-cluster/6379/6379.log"
daemonize yes
pidfile "/redis-cluster/6379/6379.pid"
[root@redis-servers redis-cluster]# cp 6379/redis.conf 6380/redis.conf
[root@redis-servers redis-cluster]# cp 6379/redis.conf 6381/redis.conf
[root@redis-servers redis-cluster]# sed -i 's/6379/6380/g' /redis-cluster/6380/redis.conf
[root@redis-servers redis-cluster]# sed -i 's/6379/6381/g' /redis-cluster/6381/redis.conf
[root@redis-servers redis-cluster]# systemctl stop redis.service
# 启动6379
[root@redis-servers 6379]# redis-server redis.conf
[root@redis-servers 6379]# ss -anput | grep redis
tcp LISTEN 0 511 127.0.0.1:6379 0.0.0.0:* users:(("redis-server",pid=3614,fd=6))
[root@redis-servers 6379]# cd ../6380
# 从节点修改配置文件
[root@redis-servers 6380]# vim redis.conf
port 6380
bind 127.0.0.1
dir "/redis-cluster/6380"
dbfilename "6380.rdb"
save 3600 1
appendonly yes
appendfilename "6380.aof"
logfile "/redis-cluster/6380/6380.log"
daemonize yes
pidfile "/redis-cluster/6380/6380.pid"
replicaof 127.0.0.1 6379
[root@redis-servers 6380]# redis-server redis.conf
[root@redis-servers 6380]# ss -anput | grep redis
tcp LISTEN 0 511 127.0.0.1:6379 0.0.0.0:* users:(("redis-server",pid=3614,fd=6))
tcp LISTEN 0 511 127.0.0.1:6380 0.0.0.0:* users:(("redis-server",pid=3635,fd=6))
tcp ESTAB 0 0 127.0.0.1:6379 127.0.0.1:40937 users:(("redis-server",pid=3614,fd=8))
tcp ESTAB 0 0 127.0.0.1:40937 127.0.0.1:6379 users:(("redis-server",pid=3635,fd=8))
[root@redis-servers 6380]# sed -i 's/6380/6381/g' /redis-cluster/6381/redis.conf
[root@redis-servers 6380]# cd ../6381
[root@redis-servers 6381]# redis-server redis.conf
port 6381
bind 127.0.0.1
dir "/redis-cluster/6381"
dbfilename "6381.rdb"
save 3600 1
appendonly yes
appendfilename "6381.aof"
logfile "/redis-cluster/6381/6381.log"
daemonize yes
pidfile "/redis-cluster/6381/6381.pid"
replicaof 127.0.0.1 6379
[root@redis-servers 6381]# ss -anput | grep redis
tcp LISTEN 0 511 127.0.0.1:6379 0.0.0.0:* users:(("redis-server",pid=3614,fd=6))
tcp LISTEN 0 511 127.0.0.1:6381 0.0.0.0:* users:(("redis-server",pid=3668,fd=6))
tcp LISTEN 0 511 127.0.0.1:6380 0.0.0.0:* users:(("redis-server",pid=3635,fd=6))
tcp ESTAB 0 0 127.0.0.1:6379 127.0.0.1:40937 users:(("redis-server",pid=3614,fd=8))
tcp ESTAB 0 0 127.0.0.1:6379 127.0.0.1:44615 users:(("redis-server",pid=3614,fd=9))
tcp ESTAB 0 0 127.0.0.1:40937 127.0.0.1:6379 users:(("redis-server",pid=3635,fd=8))
tcp ESTAB 0 0 127.0.0.1:44615 127.0.0.1:6379 users:(("redis-server",pid=3668,fd=8))
[root@redis-servers 6381]# redis-cli -p 6379
127.0.0.1:6379> info
。。。。。
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=210,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=210,lag=1
master_failover_state:no-failover
master_replid:e094b62e7f1f29b7d0449755c8627eb6e17f438d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:210
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:210
……
127.0.0.1:6379> set testa test
OK
127.0.0.1:6379> get testa
"test"
127.0.0.1:6379> exit
[root@redis-servers 6381]# redis-cli -p 6380
127.0.0.1:6380> get testa
"test"
127.0.0.1:6380> info
……
# Replication
role:slave ///
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_read_repl_offset:435
slave_repl_offset:435
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:e094b62e7f1f29b7d0449755c8627eb6e17f438d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:435
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:435
……
127.0.0.1:6380> exit
[root@redis-servers 6381]# redis-cli -p 6381
127.0.0.1:6381> info
……
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_read_repl_offset:477
slave_repl_offset:477
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:e094b62e7f1f29b7d0449755c8627eb6e17f438d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:477
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:113
repl_backlog_histlen:365
……
127.0.0.1:6381> exit
//主从架构完毕,启动哨兵集群
[root@redis-servers 6381]# cd ../5000-s/
[root@redis-servers 5000-s]# vim sentinel.conf
port 5000
sentinel monitor mymaster 127.0.0.1 6380 2
sentinel down-after-milliseconds mymaster 5001
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 3
daemonize yes
pidfile "/redis-cluster/5000-s/5000.pid"
logfile "/redis-cluster/5000-s/5000.log"
[root@redis-servers 5000-s]# cp sentinel.conf ../5001-s/sentinel.conf
[root@redis-servers 5000-s]# cp sentinel.conf ../5002-s/sentinel.conf
[root@redis-servers 5000-s]# sed -i 's/5000/5001/g' /redis-cluster/5001-s/sentinel.conf
[root@redis-servers 5000-s]# sed -i 's/5000/5002/g' /redis-cluster/5002-s/sentinel.conf
[root@redis-servers 5000-s]# redis-sentinel sentinel.conf
[root@redis-servers 5000-s]# ss -anput | grep 5000
tcp LISTEN 0 511 0.0.0.0:5000 0.0.0.0:* users:(("redis-sentinel",pid=3795,fd=6))
tcp LISTEN 0 511 [::]:5000 [::]:* users:(("redis-sentinel",pid=3795,fd=7))
[root@redis-servers 5000-s]# cd ../5001-s/
[root@redis-servers 5001-s]# redis-sentinel sentinel.conf
[root@redis-servers 5001-s]# cd ../5002-s/
[root@redis-servers 5002-s]# redis-sentinel sentinel.conf
[root@redis-servers 5002-s]# ss -anput | grep 5000
tcp LISTEN 0 511 0.0.0.0:5000 0.0.0.0:* users:(("redis-sentinel",pid=3795,fd=6))
tcp ESTAB 0 0 127.0.0.1:5000 127.0.0.1:42422 users:(("redis-sentinel",pid=3795,fd=16))
tcp ESTAB 0 0 127.0.0.1:39696 127.0.0.1:5000 users:(("redis-sentinel",pid=3806,fd=14))
tcp ESTAB 0 0 127.0.0.1:42422 127.0.0.1:5000 users:(("redis-sentinel",pid=3814,fd=14))
tcp ESTAB 0 0 127.0.0.1:5000 127.0.0.1:39696 users:(("redis-sentinel",pid=3795,fd=14))
tcp LISTEN 0 511 [::]:5000 [::]:* users:(("redis-sentinel",pid=3795,fd=7))
[root@redis-servers 5002-s]# ss -anput | grep 5001
tcp LISTEN 0 511 0.0.0.0:5001 0.0.0.0:* users:(("redis-sentinel",pid=3806,fd=6))
tcp ESTAB 0 0 127.0.0.1:58430 127.0.0.1:5001 users:(("redis-sentinel",pid=3795,fd=15))
tcp ESTAB 0 0 127.0.0.1:47264 127.0.0.1:5001 users:(("redis-sentinel",pid=3814,fd=15))
tcp ESTAB 0 0 127.0.0.1:5001 127.0.0.1:58430 users:(("redis-sentinel",pid=3806,fd=15))
tcp ESTAB 0 0 127.0.0.1:5001 127.0.0.1:47264 users:(("redis-sentinel",pid=3806,fd=16))
tcp LISTEN 0 511 [::]:5001 [::]:* users:(("redis-sentinel",pid=3806,fd=7))
[root@redis-servers 5002-s]# ss -anput | grep 5002
tcp LISTEN 0 511 0.0.0.0:5002 0.0.0.0:* users:(("redis-sentinel",pid=3814,fd=6))
tcp ESTAB 0 0 127.0.0.1:5002 127.0.0.1:60252 users:(("redis-sentinel",pid=3814,fd=16))
tcp ESTAB 0 0 127.0.0.1:60252 127.0.0.1:5002 users:(("redis-sentinel",pid=3806,fd=17))
tcp ESTAB 0 0 127.0.0.1:60254 127.0.0.1:5002 users:(("redis-sentinel",pid=3795,fd=17))
tcp ESTAB 0 0 127.0.0.1:5002 127.0.0.1:60254 users:(("redis-sentinel",pid=3814,fd=17))
tcp LISTEN 0 511 [::]:5002 [::]:* users:(("redis-sentinel",pid=3814,fd=7))
[root@redis-servers 5002-s]# cat sentinel.conf
port 5002
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5001
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 3
daemonize yes
pidfile "/redis-cluster/5002-s/5002.pid"
logfile "/redis-cluster/5002-s/5002.log"
# Generated by CONFIG REWRITE
protected-mode no
user default on nopass ~* &* +@all
dir "/redis-cluster/5002-s"
sentinel myid b90766a4d52b2d5b7a61b0813778fc69ca4b2624
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel current-epoch 0
sentinel known-replica mymaster 127.0.0.1 6381
sentinel known-replica mymaster 127.0.0.1 6380
sentinel known-sentinel mymaster 127.0.0.1 5001 8af5123cd008d359e03094f7c64ef841e4e698be
sentinel known-sentinel mymaster 127.0.0.1 5000 3b84754839534a86f071df092bc630ba51ba01c5
[root@redis-servers ~]# redis-cli -p 6379 debug sleep 60 // 打开新终端,暂停6379 主节点
OK
// 所有的哨兵日志中将出现主从架构变更过程,同时变更完成的架构将同步给每一个哨兵和redis数据实例
随便查看任何配置文件
redis数据实例配置文件变化
6379:
6380:
6381:
redis 缓存可能面临问题:
- 缓存穿透 在redis缓存没有找到对应的数据,去数据库查询数据,如果查询的是本来数据库就没有的数据,那么就会频发触发数据库的全表扫描,如果同一时间出现大量缓存穿透,那么就会增加数据库的压力。
解决方案:
- 设置空键,在数据库中查询无数据后,在redis设立空键,下次查询的时候直接返回控制,不会触发数据库查询。如果有大量的缓存穿透发生,那么空键数量增加,浪费redis存储空间,一般会设置空键的过期时间
- 利用一些编程手段规避
- 缓存击穿 有大量的空查询发生,比如说热点数据没有提前缓存,那么同时会有大量用户访问热点数据,导致数据库压力增加。
解决方案:
- 预热数据(提前缓存数据)
- 通过互斥锁,只有第一次查询连接数据库,其他查询进入队列,后续查询则会从redis
3、缓存雪崩 会造成比较严重的生产事故,大量热点数据同时过期,同时用户访问量较高,一般频发在秒杀活动等场景。
解决方案:
- 使用随机数设置键值过期时间
- 结合缓存击穿的策略进一步优化
补充阅读:
https://github.com/CoderLeixiaoshuai/java-eight-part/blob/master/docs/redis/