Redis之集群高可用与安全控制

别来无恙 提交于 2019-12-11 14:58:26

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. 主从复制数据同步过程
  1. Redis从节点向主节点建立socket长连接,从服务器会发送一PING命令给主服务器;这时候PING命令可以检查socket的读写状态是否正常,还可以检查主服务器能否正常处理命令请求。从服务器会向主服务器发送PSYNC命令,执行同步操作,并将自己的数据库同步至主服务器数据库当前的状态。
  2. Redis通过心跳检测机制定时向主服务器发送消息。
  3. Redis采用全量或者增量的形式将数据同步给从节点, 首次是全量,有数据更改是增量

***全量复制:***一般用于在初次的复制场景(从节点与主节点一次建立)

增量复制: 网络出现问题,从节点再次连接主节点时,主节点补发缺少的数据,每次数据增量同步

1.5. 主从复制存在的缺陷

如果主节点存在了问题,整个Redis环境是不可以实现写的操作,需要人工更改配置变为主节点。

解决方案:使用哨兵机制可以帮助解决Redis集群主从选举策略。

哨兵机制只解决主从选举策略,并不能解决同步的问题。

二、Redis哨兵机制

2.1. 哨兵机制

Redis的哨兵机制就是解决我们以上主从复制存在缺陷(选举问题),解决问题保证我们的Redis高可用,实现自动化故障发现与故障转移。

2.2. 哨兵机制原理
  1. 哨兵机制每个10s时间只需要配置监听我们的主节点就可以获取当前整个Redis集群的环境列表,采用info 命令形式。
  2. 哨兵不建议是单机的,最好每个Redis节点都需要配置哨兵监听。
  3. 哨兵集群原理是如何:多个哨兵都执行同一个主的master节点,订阅到相同都通道,有新的哨兵加入都会向通道中发送自己服务的信息,该通道的订阅者可以发现新哨兵的加入,随后相互建立长连接。
  4. 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失效的情况下

缓存服务器重启或者大量的缓存集中在某个时间段失效,突然给数据库产生了巨大的压力,甚至击垮数据库的情况。

解决方案:

  • 对不用的数据使用不同的失效时间,加上随机数。

四、上面的哨兵集群已经上传到码云大家可以自行下载实验

https://gitee.com/molonglove/docker-es-service

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!