Redis-05(主从复制,哨兵模式,缓存穿透和雪崩)
Redis主从复制
概念
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器.前者称为从节点(Slave/Follower),数据的复制是单向的,只能由从节点复制到主节点(主节点以写入为主,从节点只能读取)
作用
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余的方式
- 故障恢复:当前主节点故障的时候,从节点可以暂时提点主节点提供服务,是一种服务冗余的方式
- 负载均衡:在主从复制的基础上,配合读写分离,由主节点进行写的操作,从节点进行读的操作,分担服务器的负载,尤其是在多读少些的情况下,通过多个节点进行复杂,提高并发量
- 高可用基石:主从复制还是哨兵集群能够实施的基础
为什么使用集群
一般来说,要将Redis运用到工程中,只需要一个Redis是不可能的,原因如下
- 从结构上,单个容易发生故障,并且服务器需要处理所有的请求负载,压力较大
- 从容量上来说,单个Redis服务器内存有限,就算一台为256G也不能将所有的内存作用于Redis储存内存,一般来说,单台Redis最大内存不能超过20G
电商平台的商品大部分都是一次上传,多次浏览,也就是多读少些.
这种场景可以使用这种架构
主从复制,读写分里80%的情况下都是在进行读的操作,减缓服务器压力,架构中常常使用一主二从
在公司中,主从复制就是必须要使用的,因为在真实的项目中不可能单机使用Redis
总结
- 单台的服务器难以负载大量的请求
- 单台服务器故障率高,系统崩坏率高
- 单台服务器内存容量有限
环境配置
- 复制三个redis.conf分别为redis6379.conf redis6380.conf redis6381.conf.配置相对应的
- 端口
- pid名字
- log文件名字
- dump.rdb名字
- 启动单机多服集群
- 一主二从配置
默认情况下,每台Redis都是主节点,我们一般配置从机就好(80,81)
使用
slaveof 127.0.0.1 6379命令
使得80,81为从机
info replication 也能看到主机当前的信息
真实的情况下主从配置都应该在配置文件中写入,而不该在命令行中,命令行是暂时的,配置文件是永久的
使用规则
- 从机只能读,不能写,主机可读可写
127.0.0.1:6381> set name sakura # 从机6381写入失败
(error) READONLY You can't write against a read only replica.
127.0.0.1:6380> set name sakura # 从机6380写入失败
(error) READONLY You can't write against a read only replica.
127.0.0.1:6379> set name sakura
OK
127.0.0.1:6379> get name
"sakura"
- 当主机断电之后,默认情况下的从机角色不会转变,只会变成两个从机,集群失去了写入的操作,主机恢复的时候,又会连接上从机恢复原状
- 当从机断电宕机后,若不是使用配置文件配置的从机,再次启动后作为主机是无法获取之前主机的数据的,若此时重新配置称为从机,又可以获取到主机的所有数据。这里就要提到一个同步原理。
- 主机故障之后,不会出现新的主机,但是有两种情况可以产生新的主机
- 从机手动的执行命令slaveof on one,这样执行了之后就会变成主机
- 使用哨兵模式(自动选举)
复制原理
slave启动成功之后连接到master后会发送一个sync的同步命令
master接到命令之后,启动后台的存盘进程,同时收集所有接收到用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,完成一次增量复制
- 全量复制:slave服务在接收数据库文件后,将其存盘并加载到内存中
- 增量复制:master继续将新的所有收集到的修改命令依次传递给slave,完成同步
只要重连master,一次完全同步将会被自动执行,数据一定可以在从机中看到
哨兵模式
作用:当master宕机的时候从从机中选出新的master.从redis2.8开始正式提供sentinel(哨兵)
能够监控后台的主机是否故障,如果故障,可以根据投票选出从库升为主库
向每台发送信息确定是否存活,类似于springcloud的心跳检测
单机哨兵哨兵也可能会宕机,所以现在都使用多哨兵模式
当哨兵模式检测到master宕机,会自动将slave切换成master,通过发布订阅模式通知其他的从服务器,修改配置文件,让他们切换主机
多哨兵模式
假设master宕机,sentinel先检测到这个结果,系统并不会马上进行failover(故障切换、失效备援)这个现象称为主观下线,当后面的哨兵也检测到主服务器不可用,sentinel之间会发起一次投票,投票的结果由随机一个sentinel发起,进行failover操作,得到sentinel票数多的slave能成功切换为master,切换成功后,通过发布订阅模式,让各个哨兵把自己监控的服务器实现切换主机,这个过程称为客观下线
- 实现:
需要建立配置文件sentinel.conf
# sentinel monitor 被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1 6379 1
2.启动哨兵
redis-sentinel sliconf/sentinel.conf
此时哨兵正在监视6379,我们断开master
哨兵模式的优缺点
优点:
- 哨兵集群,基于主从复制模式,所有主从复制的优点都有
- 主从可以切换,故障转移,系统的可用性好
- 哨兵模式是由主从模式的升级,手动到自动,更加健壮
缺点: - Redis不好在线扩容,集群容易达到上限,在线扩容就很麻烦
- 实现哨兵模式的配置很麻烦,其中有很多配置项
哨兵模式的全部配置
# Example sentinel.conf
哨兵sentinel实例运行的端口 默认26379
port 26379
哨兵sentinel的工作目录
dir /tmp
哨兵sentinel监控的redis主节点的ip port
master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 1
当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码,设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
故障转移的超时时间 failover-timeout 可以用在以下这些方面:
1. 同一个sentinel对同一个master两次failover之间的间隔时间。
2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
3.当想要取消一个正在进行的failover所需要的时间。
4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
默认三分钟
sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
SCRIPTS EXECUTION
配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
对于脚本的运行结果有以下规则:
若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
一个是事件的类型,
一个是事件的描述。
如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
通知脚本
sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh
客户端重新配置主节点参数脚本
当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
以下参数将会在调用脚本时传给脚本:
<master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
目前<state>总是“failover”,
<role>是“leader”或者“observer”中的一个。
参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的.这个脚本应该是通用的,能被多次调用,不是针对性的。
sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
缓存的穿透与雪崩
缓存穿透(查询不到)
- 概念:
在默认情况下,用户请求数据时,会先在缓存(Redis)中查找,若没找到即缓存未命中,再在数据库中进行查找,数量少可能问题不大,可是一旦大量的请求数据(例如秒杀场景)缓存都没有命中的话,就会全部转移到数据库上,造成数据库极大的压力,就有可能导致数据库崩溃。网络安全中也有人恶意使用这种手段进行攻击被称为洪水攻击。 - 解决方案:
- 布隆过滤器
- 缓存空对象
这样做有一个缺陷:存储空对象也需要空间,大量的空对象会耗费一定的空间,存储效率并不高。解决这个缺陷的方式就是设置较短过期时间
即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
缓存击穿
-
概念
相较于缓存穿透,缓存击穿的目的性更强,一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。
eg:比如热搜排行上,一个热点新闻被同时大量访问就可能导致缓存击穿。 -
解决方案
-
设置热点数据永不过期
这样就不会出现热点数据过期的情况,但是当Redis内存空间满的时候也会清理部分数据,而且此种方案会占用空间,一旦热点数据多了起来,就会占用部分空间 -
加互斥锁(分布式锁)
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问。这样对锁的要求就十分高。
缓存雪崩
在某一个时间段,缓存集中过期失效,redis宕机
产生雪崩的原因之一,设置缓存的存活时间较短,大并发访问时刚好都过期,直接访问了数据库,对数据库而言,会产生周期性压力波峰,暴增时数据库可能会宕机
双十一时会停掉一些服务,保证主要的一些服务可用,springcloud中也有讲到过
- 解决方案:
--增加集群中服务器数量
----异地多活
--限流降级
----缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量,对某个key只允许一个线程查询数据和写缓存,其他线程等待
--数据预热
----正式部署之前,把可能的数据提前访问一遍,可能大量访问的数据就会加载到缓存中,加载不同的key,设置不同的过期时间,让缓存时间尽量均匀