Redis 面试收割 Offer 利器:高可用、分布式锁和分布式缓存通用方案

小七学习网,助您升职加薪,遇问题可联系:客服微信【1601371900】 备注:来自网站

对于 Redis 的基础知识还是很好掌握的,能够满足平常开发和初级面试的要求,但是做到这些还是不够。因为Java 从业者越来越多,竞争日趋激烈,并且企业要求技能掌握地深度越来越高,所以要能够掌握更深层…

对于 Redis 的基础知识还是很好掌握的,能够满足平常开发和初级面试的要求,但是做到这些还是不够。因为Java 从业者越来越多,竞争日趋激烈,并且企业要求技能掌握地深度越来越高,所以要能够掌握更深层次的东西,变成自己的硬实力。

Redis 是很重要的 NoSQL 非关系性数据库,它的技术栈并不是那么海量无边,晦涩难懂。并且在业界都有成熟的通用方案,只要能够在基础之上,深入钻研一下,定能收获满满。

另外,相信大家都有相同的经历,在面试官考查 Redis 的掌握程度的时候,面试者常常被问地面红耳赤,难以作答。

在此,向大家分享相关地干货,希望能对小伙伴们有所帮助,来提高硬实力,以解窘境。与君共勉,共同学习共同进步。

将分享的内容:

  1. Redis 与 Spring Boot 集成及应用
  2. Redis 的持久化机制
  3. Reids 的事务机制
  4. 缓存穿透、缓存雪崩、缓存击穿
  5. Redis 的分布式锁
  6. Redis 的分布式缓存
  7. Redis 的高可用解决方案:主从、哨兵、集群

适用人群:Redis 初中级开发人员、Java 热爱者、Java 进阶者、Java 面试者



Redis 与 Spring Boot 集成及应用

为控制文章篇幅,相关说明和源码请直接下载项目。

Demo 项目地址

Redis 的事务机制

概念

Redis 的事务是一组按照先后顺序执行地命令集。这与关系性数据库的事务概念不一样,需要区分下。

当命令出现执行失败,那么已经执行过的命令也不会回滚,所以不能保证真正意义上的原子性。

实现机制

Redis 的事务执行过程形象地来说就是三步曲:开始、命令排队、执行。

开始:开启事务,将客户端的 REDIS_MULTI 选项打开, 让客户端从非事务状态切换到事务状态。

redis > MULTIOK

命令排队:因已开启事务,所以客户端的请求命令都会被存放到一个事务队列里。但不包括跟事务有关的命令如 EXEC、DISCARD、MULTI 和 WATCH 这四个命令。

redis> MULTIOKredis> SET hw \"Hello World\"QUEUEDredis> GET hwQUEUED

执行:当执行 EXEC 命令时,服务器将队列中的指令以先进先出(FIFO)的方式执行。

执行结果:执行每个命令返回的结果会放到一个“回复队列”中,并在所有命令执行结束之后将“回复队列”返回给客户端,然后客户端从事务状态还原回非事务状态。

redis > EXEC

Redis 事务命令-WATCH

WATCH 命令的机制属于一种乐观锁,保证了数据的一致性。WATCH 命令通过监视 Key 来检查 Value 是否被更改,若 Value 被更改,则该事务执行会失败。

事务执行之后,就会自动取消对应的监视。也可以手动执行 UNWATCH 命令来取消监视。

Redis 的持久化机制

Redis 是内存式数据库,当机器重启后内存中的数据都会丢失。

Redis 持久化机制:RDB 和 AOF。

RDB

概念

RDB 保存地是某一时间段的快照数据,会将数据放到一个 dump.rdb 文件中,当服务器重启时,会将数据直接加载到内存。

RDB 执行流程

在这里插入图片描述

在客户端输入 bgsave 命令后,Redis 调用 bgsaveCommand 函数,该函数 fork 一个子进程执行 rdbSave 函数进行实际的快照存储工作,而父进程可以继续处理客户端请求。当子进程退出后,父进程调用相关回调函数进行后续处理。

RDB 存在的问题

由于 RDB 机制存储的是一段时间的数据快照,那么假如在下一时间点进行记录快照之前服务器崩溃,那么这段数据将会丢失。

RDB 优缺点

  • 优点:数据恢复时,加载到内存较快
  • 缺点:数据易丢失

AOF

概念

AOF 保存地是 Redis 服务器执行地每条命令,保存在 appendonly.aof 文件中,当服务器重启时,会将指令重新执行一遍。

在这里插入图片描述

AOF 存在的问题

  • 当服务器重启恢复数据时,需要大量的执行命令,所以数据恢复较慢。
  • 不能保证百分百命令不丢失,也会丢失出现故障前的很少命令。

AOF 触发写入的模式

AOF 写入命令时调用的是 write 函数,底层调用的是 fsync 函数,该函数是阻塞并且缓慢的操作,所以需要控制执行的频率。

Redis 通过 appendfsync 配置控制执行 fsync 的频率。具体有如下 3 种模式:

  • no:不执行 fsync,由操作系统负责数据的刷盘。数据安全性最低但 Redis 性能最高。
  • always:每执行一次写入就会执行一次 fsync。数据安全性最高但会导致 Redis 性能降低。
  • everysec:每 1 秒执行一次 fsync 操作。属于折中方案,在数据安全性和性能之间达到一个平衡。

建议:生产环境一般配置为 appendfsync everysec,即每秒执行一次 fsync 操作。

AOF 优缺点

  • 优点:数据不易丢失
  • 缺点:数据恢复速度较慢

缓存穿透、缓存并发、缓存雪崩

缓存穿透

概念

当客户端通过通过一个键去访问缓存,假设缓存中没有,此时又去访问数据库,假设数据库中也没有,此时返回 null 并且也不会放到缓存中。

在这里插入图片描述

产生的问题

当高并发访问这个键的时候,请求就会频繁访问数据库,会对数据库带来很大的压力。

解决问题的方案

当数据库返回 null 时,就把 null 值放到缓存中。为了避免产生大量的 null 值,可在放到缓存前判断该缓存是否常用。

缓存并发

概念

当客户端访问热点数据的时候,假如某个热点数据已超时失效,那么此时请求就会访问数据库。

在这里插入图片描述

产生的问题

当高并发访问失效的某个热点数据时,大量的请求就会访问数据库,会对数据库带来很大的压力。

解决问题的方案

  • 限流:限制请求访问的数量。
  • 加锁:对缓存数据加锁,但是不能并发访问,故不推荐。
  • 错峰失效:对热点数据在访问高峰期的时间段,不让其失效,推荐该方案。

缓存雪崩

概念

大量的缓存数据,同时超时失效,就像雪山崩塌一样。

在这里插入图片描述

产生的问题

当系统启动时,会对系统进行预热,并且对缓存设置的失效时间相同,那么同一批缓存将会在未来同一时刻集体失效。如果客户端在这一刻进行高并发访问,会对数据库造成巨大压力。

解决问题的方案

对不同的 key 设置不同的超时时间,能够避免缓存集体同时失效。

Redis 的高可用实现机制

高可用,英文翻译是 High Availability,简称为 HA。在分布式系统架构设计中,系统的高可用性是必须要考虑的。

百科对高可用的解释是:在对系统进行日常维护或者突发系统崩溃时,尽量缩短停机的时间,进而提高系统的可用性。

Redis 常用的高可用实现机制有主从复制(Master-Slave)、哨兵(Sentinel)、集群(Cluster)。

带着问题去学习:为什么会有三种机制呢?每种机制能解决什么问题呢?每种机制又能带来什么问题呢?

主从复制

在这里插入图片描述

作用

  • 能够实现读写分离,提高服务器的负载能力和性能
  • 能够容灾恢复,增强高可用性

实现原理

在这里插入图片描述

全量同步——从服务器数初始化据库

  1. 从服务器发送请求:当从服务器启动时,会向主服务器发送 SYNC 命令,请求同步数据。
  2. 主服务器执行操作:主服务器接收到消息之后,开始执行 BGSAVE 命令生成 RDB 快照文件并且缓存执行的所有写命令。在快照文件生成之后,主服务器将 RDB 快照文件和缓存地命令发送给从服务器。
  3. 从服务器接收请求:从服务器首先会加载 RDB 快照文件,然后执行缓存地命令。

增量同步——从服务器数据库保持同步

正常情况下:

从服务器在初始化同步完成之后,主服务器接收到的所有命令都会异步的发送给从服务器用来保持主从数据的一致性。

网络短时断开后重连:

当 Master 和 Slave 短时断开并重连后,为避免 Master 发送全量数据,Slave 需要发送 PSYNC 的请求命令,此时 Master 只会发送断开期间的增量数据。这称为断点续传。

断点续传的原理:

Master-Slave 两端通过维护一个 offset 记录当前已经同步过的命令,Slave 断开期间,Master 的客户端命令会保持在缓存中,在 Slave 重连之后,告知 Master 断开时的最新 offset,Master 则将缓存中大于 offset 的数据发送给 Slave。

主从持久化方案

从服务器开启持久化,同时主服务器禁用持久化。

从服务器故障处理

当从服务器崩溃之后,重启之后进行初始化,会自动地同步主服务器的数据。

主服务器故障处理

当主服务器崩溃之后,情况就变得复杂了,需要手动执行相关命令。 解决方案如下:

  • 首先需要手动地选择一个从服务器升级为主服务器(需要手动调整所有相关的从服务器),执行命令:replicaof no one。
  • 然后启动之前已经崩溃的主服务器作为从服务器,执行命令:replicaof。

优缺点

优点:

  • 读写分离:从节点可以扩展主服务器节点的读能力,能有效应对大并发量的读操作。
  • 高可用性:采用主备结构,在主服务器出现故障时,能够实现主备切换,能够保证服务平稳的运行。

缺点:

  • Redis 在主服务器崩溃之后需要繁琐的人工干预来恢复服务。
  • 主服务器的写能力会受到单机的限制。
  • 主服务器的存储能力受到单机的限制。
  • 由于主服务器数据库禁止了持久化,所以需要保证服务恢复的安全性,否则会导致主从数据库恢复了错误的数据。

应用场景

读操作的频率大于写操作的频率,主服务器只进行写操作,从服务器负责读操作。

小结

  • Redis 的主从复制机制允许主从服务器的数据出现短时不一致,不过能保证最终一致性。
  • Redis 允许用户配置主库的 min-slaves-to-write (代表至少 N 台从服务器完成复制,才允许主服务器写入)和 min-slaves-max-lag (允许从服务器断开连接的时间)这两个配置项来控制分区中数据不一致的影响。
  • 一主多从的主从复制对海量数据的存储能力跟集群相比还是较弱。
  • 主从切换需要人工手动操作,带来了操作地安全性风险。

哨兵

Redis 的主从复制机制需要手动进行主从切换,这显然是个问题。为了解决该问题,Redis 又提供了哨兵机制。

哨兵是一个独立于数据服务器的进程,用于自动监控和维持分布式 Redis 系统的稳定运转。

作用

当主从模式下最关键的主服务器出现故障时,哨兵进程能够自动的察觉。然后它会在剩余的从服务器中”选举”出新的主服务器,使系统能够自动恢复。

实现原理

单哨兵实例部署:

在这里插入图片描述

  1. 哨兵启动时会与主服务器建立连接,并且间接的获得所属从服务器信息,完成哨兵的初始化。
  2. 哨兵通过发送命令来监测各个 Redis 主从服务器是否可用。
  3. 当主服务器出现故障不可用时,哨兵监测到这个故障后,就会启动故障切换机制,作废当前故障的主 Redis 服务器,将其中的一台 Redis 从服务器修改为主服务器。
  4. 然后将这个消息发给各个从服务器,使得它们也能做出对应的修改。

多哨兵实例部署:

在这里插入图片描述

  1. 哨兵启动时会与主服务器建立连接,并且间接的获得所属从服务器信息,完成哨兵的初始化。
  2. 哨兵初始化完成之后,会周期性的和主从服务器、其它哨兵节点(通过消息频道的订阅/发布)进行通信。
  3. 哨兵每 10 秒会向所有服务器发送一次 INFO 命令,用来获得相关 Redis 服务器的当前状态以便决定是否需要故障恢复。
  4. 当一个哨兵在 down-after-milliseconds 规定时间内未收到主服务器的响应,则当前哨兵“主观”认为主服务器下线, 同时呢其它哨兵也会发现,那么就要进行投票决定,当超过当前哨兵配置中投票决定的数目时, 则当前哨兵“客观”认为主服务器下线,即哨兵们达成一致。最后哨兵集群会选举出哨兵 Leader 来进行 Redis 主从服务器的主从状态切换(使用 Raft 算法)。

哨兵集群部署

  • 对哨兵实现多实例部署,避免出现单点故障。
  • 哨兵的部署数量最好是大于等于 3 的奇数,为了更容易投票选出哨兵 Leader 来执行主从切换。
  • 哨兵间感知:通过共同订阅 Master 节点的 channel:__sentinel__:hello,来感知新加入的 Sentinel,然后建立长连接。

集群

一主多从的部署方式在处理和存储海量数据的时候显得捉襟见肘,而且对于写操作的压力全落在了 Master 服务器。

Redis 为了解决以上问题,在 Redis 3.0 加入了集群模式,这种模式是通过增加服务节点来横向地扩展服务能力。

一个服务节点可以理解为一主多从的组合体,许多个这样的组合体构成一个集群。

Redis 集群没有中心节点的概念,对于客户端来说,整个集群是一个整体,客户端能够连接到任意一个节点。

作用

  • 大大提高了存储能力
  • 实现了写操作的负载均衡

认识哈希槽(Hash Slot):

在这里插入图片描述

  • 哈希槽的每个数字都对应着一台服务器.
  • 假设计算 Key 的哈希值为 hashcode,那么 n = hashcode % 6 + 1。
  • 假设 n = 6,那么便会路由到服务器 3。
实现原理

Redis 集群的哈希槽

在这里插入图片描述

  • Redis 集群的哈希槽的原理跟哈希槽(Hash Slot)的原理相似。
  • Redis 集群的哈希槽大小为(214=16384),也就是取值范围为区间[0, 16383],最多能够支持 16384 个节点。
  • Redis 集群会采用 CRC16 算法计算 key 的哈希值。
  • 计算公式:hashcode = CRC16(key); n = hashcode % 16384

原理分析

  1. 客户端发送请求,Redis 集群会采用 CRC16 算法计算 key 的哈希值,然后对该哈希值进行取模,得到一个值记为 n。
  2. 由于一个哈希槽区间对应一个 Redis 主服务器,上述的值 n 能够落在所属的哈希槽区间。那么则根据数据 n 就能够找到对应的 Redis 主服务器。
Redis 服务器节点间的关系

在这里插入图片描述

  • 集群中各个 Redis 服务器之间是相互连通的,采用的是 PING-PONG 机制,内部使用了二进制协议优化传输速度和带宽。
  • 客户端与 Redis 节点是直连的,不需要中间代理层,并且不需要连接集群所有节点,只需连接集群中任何一个可用节点即可。
  • 在 Redis 集群中,要判定某个主节点不可用,需要各个主节点进行投票,如果半数以上主节点认为该节点不可用,该节点就会从集群中被剔除,然后由其从节点代替,可以实现容错。
  • 因为这个投票机制需要半数以上,所以一般来说,要求节点数大于 3,且为单数。

Redis 集群不可用:

  • 如果某个主节点被认为不可用,并且没有从节点可以代替它,那么就构建不成哈希槽区间[0, 16383],此时集群将不可用。
  • 如果原有半数以上的主节点发生故障,那么无论是否存在可代替的从节点,都认为该集群不可用。

小结

  • 具有可复制、高可用和分片特性。
  • 每个节点的 Master 服务器负责处理槽进行数据存储,从服务器只负责接收同步的数据。
  • 每个节点的 Master 服务器只处理属于自己槽的请求,当发现不属于自己槽的命令时,它将会把槽对应节点的地址返回给客户端。
  • Redis 集群是不保证数据一致性的,这也就意味着,它可能存在一定概率的数据丢失现象,所以更多地使用它作为缓存。

Redis 实现分布式锁

在多线程环境中控制对共享资源的并发访问。

分布式锁

什么是分布式锁:控制分布式系统对共享资源的并发访问。

为什么设计分布式锁:保证同一时刻只有一个客户端可以操作共享资源,保证数据的一致性。

分布式锁具备的特性

  • 排他性:在同一时刻,只能有一个客户端获取锁,其他客户端不能获取锁。
  • 防死锁:获取锁在一定的时间后需要释放,即使某一进程在持有锁期间出现崩溃未主动释放锁。
  • 可重入性:同一个节点上的同一个线程如果获取了锁之后依然可以再次获取这个锁。
  • 锁超时:和本地锁一样支持锁超时,防止死锁。
  • 高可用:加锁和解锁需要保证高可用,防止分布式锁失效。
  • 支持阻塞和非阻塞:和 ReentrantLock 一样支持 lock 和 trylock 以及 tryLock(long timeOut)。

实现分布式锁的常见方案

  • 基于数据库实现分布式锁,如 MySQL
  • 基于 ZooKeeper 实现分布式锁
  • 基于 Redis 实现分布式锁
  • 谷歌的 Chubby

基于 Redis 实现分布式锁

  • Redis 分布式锁的简单实现:基于 redis client 原生 api 来实现,即调用 Redis 的 setnx() 等相关方法。
  • Redisson:Redisson 是官方推荐地 Redis 的企业级客户端组件框架。Redisson 通过 Netty 支持非阻塞 I/O。Redisson 封装了锁的实现,它继承了 java.util.concurrent.locks.Lock 的接口。
  • RedLock:Redlock 是 Redis 官方提出的实现分布式锁管理器的算法。
Redis 实现分布式锁的大致思路

1. 获取锁的时候加锁,设置锁的唯一标识 key,返回 1 加锁成功,返回 0 加锁失败;锁的 value 值为一个随机生成的 UUID。

setnx key value

2. 设置锁超时时间,防止死锁

expire key timeout

3. 释放锁的时候,通过 UUID 判断是不是该锁,若是该锁,则执行 delete 进行锁释放。

del key

注意:假如在步骤 1 执行之后服务器出现宕机,导致步骤 2 无法执行,那么也会出现死锁。Redis 2.6.12 版本的改进方法是 set key value NX EX max-lock-time。

NX:只有这个 key 不存才的时候才会进行操作,if not exists;EX:设置 key 的过期时间为秒,具体时间由第 5 个参数决定。

分布式锁的注意事项:

  • 分布式锁的开销
  • 加锁的粒度
  • 加锁的方式

分布式锁的应用场景:

  • 秒杀
  • ID 生成

Redisson 实现分布式锁原理

在这里插入图片描述

执行主流程:

  1. 线程通过竞争获取锁。
  2. 如果获取锁失败则循环获取锁;如果获取锁成功,则通过 Hash 算法选择一个主节点,然后执行 lua 脚本,保存数据到 Redis。

Watch Dog 机制:

它是一个后台线程,用来不断地延长锁的超时时间,让业务线程一直持有锁。当业务线程释放锁的时候,同时关闭这个后台线程。

Lua 脚本

将操作封装在 Lua 脚本发给 Redis 执行,能保证操作的原子性。

Redisson 相关问题

失效时间设置多长时间为好?

每获得一个锁时,只设置一个很短的超时时间,同时起一个线程在每次快要到超时时间时去刷新锁的超时时间。在释放锁的同时结束这个线程。

Redisson 实现分布式锁缺点

缺点

  • 在哨兵模式或主从复制模式下,如果 Master 服务器宕机,可能导致多个客户端完成加锁。

解释:

  • 客户端 1 对 Master 节点写入了 Redisson 锁,此时会异步复制到对应的 Slave 节点。如果过程中发生 Master 节点宕机,进行主备切换,Slave 节点变成了 Master 节点。
  • 如果客户端 2 来尝试加锁的时候,在新的 Master 节点上也能加锁,此时就会导致多个客户端对同一个分布式锁完成了加锁。

Redis 的分布式缓存

Redis 单实例节点

在实际应用中,单实例节点面临地挑战:

  • 存储容量的限制:单机的内存和磁盘容量是有限的。即使有淘汰机制,也难以有效解决。
  • 吞吐量的限制:当数据量很大的时候,会增加单次请求的处理耗时,进而影响吞吐量。
  • 单点故障:当服务器宕机或者重启的时候,容易造成业务数据丢失和难以迅速恢复服务。

Redis 分布式缓存解决方案:

  • 水平拆分
  • 主从复制
  • 故障转移
  • 集群
水平拆分

水平拆分(Sharding),即分片。

  • 所谓的水平拆分/分片,就是将对数据的存储和访问分散到不同的节点进行处理。在原则上,各分片节点相互独立,其存储的数据之间没有交集。
  • 水平拆分对外部服务透明,通过路由机制来实现数据的分布存储和请求路由。

数据分布存储机制:

它是一种分配数据的机制,通常采用一致性 Hash 映射机制。

所谓的一致性 Hash 映射机制是 Hash 映射和范围映射的结合体,原理如下:

  1. 首先对 key 进行 hash 运算,得到一个 Hash 值。
  2. 然后对 hash 值做范围映射,确定出对应的服务实例。

请求路由:

  • 它用来将请求路由到对应的实例,从而获得正确的数据。它的机制和数据分布存储机制保持一致。
  • 对于只读的跨实例请求,需要将请求中的多个 key 分别转发到对应的实例,然后将查询的结果进行合并。
主从复制

主从复制也叫主备复制,Replication。

所谓的主从复制,就是一主多从,主服务器节点负责写操作,从服务器进行数据备份和负责读操作。具体原理在 Redis 高可用章节中已分析,此处不再赘述。

故障转移
  • 故障转移(Failover):当 Master 故障时,Slave 可以成为新的 Master,对外提供读写服务,这种运行机制称为 Failover。

实现方式:

故障转移可以用高可用中的哨兵机制实现,具体原理在 Redis 高可用章节中已分析,此处说下整体运作机制,不再详细赘述。

整体运行机制:

  1. 多个 Sentinel 之间通过订阅 Master 节点的 channel:__sentinel__:hello,获得感知,并建立长连接。
  2. 当一台 master 真正宕机后,经过一个 Sentinel 的“主管下线”,再经过多个 Sentinel 确认后的“客观下线”。
  3. Sentinel 之间通过 Raft 协议算法来选举出一个 Sentinel Leader 来执行主从切换。
集群
  • 在 Redis 3.0 之后,Redis 通过去中心化的方式,将 sharding、replication、failover 解决方案进行节点化,形成多节点分布式部署,构成一个集群。
  • 客户端可以和集群中的任一节点连接。

小七学习网,助您升职加薪,遇问题可联系:客服微信【1601371900】 备注:来自网站

免责声明: 1、本站信息来自网络,版权争议与本站无关 2、本站所有主题由该帖子作者发表,该帖子作者与本站享有帖子相关版权 3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和本站的同意 4、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责 5、用户所发布的一切软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。 6、您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。 7、请支持正版软件、得到更好的正版服务。 8、如有侵权请立即告知本站(邮箱:1099252741@qq.com,备用微信:1099252741),本站将及时予与删除 9、本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章和视频仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。