Redis及缓存雪崩、缓存穿透

本文大纲

一、简介

二、Redis持久化方案

三、Redis的缓存雪崩,缓存穿透,缓存预热,缓存更新,缓存降级等问题及解决方案

一 、简介

1.Redis的特点

  • Redis是一种非关系数据库(不会像MySQL一样多个表直接存在直接关联关系),也是一种内存型数据库。

  • Redis是单线程的,并且是直接基于内存的,所以执行效率会非常高
    为什呢Redis是单线程的呢?(经典面试题)
    官方回答 : 因为Redis是基于内存的,所以CPU不会是Redis的瓶颈,但是内存会是,而且多线程的实现比较麻烦,直接使用单线程更省事一点(PS:这真的是官方说的,虽然说em....有一点...你懂得,但真实原因就是这么简单)
    有图为证 :


如果有兴趣的话可以去看一下传送门
因为Redis是直接操作内存的,而且是单线程的,避免了频繁的切换上下文,所以速度非常快

2.Redis的数据结构

  • String : 最普通和常用的一种结构,key/value
  • Hash : 类似于HashMap,底层也是哈希表
  • List : 类似于LinkedList,底层是双向链表
  • Set : 类似于HashSet,于HashSet的特性一样,不允许重复数据,不记录元素添加顺序
  • SortSet : 会自动的进行排序的set
  • Streams(流) : 18年刚刚发布的一个新的数据结构,不是太了解,相关资料比较少

二 、Redis持久化方案

1.RDB(默认)

  • RDB是redis默认的持久化方式
  • RDB是采用快照的方式来进行数据持久化的,当符合快照的条件时Redis会自动对内存中的数据进行快照,然后持久化到硬盘中
  • 触发条件
    • 符合自定义配置的快照规则
    • 执行save或者bgsave命令
      • eg : save 60 10000 :表示1分钟内至少100个键被更改则进行快照。
    • 执行flushall命令
    • 执行主从复制操作
    • 在redis.conf中设置快照规则
      • 打开redis.config文件,202行,这是RDB的默认配置,满足下面三个之一就会被触发

      • 在247行,配置快照生成地址

      • 在237行可以配置生成快照的名称,默认是dump.rdb

  • 每次redis启动时都会去读取dump.rdb快照文件,将数据加载到内存中
  • 原理 : Redis使用fork函数复制一份当前进程的副本(创建一个子进程)父进程继续接收处理客户端的请求,子进程负责将内存中的数据存储到硬盘中,当子进程写入完所有数据之后会用新的rdb文件替换旧的rdb文件
  • 细节 : 快照的时候并不会修改原有的rdb文件,而是用新生成的替换旧的,所以rdb文件是一定存在的,rdb文件是经过压缩的二进制文件,所以占用内存会很少,并且方便读取
  • 优点 : 因为是复制除了一个子进程来实现数据存储,所以父进程还是可以继续响应客户端的请求,所以客户端基基本不会受到影响,而且rdb是压缩后的二进制文件,进行数据恢复的速度会比较快,所以rbd非常适合用来做数据备份
  • 缺点 : 必须要满足rdb的条件才会执行数据备份,所以有可能因为Redis的突然宕机导致部分数据丢失,所以设置快照条件时必须足够严谨

2.AOF

  • 默认情况下,AOF是处于关闭状态的
  • 只要当前指令会修改Redis中的数据,那么Redis就会将这条命令存储到硬盘中,比较消耗资源,当然我们也可以通过固态硬盘等硬件来提升性能
  • 操作 :
    • 可以通过修改 redis.conf 配置文件593行中 appendonly 参数开启 AOF 方式

    • 默认的文件名是 appendonly.aof,可以通过597行的 appendfilename 参数修改

    • 可以通过623行的appendfsync来修改写入策略
      • everysec(默认) : 每秒写一次
      • always : 每次有redis命令执行时就写一次,比较安全,但是效率较低
      • no : 由系统决定什么是后写入,有可能导致数据丢失

    • 是否在重写文件时进行写入操作,645行
      • no(默认) : 会阻塞正在重写的进程,执行写入操作
      • yes : 不会阻塞正在重写的进程,等到重写完成后再进行写入操作,性能较高

    • 设置重写的文件增长比例以及最小内存,664行
      • percentage : 表示当前 aof 文件大小超过上一次 aof 文件大小的百分之多少的时候会进行重写。
      • min-size : 重写时最小的aof内存大小

    • 异常处理机制,689
      • yes(默认) : 如遇到停电等异常,在恢复之后会继续重写
      • no : 恢复之后直接失败

  • 原理 :
    • Redis可以在AOF文件太大时都AOF中的命令进行重写,重写后的AOF文件中是恢复全部数据的最小命令合集(比如有一个key修改俩次的命令,那么只会保留最后一次的修改命令)
    • 重写操作是安全的,在重写过程中并不会删除掉旧的AOF文件,并且如果有新的命令还是会继续加入到旧的AOF文件中,当新的AOF文件完成后会对旧的AOF文件进行替换,并且将重写过程中新加入到旧的AOF中 的命令追加到新的AOF文件中
    • AOF文件中保存的命令都是有序的,而且都是以Redis协议的格式进行保存的,所以容易读懂
  • AOF的修复
    • 如果Redis此时正在对AOF文件进行写入操作,此时如果Redis突然宕机的话,AOF就会受损,Redis不会加载破损的AOF文件
    • 修复步骤 :
      • 将当前的AOF进行备份
      • 使用Redis自带的redis-check-aof 程序,对原来的 AOF 文件进行修复.redis-check-aof --fix
      • 重启Redis服务器,让其重新加载AOF文件

3.RDB及AOF的选择

  • 如果对数据的安全性要求非常高的话,那么最好俩个一起开启,如果能允许分钟内的数据丢失的话,就选择RDB
  • 如果数据需要时常备份的话,最好开启RDB
  • 俩中可以同时开启,也可以只使用一个,但是如果都开启的话,Redis重启时只会加载AOF文件来恢复数据

三 、Redis会遇到的问题以及解决方案

1.缓存雪崩

  • 发生场景 : 当Redis服务器重启或者大量缓存在同一时期失效时,此时大量的流量会全部冲击到数据库上面,数据库有可能会因为承受不住而宕机
  • 解决方案 :
    • 均匀分布 : 我们应该在设置失效时间时应该尽量均匀的分布,比如失效时间是当前时间加上一个时间段的随机值
    • 熔断机制 : 类似于SpringCloud的熔断器,我们可以设定阈值或监控服务,如果达到熔断阈值(QPS,服务无法响应,服务超时)时,则直接返回,不再调用目标服务,并且还需要一个检测机制,如果目标服务已经可以正常使用,则重置阈值,恢复使用
    • 隔离机制 : 类似于Docker一样,当一个服务器上某一个tomcat出了问题后不会影响到其它的tomcat,这里我们可以使用线程池来达到隔离的目的,当线程池执行拒绝策略后则直接返回,不再向线程池中增加任务
    • 限流机制 : 其实限流就是熔断机制的一个版本,设置阈值(QPS),达到阈值之后直接返回
    • 双缓存机制 : 将数据存储到缓存中时存储俩份,一份的有效期是正常的,一份的有效期长一点.不建议用这个方案,因为比较消耗内存资源,毕竟Redis是直接存储到内存中的

2.缓冲穿透

  • 发生场景 : 此时要查询的数据不存在,缓存无法命中所以需要查询完数据库,但是数据是不存在的,此时数据库肯定会返回空,也就无法将该数据写入到缓存中,那么每次对该数据的查询都会去查询一次数据库
  • 解决方案 :
    • 布隆过滤 : 我们可以预先将数据库里面所有的key全部存到一个大的map里面,然后在过滤器中过滤掉那些不存在的key.但是需要考虑数据库的key是会更新的,此时需要考虑数据库 --> map的更新频率问题
    • 布隆过滤器(英语:Bloom Filter)是1970年由布提出的。 它实际上是一个很长的二进制向量和一系列随机映射函数。 布隆过滤器可以用于检索一个元素是否在一个集合中。
    • 缓存空值 : 哪怕这条数据不存在但是我们任然将其存储到缓存中去,设置一个较短的过期时间即可,并且可以做日志记录,寻找问题原因

3.缓存预热

  • 其实这个不是一个问题,是一种机制,在上线前先将需要缓存的数据放到缓存中去,这个的实现很简单,可以在启动的时候放(数据比较小),做一个开关(一个隐秘的接口),定时刷新缓存

4.缓存更新

  • 这也不是一个问题,是一种机制,怎么样保证缓存中的key是实时有效的,以及及时的更新数据资源
    • 监测机制 : 定时去监测Redis,查看过期的缓存,
      问题 :
      在看到这里的时候我有一个问题,如果key过期了那么我要不要再将key重新放入缓存呢,如果放入的话我设置这个有效期就完全没有必要了,完全可以设置为永久有效
      我想了一个解决方案,我们可以对命中率做一个记录,如果这个key在最近一段时间内被频繁命中的话,我们就在失效时进行更新,否则就直接清除掉
    • 被动更新 : 每次请求过来时我们判断一下当前key是否失效,失效就重新查询存放到缓存中,这个问题不会涉及到监测机制那个问题

5.服务降级

  • 服务降级是不得已而为之的,在关键的时候丢卒保帅,保证核心功能正常运行
    • 服务拒绝 : 直接拒绝掉非核心功能的所有请求,其实基本就是直接废弃掉某些模块
    • 服务延迟 : 将请求加入到线程池中或队列中,延迟执行这些请求
  • 注意 : 服务降级一定要有对应的恢复策略,不能降下去就不回来了,我们可以监测服务的状态,当状态适当时恢复服务的正常使用

猜你喜欢

转载自blog.csdn.net/striner/article/details/88381884