Redis
之集群高可用和安全控制
一、Redis
的主从复制
1.1. 为什么使用主从
单个Redis
如果因为某种原因宕机的话,可能会导致Redis
服务不可用,可以使用主从复制实现一主多从,主节点负责写的操作,从节点负责读的操作,主节点会定期将数据同步到从节点中,保证数据一致性的问题。
1.2. 主从的方式
最少需要3个节点。
第一种:
第二种:
优先选择第二种,第一种方式主节点向从节点同步数据压力大。
1.3. 主从复制配置:
将编译之后的Redis
中的bin
目录中全部内容
角色 | 端口 | 配置文件 |
---|---|---|
主节点 | 6379 | redis_6379.conf |
从节点 | 6380 | redis_6380.conf |
从节点 | 6381 | redis_6381.conf |
从节点 | 6382 | redis_6382.conf |
相关目录结构
内存有限,我们只模拟不同端口下的Redis
主从复制。我模拟了一主三从,采用树状结构:
核心配置:主节点
# ip监控
bind 0.0.0.0
protected-mode no
# requirepass 123456
# 端口
port 6379
# 后台运行
daemonize yes
# 工作目录
dir ./
# pid
pidfile "/var/run/redis_6379.pid"
# 日志名
logfile "redis_6379.log"
# RDB同步数据
dbfilename "dump_6379.rdb"
# AOF同步
appendonly yes
appendfilename "appendonly_6379.aof"
# AOF 策略
appendfsync everysec
# 失效消息通知配置
notify-keyspace-events "xE"
从节点:其他的从节点和这个一样
# ip监控
bind 0.0.0.0
protected-mode no
# requirepass 123456
# 端口
port 6379
# 后台运行
daemonize yes
# 工作目录
dir ./
# pid redis_6380 对应的端口号,自己替换
pidfile "/var/run/redis_6380.pid"
# 日志名
logfile "redis_6380.log"
# RDB同步数据
dbfilename "dump_6380.rdb"
# AOF同步
appendonly yes
appendfilename "appendonly_6380.aof"
# AOF 策略
appendfsync everysec
notify-keyspace-events "xE"
# slaveof和replicaof一样效果
replicaof 192.168.252.131 6379
Redis
命令添加从节点:重启之后失效
127.0.0.1:6380> slaveof 192.168.252.131 6379
ok
结构图:
查看进程:
[root@long-test redis-slave-3]# ps -ef | grep redis | grep -v grep
root 12636 1 0 09:02 ? 00:00:01 ./bin/redis-server 0.0.0.0:6379
root 12685 1 0 09:12 ? 00:00:00 ./bin/redis-server 0.0.0.0:6380
root 12700 1 0 09:15 ? 00:00:00 ./bin/redis-server 0.0.0.0:6381
root 12716 1 0 09:19 ? 00:00:00 ./bin/redis-server 0.0.0.0:6382
root 12751 12505 0 09:21 pts/1 00:00:00 redis-cli -p 6382
[root@long-test redis-slave-3]#
主从节点详细信息:
127.0.0.1:6379> info replication # 主节点 6379
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.252.131,port=6380,state=online,offset=1427,lag=0 # 从节点 6380
master_replid:62e34afa2f9e2b2fdf23614109aaf63b5d45c380
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1427
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1427
# ------------------------------------------------------------------------------
127.0.0.1:6380> info replication # 从节点6380, 有两个从节点通过6380连接进行同步
# Replication
role:slave
master_host:192.168.252.131 # 主节点 6379
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:1245
slave_priority:100
slave_read_only:1
connected_slaves:2
slave0:ip=192.168.252.131,port=6381,state=online,offset=1245,lag=0 # 6381节点
slave1:ip=192.168.252.131,port=6382,state=online,offset=1245,lag=0 # 6382节点
master_replid:62e34afa2f9e2b2fdf23614109aaf63b5d45c380
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1245
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1245
测试:
[root@long-test bin]# redis-cli # 主节点6379
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> set name tom # 可读可写
OK
127.0.0.1:6379> get name
"tom"
127.0.0.1:6379> exit
[root@long-test bin]# redis-cli -p 6380 # 从节点 6380
127.0.0.1:6380> auth 123456
OK
127.0.0.1:6380> get name # 获取到刚才的key
"tom"
127.0.0.1:6380> exit
[root@long-test bin]# redis-cli -p 6381 # 从节点 6381
127.0.0.1:6381> auth 123456
OK
127.0.0.1:6381> get name
"tom"
127.0.0.1:6381> exit
[root@long-test bin]# redis-cli -p 6382 # 从节点 6382
127.0.0.1:6382> auth 123456
OK
127.0.0.1:6382> get name
"tom"
127.0.0.1:6382> set age 123 # 不可写,只能读
(error) READONLY You can't write against a read only replica.
127.0.0.1:6382>
1.4. 主从复制数据同步过程
Redis
从节点向主节点建立socket
长连接,从服务器会发送一PING命令给主服务器;这时候PING命令可以检查socket的读写状态是否正常,还可以检查主服务器能否正常处理命令请求。从服务器会向主服务器发送PSYNC
命令,执行同步操作,并将自己的数据库同步至主服务器数据库当前的状态。Redis
通过心跳检测机制定时向主服务器发送消息。Redis
采用全量或者增量的形式将数据同步给从节点, 首次是全量,有数据更改是增量
***全量复制:***一般用于在初次的复制场景(从节点与主节点一次建立)
增量复制: 网络出现问题,从节点再次连接主节点时,主节点补发缺少的数据,每次数据增量同步
1.5. 主从复制存在的缺陷
如果主节点存在了问题,整个Redis
环境是不可以实现写的操作,需要人工更改配置变为主节点。
解决方案:使用哨兵机制可以帮助解决Redis
集群主从选举策略。
哨兵机制只解决主从选举策略,并不能解决同步的问题。
二、Redis
哨兵机制
2.1. 哨兵机制
Redis
的哨兵机制就是解决我们以上主从复制存在缺陷(选举问题),解决问题保证我们的Redis
高可用,实现自动化故障发现与故障转移。
2.2. 哨兵机制原理
- 哨兵机制每个10s时间只需要配置监听我们的主节点就可以获取当前整个
Redis
集群的环境列表,采用info 命令形式。 - 哨兵不建议是单机的,最好每个
Redis
节点都需要配置哨兵监听。 - 哨兵集群原理是如何:多个哨兵都执行同一个主的
master
节点,订阅到相同都通道,有新的哨兵加入都会向通道中发送自己服务的信息,该通道的订阅者可以发现新哨兵的加入,随后相互建立长连接。 Master
的故障发现 单个哨兵会向主的master节点发送ping的命令,如果master节点没有及时的响应,哨兵会认为该master节点为“主观不可用状态”会发送给其他都哨兵确认该Master节点是否不可用,当前确认的哨兵节点数>=quorum(可配置),会实现重新选举。
2.3. 原理图
2.4. 核心配置
我们接着上面的主从配置,接着测试哨兵集群的主节点宕机之后自动选举产生新的主节点。
节点 | 端口 | 配置文件 |
---|---|---|
主节点 | 26379 | sentinel_26379.conf |
从节点 | 26380 | sentinel_26380.conf |
从节点 | 26381 | sentinel_26381.conf |
从节点 | 26382 | sentinel_26382.conf |
哨兵节点配置:需要和现有的Redis
服务器数量相同。
bind 0.0.0.0
# 端口
port 26379
# 后台
daemonize yes
# 工作目录
dir ./
# 日志文件 sentinel_26379 对应不同的端口 ,自己配置
logfile "sentinel_26379.log"
# 监控的主节点
protected-mode no
# 监听主节点 ip和端口 3 是主节点挂掉之后需要几个节点确认之后就可以选举主节点
sentinel monitor mymaster 192.168.252.131 6380 3
启动哨兵:
# 其他的redis哨兵启动一样,对应不同的端口哨兵配置文件
[root@long-test redis-master]# ./bin/redis-sentinel sentinel_26379.conf
查看进程:
[root@long-test redis-master]# ps -ef | grep redis
root 15252 1 0 20:25 ? 00:00:06 ./bin/redis-sentinel 0.0.0.0:26379 [sentinel]
root 15257 1 0 20:26 ? 00:00:03 ./bin/redis-server 0.0.0.0:6380
root 15264 1 0 20:26 ? 00:00:05 ./bin/redis-sentinel 0.0.0.0:26380 [sentinel]
root 15269 1 0 20:26 ? 00:00:03 ./bin/redis-server 0.0.0.0:6381
root 15276 1 0 20:26 ? 00:00:05 ./bin/redis-sentinel 0.0.0.0:26381 [sentinel]
root 15281 1 0 20:26 ? 00:00:03 ./bin/redis-server 0.0.0.0:6382
root 15288 1 0 20:27 ? 00:00:05 ./bin/redis-sentinel 0.0.0.0:26382 [sentinel]
root 15387 1 0 20:56 ? 00:00:01 ./bin/redis-server 0.0.0.0:6379
root 15464 14823 0 21:20 pts/0 00:00:00 grep --color=auto redis
[root@long-test redis-master]#
这启动之后就会发现,哨兵文件会出现如下内容:
[root@long-test redis-master]# cat sentinel_26379.conf
bind 0.0.0.0
# 端口
port 26379
# 后台
daemonize yes
# 工作目录
dir "/opt/redis-cluster-service/redis-master"
# 日志文件
logfile "sentinel_26379.log"
# 监控的主节点
# Generated by CONFIG REWRITE 下面启动之后添加的
protected-mode no
sentinel myid 2308a50d7354fab01bb1710b658b6beb6fd9e029
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 192.168.252.131 6379 3
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
# 从节点
sentinel known-replica mymaster 192.168.252.131 6380
sentinel known-replica mymaster 192.168.252.131 6381
sentinel known-replica mymaster 192.168.252.131 6382
# 哨兵节点
sentinel known-sentinel mymaster 192.168.252.131 26381 23c43bfdc86a95f4a0c1504c229250bc821772f8
sentinel known-sentinel mymaster 192.168.252.131 26380 7b32e61d00c9d7a47adb92142615ea94b39fbc59
sentinel known-sentinel mymaster 192.168.252.131 26382 72848de80af7f13209d24d7e683a07f9f25816f5
sentinel current-epoch 1
关闭主节点:
[root@long-test redis-master]# redis-cli -p 6379
127.0.0.1:6379> shutdown # 关闭redis
查看主节点的配置文件:
[root@long-test redis-slave-1]# cat redis_6379.conf
# ip监控
bind 0.0.0.0
protected-mode no
# requirepass 123456
# 端口
port 6379
# 后台运行
daemonize yes
# 工作目录
dir "/opt/redis-cluster-service/redis-slave-1"
# pid
pidfile "/var/run/redis_6379.pid"
# 日志名
logfile "redis_6379.log"
# RDB同步数据
dbfilename "dump_6379.rdb"
# AOF同步
appendonly yes
appendfilename "appendonly_6379.aof"
# AOF 策略
appendfsync everysec
notify-keyspace-events "xE"
# 标注所属的主机
# 这个是哨兵帮我们删除的
# Generated by CONFIG REWRITE
哨兵切换主节点:
127.0.0.1:6379> shutdown # 关闭6379端口对应的redis服务器
not connected> exit
[root@long-test redis-slave-1]# redis-cli -p 26381 # 进入26381端口的哨兵
127.0.0.1:26381> info sentinel # 查看哨兵已经将主节点切换到了6381端口对应的节点了
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.252.131:6381,slaves=3,sentinels=4
127.0.0.1:26381>
开启刚才关闭的节点:查看redis的配置文件
[root@long-test redis-master]# cat redis_6379.conf
# ip监控
bind 0.0.0.0
protected-mode no
# requirepass 123456
# 端口
port 6379
# 后台运行
daemonize yes
# 工作目录
dir "/opt/redis-cluster-service/redis-slave-1"
# pid
pidfile "/var/run/redis_6379.pid"
# 日志名
logfile "redis_6379.log"
# RDB同步数据
dbfilename "dump_6379.rdb"
# AOF同步
appendonly yes
appendfilename "appendonly_6379.aof"
# AOF 策略
appendfsync everysec
notify-keyspace-events "xE"
# 标注所属的主机
# 这个是哨兵帮我们添加的新主节点
# Generated by CONFIG REWRITE
replicaof 192.168.252.131 6381
三、Redis
安全控制
3.1. 缓冲穿透:key不存在的情况
key
对应的数据在数据源并不存在,每次针对此key
的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
解决方案:
- 接口层实现
api
限流、用户授权、id检测 - 从缓冲和数据库都获取不到数据,一样可以将数据库空值放入缓冲中,设置30s有效期避免使用同一个id对数据库攻击压力大
3.2. 缓冲击穿:单个热点key
失效在并发查询的情况下
在高并发的情况下,当一个缓存key过期时,因为访问该key请求较大,多个请求同时发现缓存过期,因此对多个请求同时数据库查询、同时向Redis写入缓存数据,这样会导致数据库的压力非常大。
解决方案:
- 使用分布式锁:保证在分布式情况下,使用分布式锁保证对每一个
key
同时只允许一个线程查询到后端服务,其他没有获取到锁的权限,只需要等待即可,这种高并发压力直接转移到分布式锁上,对分布式锁压力非常大。 - 使用本地锁:使用本地锁与分布式机制一样,只不过分布式锁适应于集群;本地锁仅限于单个服务使用。
- 软过期:设置热点数据永不过期或者异步延长过期时间
- 布隆过滤器
3.3. 缓冲雪崩:多个key
失效的情况下
缓存服务器重启或者大量的缓存集中在某个时间段失效,突然给数据库产生了巨大的压力,甚至击垮数据库的情况。
解决方案:
- 对不用的数据使用不同的失效时间,加上随机数。
四、上面的哨兵集群已经上传到码云大家可以自行下载实验
https://gitee.com/molonglove/docker-es-service
致数据库的压力非常大。
解决方案:
- 使用分布式锁:保证在分布式情况下,使用分布式锁保证对每一个
key
同时只允许一个线程查询到后端服务,其他没有获取到锁的权限,只需要等待即可,这种高并发压力直接转移到分布式锁上,对分布式锁压力非常大。 - 使用本地锁:使用本地锁与分布式机制一样,只不过分布式锁适应于集群;本地锁仅限于单个服务使用。
- 软过期:设置热点数据永不过期或者异步延长过期时间
- 布隆过滤器
3.3. 缓冲雪崩:多个key
失效的情况下
缓存服务器重启或者大量的缓存集中在某个时间段失效,突然给数据库产生了巨大的压力,甚至击垮数据库的情况。
解决方案:
- 对不用的数据使用不同的失效时间,加上随机数。
四、上面的哨兵集群已经上传到码云大家可以自行下载实验
来源:CSDN
作者:易水墨龙吟
链接:https://blog.csdn.net/yhflyl/article/details/103491554