ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

Redis 手把手教程(3/3) - Redis集群及常见企业级解决方案

2021-11-26 16:59:21  阅读:113  来源: 互联网

标签:缓存 slave 手把手 redis Redis 企业级 master 服务器 数据


1. Redis集群

1.1 主从复制

1.1.1 简介

主从复制即将master中的数据即时,有效的复制到slave中。一个master可以拥有多个slave,一个slave只对应一个master。

职责

  • master:
    写数据
    执行写操作时,将出现变化的数据自动同步到slave
    读数据(可忽略)
  • slave:
    读数据
    写数据(禁止)

单机redis的风险

  • 服务器宕机,可能会造成数据丢失,对业务造成灾难性打击。
  • 容量瓶颈,单台服务器的容量有限,不易扩容。

解决方案
为了避免单点redis服务器故障,准备多台服务器,互相连通。将数据复制多个副本保存在不同的服务器上,链接在一起,并保证数据是同步的。即使有其中一台服务器宕机,其他服务器依然可以继续提供服务,实现Redis的高可用,同时实现数据冗余备份。

1.1.2 多台服务器链接方案

在这里插入图片描述

1.1.3 主从复制的作用

  • 读写分离:master写,slave读,提高服务器的读写负载能力
  • 负载均衡:基于主从结构,配合读写分离,由slave分担master负载,并根据需求的变化,改变slave的数量,通过多个从节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量
  • 故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复
  • 数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式
  • 高可用基石:基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案

1.1.4 主从复制的工作流程

总述
主从复制过程大体可以分为3个阶段

  • 建立连接阶段(即准备阶段)
  • 数据同步阶段
  • 命令传播阶段
    在这里插入图片描述

1.1.4.1 建立连接阶段

建立slave到master的链接,使master能够识别slave,并保存slave端口号。

  • 设置master的地址和端口,保存master信息
  • 建立socket链接
  • 发送ping命令(定时器任务)
  • 身份验证
  • slave发送端口信息,master进行保存
    至此,主从链接成功!

状态:

slave:
保存master的地址和端口
master:
保存slave的端口

流程如下图:
在这里插入图片描述
具体实现步骤(slave链接master):

建立连接的方式有多种,比如slave客户端发送连接命令,或者启动服务的时候指定连接参数,但是最常用的还是在配置文件中进行配置,这里也只描述配置文件的方式。

  • 服务器配置,配置文件加入以下命令就行

slaveof <masterip> <masterport>

授权访问

  • master配置文件设置密码

requirepass <password>

  • slave配置文件设置密码访问master

masterauth <password>

1.1.4.2 数据同步阶段

在slave初次链接master后,复制master中的所有数据到slave,将slave的数据库状态更新成master当前数据库状态。

  • slave请求同步数据
  • master创建RDB同步数据
  • slave接收并恢复RDB同步数据
  • slave请求部分同步数据(AOF)
    • slave在接收RDB数据的时候,可能又会有新的数据进入master,这部分新的数据的指令会被放在master的复制缓冲区,恢复完RDB数据后就会请求这部分数据的指令。
  • slave接收并恢复部分同步数据

注意:从slave开始请求同步数据到slave恢复完成RDB同步数据,这一阶段叫做 全量复制。而slave请求部分数据到恢复完成(AOF阶段),叫做部分复制或增量复制,详细过程如下图:
在这里插入图片描述
状态:

slave:
具有master端全部数据,包含RDB过程接收的数据
master:
保存slave在master复制缓冲区同步的位置(下次同步从该位置开始)

1.1.4.3 命令传播阶段

当master数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的状态,同步的动作称为命令传播。master将接受到的数据变更命令发送给slave,slave接受命令后执行命令。
总结:命令传播阶段的主要作用就是实时保持中从服务器的数据一致。

命令传播阶段出现的断网现象

  • 网络闪断闪连 - 忽略
  • 短时间网络中断 - 部分复制
  • 长时间网络中断 - 全量复制

1. 命令传播阶段的部分复制

部分复制的三个核心要素

  • 服务器的运行id (run id)
  • 主服务器的复制积压缓冲区
  • 主从服务器的复制偏移量

服务器的运行id (run id)

  • 概念
    服务器的运行Id是一个随机的十六进制字符,由40位字符组成。每一台服务器每次运行的身份识别码,一台服务器多次运行可以生成多个运行Id
  • 实现方式
    服务器启动时,自动生成的。master首次连接slave时,会将自己的运行Id发送给slave,而slave会保存master的runid,通过info Server命令可以查看节点的runid。

2. 复制缓冲区

  • 概念
    • 复制缓冲区,又名复制积压缓冲区,是一个先进先出(FIFO)的队列,用于存储服务器执行过的命令,每次传播命令,master会将传播的命令记录下来,并存储在复制缓冲区
    • 复制缓冲区默认存储空间大小是1M,由于存储空间大小是固定的,当入队元素的数量大于队列长度时,最先入队的元素会被弹出,而新元素会被放入队列
  • 由来
    每台服务器启动时,如果开启有AOF或被连接成为master节点,就会创建复制缓冲区。
  • 组成
    • 偏移量
    • 字节值
  • 数据来源
    当master接收到主客户端的指令时,除了执行指令,还会将该指令储存到缓冲区。
  • 工作原理
    • 通过offset区分不同的slave当前数据传播的差异
    • master记录已发送的信息对应的offset
    • slave记录已接收的信息对应的offser

在这里插入图片描述
3. 复制偏移量

  • 概念
    一个数字,描述复制缓冲区中的指令字节位置
  • 分类
    • master复制偏移量:记录发送给所有slave的指令字节对应的位置(多个)
    • slave复制偏移量:记录slave接受master发送过来的指令字节对应的位置(一个)
  • 数据来源
    master端:发送一次记录一次
    slave端:接收一次记录一次
  • 作用
    同步信息,对比master与slave的差异,当slave断线后,恢复数据使用

1.1.4.4 数据同步和命令传播阶段详细流程图

在这里插入图片描述

1.1.4.5 心跳机制

进入命令传播阶段后,master与slave间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线。

  • master心跳
    • 指令:PING
    • 周期:由repl-ping-slave-period决定,默认10秒
    • 作用:判断slave是否在线
    • 查询: info命令,获取slave最后一次链接时间间隔,lag项维持在0或1视为正常
  • slave心跳指令
    • 指令:REPLCONF ACK{offset}
    • 周期:1秒
    • 作用1:汇报slave自己的复制偏移量,获取最新的数据变更指令
    • 作用2:判断master是否在线

心跳阶段注意事项

  • 当slave多数掉线,或延迟过高时,master为保障数据稳定性,将拒绝所有信息同步操作

min-slave-to-write 2
min-slave-max-lag 10

slave数量少于2个或者多有slave延迟都大于等于10秒时,强制关闭master写功能,停止数据同步

  • slave数量和延迟是由slave发送REPLCONF ACK命令做确认

完整流程图
在这里插入图片描述

1.2 哨兵模式

哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master。

1.2.1 哨兵的作用

  • 监控
    不断地检查master和slave是否正常运行。
    master存活检测、master与slave运行情况检测。
  • 通知(提醒)
    当被监控的服务器出现问题时,向其他(哨兵,客户端)发送通知
  • 自动故障转移
    断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务器地址。

注意:哨兵也是一台redis服务器,只是不提供数据服务,通常哨兵配置数量为单数

1.2.2 哨兵的工作原理

1). 主从切换
哨兵在主从切换中经历三个阶段:

  • 监控
  • 通知
  • 故障转移

1.1) 监控阶段

  • 同步各个节点的状态信息
    • 获取各个sentinel的状态(是否在线)- ping
    • 获取master的状态 - info
      • master属性
        • runid
        • role:master
      • 各个slave的详细信息
    • 获取所有slave的状态(根据master中的slave信息)- info
      • slave属性
        • runid
        • role:slave
        • master_host、master_port
        • offset

        • 在这里插入图片描述

1.2) 通知阶段
当被监控的服务器出现问题时,向其他(哨兵,客户端)发送通知,保证每个哨兵信息的对等。
在这里插入图片描述
1.3) 故障转移阶段
太复杂了,记不住。想了解的同学,去百度吧。

1.3 集群

1.3.1 简介

现状问题
业务发展过程中遇到的峰值瓶颈

  • redis提供的服务OPS可以达到10万/秒,当前业务OPS已经达到20万/秒
  • 内存单机容量达到256G,当前业务需求内存容量1T

集群架构
集群就是使用网络将若干台计算机联通起来,并提供统一的管理方式,使其对外呈现单机的服务效果。

集群的作用

  • 分散单台服务器的访问压力,实现负载均衡
  • 分散单台服务器的存储压力,实现可扩展性
  • 降低单台服务器宕机带来的业务灾难

1.3.2 集群的搭建

集群具体怎么搭建的,其实也不难,百度一下就可以了,这里就不写具体步骤了,只描述下注意事项。

  • 搭建集群至少需要六台服务器(至少3主,每个主服务器至少1个从服务器)
  • 启动集群命令
    redis-cli --cluster create --cluster-replicas 1 172.16.88.168:6379 172.16.88.168:6380 172.16.88.168:6381 172.16.88.168:6389 172.16.88.168:6390 172.16.88.168:6391
    • cluster-replicas 1:代表每台主服务器都要有1台从服务器
    • 这6台redis服务器前3个会设置成主,后3个设置成从;如果是8台的话,前4台主,后4台从。

1.3.3 进群中数据是如何存储的?

由于主服务器有多台,当添加新数据的时候,具体会保存到哪台主服务器呢?

  • 一个 Redis 集群包含 16384 个插槽
  • 每个主服务器会分配一部分插槽
  • 新添加一条数据时,会根据key计算出保存到哪个插槽。

在这里插入图片描述
录入或读取数据
在 redis-cli 每次录入、查询键值,redis 都会计算出该 key 应该送往的插槽,如果不是该客户端对应服务器的插槽,redis 会报错,并告知应前往的 redis 实例地址和端口。

  • 录入/读取 数据的时候可以通过 redis-cli -c 命令实现重定向,如果录入/读取 的key计算出的插槽不在该服务器会自动重定向到正确的服务器。
    在这里插入图片描述

1.3.4 主从切换/故障恢复

在一个集群中,如果一台主服务器挂了,redis是怎么处理的?如果主服务器挂了超过一定时间从服务器会晋升为主服务器,原来的主服务器再次上线的话会变成从服务器。配置如下:

  • 设置节点失联事件,超过该时间(ms),集群自动进行主从切换

cluster-node-timeout 15000

2. 企业级解决方案

2.1 缓存雪崩(多个key过期)

1. 出现原因
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

2. 解决方案

  • 构建多级缓存架构
    nginx缓存+redis缓存+ehcache缓存
  • 使用队列或锁
    高并发情况下不适用
  • 让缓存失效时间分散开
    比如在原有的失效时间上加个1-5分钟的随机值。
  • 热点数据特殊处理
    如果是临时热点数据,比如秒杀,就让秒杀结束后再失效。
    如果一直都是热点数据,就设置永久key。

3. 总结
解决方案各种各样,只要能保证不要让key短时间内集中失效就可以,特别是热点数据。

2.2 缓存击穿(单个key过期)

1. 出现原因
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,缓存中读不到数据就去数据库读取,引起数据库压力瞬间增大,造成过大压力。

2. 解决方案

  • 预先设置热点数据
    在一些热点数据高峰访问之前,提前加入redis缓存,延长过期时间。
  • 使用锁
    如果请求到redis中的数据为空就加锁,获取到锁再去判断下redis中有没有数据,多个请求进来的只要一个获取到锁,这样就只有这一个去请求数据库,请求到数据库后放入缓存,然后释放锁。其他线程获取到锁后回再去判断redis,这时候redis已经有值了,就不会去请求数据库了。只不过用锁回降低效率,要慎用。

3. 总结
在涉及架构的时候,提前考虑到这些问题,尽量避免问题的发送。一旦发送了缓存击穿,根据情况去解决,解决方案也不是一成不变的,能解决问题就好。

2.3 缓存穿透

出现原因
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,导致数据库频繁请求不存在得数据,数据库压力增大。这种情况很可能是黑客攻击。

解决方案

  • 对空值进行缓存
    对数据库查询位null得数据也进行缓存,value就位null,然后设置个短暂的过期时间。
  • 白名单策略
    使用bitmaps类型定义一个可访问的白名单,名单id作为bitmaps的偏移量,每次访问和bitmaps里面的id进行比较,如果访问id不在bitmaps里面就进行拦截。

2.4 分布式锁

分布式锁常见的三种实现方式有:redis、zeekooper、数据库。这里只分析Redis实现分布式锁的方式。

Redis实现分布式锁
通过setnx命令设置锁,通过del命令删除锁。

问题1:锁一直不释放怎么办?
比如加锁之后代码出现异常,导致后面的del命令没有执行,锁一直不会释放怎么办?

解决方案
给锁设置过期时间,指定时间没有释放就自动释放。

expire key second


问题2:过期时间设置失败怎么办?
比如加完锁了,代码刚运行到设置过期时间还未执行,这是时候服务器宕机了,导致设置过期时间失败。

解决方案
通过一条命令,在设置锁的同时设置过期时间。

#nx:key不存在时才生效;ex:过期时间
set key value nx ex second


问题3:释放了别人的锁怎么办(1)?
假如A线程获取到了锁也设置了过期时间了,在执行的过程中卡住了,时间到了就自动释放锁了。这时B线程抢到了锁,正在执行,A线程反应过来了继续往下执行,执行完了要手动释放锁,这时锁在B线程呢,那么A就会把B线程的锁释放掉,这种问题怎么办?

解决方案
设置锁的时候将value设置为UUID,这样没过线程获取到的锁的值都是不一样的,手动释放锁的时候先判断下释放的锁的value和自己拥有锁的value是否相同,相同才释放。

代码效果如下:
在这里插入图片描述


问题4:释放了别人的锁怎么办(2)?
在问题3中,手动释放锁的时候需要通过UUID进行比较防止误删。但是,假如A线程刚比较完,自动释放时间就到了,自动释放了,然后B线程快速获取到了锁,而A线程比较完就执行手动操作了,这时A线程就会把B线程的锁给释放掉?

解决方案
要保证比较UUID和手动释放锁的原子性,通过lua(撸啊)脚本可以保证他们的原子性。

#这句话的意思时判断redis中所得value跟我们传过来的是否相同,相同就调用删除key的方法,不相同就返回0
if redis.call(‘get’,KEYS[1])==ARGV[1] then return redis.call(‘del’,KEYS[1]) else return 0 end

如下图:
在这里插入图片描述


其他说明
通过Redisson的看门狗机制也可以实现分布式锁,当一个锁快过期的时候,看门狗会自动延长有效期。


总结
为了确保分布式锁可用,实现分布式锁的时候至少要确保满足以下4个条件:

  • 互斥性。在任意时刻,只有一个客户端能拥有锁。
  • 不会发生死锁。即使一个客户端持有锁的期间崩溃没有手动释放锁,也要保证后续其他客户端依然可以获取锁。
  • 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端不能把别人的锁给释放了。
  • 加锁和解锁必须具有原子性。加完锁,在解锁之前,其他客户端不能干扰。

标签:缓存,slave,手把手,redis,Redis,企业级,master,服务器,数据
来源: https://blog.csdn.net/hpp3501/article/details/121494396

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有