Redis 学习-下

Posted 菜鸟-肥龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis 学习-下相关的知识,希望对你有一定的参考价值。

六、Redis 主从复制

6.1、主从复制概念

  • 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是 单向 的,只能由主节点到从节点。

  • 默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

  • 主从复制的作用主要包括:

    • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
    • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
    • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
    • 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

如果没有主机了,这个时候能不能选择出来一个主句呢?

  • 可以,不过得手动设置
  • 如果主机断开了连接,我们可以使用SLAVEOF no one让自己变成主机!其他的节点就可以手动连接到最新的主节点(手动)!如果这个时候老大修复了,那么就重新连接!

6.2、建立主从复制

6.3、主从复制特点

1、主从复制机制

  • 当主实例和副本实例连接良好时,主实例通过向副本发送命令流来保持副本更新,以便复制主端发生的对数据集的影响:客户端写入,密钥过期或驱逐,任何其他更改主数据集的操作。
  • 当 master 和 replica 之间的链接中断时,由于网络问题或因为 master 或 replica 检测到超时,replica 重新连接并尝试进行部分重新同步:这意味着它将尝试仅获取部分它在断开连接期间错过的命令流。
  • 当无法进行部分重新同步时,副本将请求完全重新同步。这将涉及一个更复杂的过程,其中 master 需要创建其所有数据的快照,将其发送到副本,然后随着数据集的变化继续发送命令流。

2、主从复制特性

  • Redis 使用异步复制,异步复制到主服务器确认处理的数据量。
  • 一个master可以有多个replicas。
  • 副本能够接受来自其他副本的连接。除了将多个副本连接到同一个主服务器之外,副本还可以以类似级联的结构连接到其他副本。从 Redis 4.0 开始,所有子副本都将从主副本接收完全相同的复制流。
  • Redis 复制在 master 端是非阻塞的。这意味着当一个或多个副本执行初始同步或部分重新同步时,主控将继续处理查询。
  • 复制在副本方面也很大程度上是非阻塞的。当副本执行初始同步时,它可以使用旧版本的数据集处理查询,假设您在 redis.conf 中配置了 Redis。否则,您可以将 Redis 副本配置为在复制流关闭时向客户端返回错误。但是,在初始同步后,必须删除旧数据集并加载新数据集。副本将在这个短暂的窗口期间阻止传入连接(对于非常大的数据集,这可能长达几秒钟)。从 Redis 4.0 开始,可以配置 Redis,以便旧数据集的删除发生在不同的线程中,但是加载新的初始数据集仍将发生在主线程中并阻塞副本。
  • 复制既可以用于可扩展性,以便为只读查询提供多个副本(例如,可以将慢O(N)操作卸载到副本),也可以仅用于提高数据安全性和高可用性。
  • 可以使用复制来避免让 master 将完整数据集写入磁盘的成本:一种典型的技术涉及配置 masterredis.conf以完全避免持久化到磁盘,然后连接配置为不时保存的副本,或者使用启用 AOF。但是,必须小心处理此设置,因为重新启动的 master 将从空数据集开始:如果副本尝试与其同步,则副本也将被清空。

6.4、主从复制原理

1、主从复制建立流程

  • 当从库和主库建立MS关系后,会向主数据库发送SYNC命令
  • 主库接收到SYNC命令后会开始在后台保存快照(RDB持久化过程),并将期间接收到的写命令缓存起来
  • 当快照完成后,主Redis会将快照文件和所有缓存的写命令发送给从Redis
  • 从Redis接收到后,会载入快照文件并且执行收到的缓存的命令
  • 之后,主Redis每当接收到写命令时就会将命令发送从Redis,从而保证数据的一致

总结:当启动一个 slave node 的时候,它会发送一个 PSYNC 命令给 master node。 如果这是 slave node 初次连接到 master node,那么会触发一次 full resynchronization 全量复制。此时 master 会启动一个后台线程,开始生成一份 RDB 快照文件, 同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB 文件生成完毕后, master 会将这个 RDB 发送给 slave,slave 会先写入本地磁盘,然后再从本地磁盘加载到内存中, 接着 master 会将内存中缓存的写命令发送到 slave,slave 也会同步这些数据。 slave node 如果跟 master node 有网络故障,断开了连接,会自动重连,连接之后 master node 仅会复制给 slave 部分缺少的数据。

2、部分复制原理

  • 复制偏移量

    • 主节点和从节点分别维护一个复制偏移量(offset),代表的是主节点向从节点传递的字节数;主节点每次向从节点传播N个字节数据时,主节点的offset增加N;从节点每次收到主节点传来的N个字节数据时,从节点的offset增加N。
    • offset用于判断主从节点的数据库状态是否一致:如果二者offset相同,则一致;如果offset不同,则不一致,此时可以根据两个offset找出从节点缺少的那部分数据。
  • 复制积压缓存区

    • 复制积压缓冲区是由主节点维护的、固定长度的、先进先出(FIFO)队列,默认大小1MB。
    • 如果offset偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制。
    • 如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制。

6.5、心跳机制

在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING和REPLCONF ACK。心跳机制对于主从复制的超时判断、数据安全等有作用。

1、主 —> 从:PING

  • 每隔指定的时间,主节点会向从节点发送PING命令,这个PING命令的作用,主要是为了让从节点进行超时判断。
  • PING发送的频率由repl-ping-slave-period参数控制,单位是秒,默认值是10s。

2、从 —> 主:REPLCONF ACK

  • 在命令传播阶段,从节点会向主节点发送REPLCONF ACK命令,频率是每秒1次;命令格式为:REPLCONF ACK offset,其中offset指从节点保存的复制偏移量。\\

  • REPLCONF ACK命令的作用包括

    • 实时监测主从节点网络状态
    • 检测命令丢失
    • 辅助保证从节点的数量和延迟

七、哨兵模式

7.1、哨兵模式概念

1、哨兵定义

  • Sentinel(哨兵)是用于监控redis集群中Master状态的工具,是Redis 的高可用性解决方案,sentinel哨兵模式已经被集成在redis2.4之后的版本中。sentinel是redis高可用的解决方案,sentinel系统可以监视一个或者多个redis master服务,以及这些master服务的所有从服务;当某个master服务下线时,自动将该master下的某个从服务升级为master服务替代已下线的master服务继续处理请求。

2、哨兵模式作用

  • 监测 :哨兵(Sentinel )会不断检查你的 Master 和 Slave 是否运作正常。
  • 通知:哨兵(Sentinel)可以通过 API 同通知系统管理员活其他应用程序,其中一个受监控的 Redis 服务出现问题。
  • 自动故障转移:如果一个 master 没有按预期工作,Sentinel 可以启动一个故障转移过程,其中一个 Slave 被提升为 master ,其他额外的 Slave 被重新配置为使用新的 master ,并且使用 Redis 服务器的应用程序被告知要使用新地址连接。
  • 配置提供者:哨兵(Sentinel)充当客户端服务发现的授权来源,客户端连接到 Sentinel 以请求负责给定位服务的当前 Redis 主服务器的地址。如果发送故障转移,Sentinels 将报告新地址。

3、哨兵模式配置

7.2、哨兵进程的工作方式

1】、每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令。

​ 2】、如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN)。

​ 3】、如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态。

​ 4】、当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)。

​ 5】、在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。

​ 6】、当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。

​ 7】、若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。

7.3、哨兵模式的优劣

1、优势

  • 哨兵集群,基于主从复制模式,所有主从复制的优点,它都有
  • 主从可以切换,故障可以转移,系统的可用性更好
  • 哨兵模式是主从模式的升级,手动到自动,更加健壮

2、劣势

  • 维护起来更加困难
  • Redis 不好在线扩容,集群容量一旦达到上限,在线扩容就十分麻烦
  • 实现哨兵模式的配置比较繁琐

八、Redis 缓存异常

8.1、缓存异常概念

1、缓存穿透

  • Key 对应的数据在 DB(数据库)中并不存在,每次针对此 Key 的请求从缓存中获取不到,请求都会到 DB(数据库)中获取,大量的请求直接从 DB 中获取可能会压垮数据库。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

2、缓存击穿

  • Redis 中某个热点数据的 key,到期失效,同时有大量请求过来访问这个 key ,在 Redis 中无法获取到数据,就会直接访问 DB(数据库)并把数据存入缓存中,这个过程就可能把数据库压垮。

3、缓存雪崩

  • Redis 中量的 key 在某个时间段集中过期,同时有大量请求过来,这是就会直接访问 DB(数据库),给 DB(数据库)带来很大的压力。

4、缓存预热

  • 就是在系统上线后,将相关的缓存数据直接加载到缓存系统中。这样就避免了在用户请求的时候,先查询数据库,然后再将数据缓存的问题。

8.2、缓存异常解决方案

1、缓存穿透

  • 布隆过滤器:对所有可能查询的参数以Hash的形式存储,以便快速确定是否存在这个值,在控制层先进行拦截校验,校验不通过直接打回,减轻了存储系统的压力

  • 缓存空对象:如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

2、缓存击穿

  • 设置热点数据永不过期:就是不给热点数据设置过期时间,后期当这类术数据过多,手动进行清理。

3、缓存雪崩

  • 过期时间设置随机值:我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟,这样每一个缓存的过期时间的重复率就会降低。
  • 数据预热:提前把热点数据存入缓存,设置不同的过期时间,尽量让缓存失效的时间变得均匀些。

九、Redis 分布式锁

8.1、分布式锁概念

8.2、分布式锁需要具备哪些条件

  • 互斥性:在任意一个时刻,只有一个客户端持有锁
  • 无死锁:即便持有锁的客户端崩溃或者其他意外事件,锁仍然可以被获取
  • 容错:只要大部分 Redis 节点都活着,客户端就可以获取和释放锁

8.3、Redis 实现分布式锁

1、基础命令

  • SETNX 命令:当且仅当 key 不存在,将 key 的值设置为 value ,并且返回 1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回 0。

  • GETSET 命令:将给定 key 的值设置为 value,并返回 key 的旧值(old value),当 key 存在但不是字符串类型时,返回一个错误,当 key 不存在时,返回 nil。

  • GET 命令:返回 key 所关联的字符串值,当 key 存在但不是字符串类型时,返回一个错误,当 key 不存在时,返回 nil。

  • DEL 命令:删除给定的一个或多个 key,不存在的 key 会被忽略。

  • SET 命令:

    • EX seconds -- 设置指定的过期时间,以秒为单位。
    • PX 毫秒——设置指定的过期时间,以毫秒为单位。
    • EXAT timestamp-seconds -- 设置密钥过期的指定 Unix 时间,以秒为单位。
    • PXAT timestamp-milliseconds -- 设置密钥到期的指定 Unix 时间,以毫秒为单位。
    • NX-- 仅当密钥不存在时才设置它。
    • XX-- 仅当密钥已存在时才设置它。
    • KEEPTTL-- 保留与密钥关联的生存时间。
    • GET-- 返回存储在 key 中的旧字符串,如果 key 不存在,则返回 nil。如果存储在 key 的值不是字符串,则返回错误并中止SET 。
  • EXPIRE 命令:设置 key 对应的过期时间。

2、分布式锁基础版本

  • 步骤:

    • SETNX 可以直接加锁操作:SETNX lock 【current unix time】
    • 客户端可以尝试 SETNX lock 【current unix time】操作,如果返回 1,表示客户端已经获取锁,可以往下操作,操作完成之后,通过 DEL lock 释放锁。
    • 如果返回 0,说明 lock 已经被其他用户拥有,可以循环请求,直到成功获得锁或者重试超时。
  • 缺陷:

    • 拥有锁定的客户端出现宕机或者其他意外,容易造成 死锁

3、分布式锁升级版 —— 解决死锁问题

  • 思想:设置过期时间(要保证设置过期时间和设置锁具有原子性
    • expir key :设置过期时间(同设置锁的命令,分为两步执行,不具备原子性)
    • SET lockKey value NX EX 30 :具备原子性
  • 缺陷:
    • 过期时间如何保证大于业务执行时间?
    • 如何保证锁不会被误删除?

4、分布式锁最终版 —— 解决上文遗留问题

  • 如何保证锁不会被误删除?
    • SET lockKey value NX EX 30:设置的 value 设置一个唯一的标识,删除的时候判断之后是自己的才执行删除(不具备原子性,可以通过 lua 脚本实现原子性)。
  • 过期时间如何保证大于业务执行时间?
    • 开启子线程轮询刷新过期时间

十、Redis 常见问题

10.1、Redis 双写一致性问题

1、缓存 + 数据库 读写模式(Cache Aside Pattern)

  • 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
  • 更新的时候,先更新数据库,然后在删除缓存。

2、最佳实现:数据异步同步

  • Canal:基于数据库增量日志解析,提供增量数据订阅和消费https://github.com/alibaba/canal

  • mysql会将操作记录在Binary log日志中,通过canal去监听数据库日志二进制文件,解析log日志,同步到redis中进行增删改操作。

  • canal的工作原理:canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议;MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal );canal 解析 binary log 对象(原始为 byte 流)。

以上是关于Redis 学习-下的主要内容,如果未能解决你的问题,请参考以下文章

Redis进阶学习07--分布式缓存--下

Redis学习之路(002)-Ubuntu下redis开放端口

Redis学习:Windows环境下的Redis安装与配置

深入学习Redis主从复制(下)

在微博微信场景下学习Redis数据结构

redis学习