Redis重点知识详解

在这里插入图片描述在这里插入图片描述
因为热爱所以坚持,因为热爱所以等待。熬过漫长无戏可演的日子,终于换来了人生的春天,共勉!!!

1.什么是Redis?

  • Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库

  • 五种基本类型 : String(字符串)、List(列表)、Set(集合)、Hash(散列表)、Sorted Set(有序集合)。

  • 三种特殊类型 : geospatial(地理位置)、Hyperloglog(基数统计)、Bitmaps(位地图)

  • 与传统数据库不同的是 Redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。另外,Redis 也经常用来做分布式锁。除此之外,Redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。

2.Redis为什么这么快?

1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1)

2、数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;

3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

4、使用多路 I/O 复用模型,非阻塞 IO

5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

3.五大数据类型基本

Redis主要有5种数据类型,包括String,List,Set,Zset,Hash,满足大部分的使用要求

数据类型可以存储的值操作应用场景
STRING字符串、整数或者浮点数对整个字符串或者字符串的其中一部分执行操作 对整数和浮点数执行自增或者自减操作做简单的键值对缓存
LIST列表两端压入或者弹出元素, 根据下标取元素,特定元素前后插入元素, 截取范围内的元素, 修剪列表存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的数据
SET无序集合添加元素、查看所有值、随机移除单个元素, 判断元素是否在集合中, 从集合里面随机获取元素, 计算交集、并集、差集,交集、并集、差集的操作,比如交集,可以把两个人的粉丝列表整一个交集
HASH包含键值对的无序散列表添加、获取、移除单个键值对, 获取所有键值对,检查某个键是否存在结构化的数据,比如一个对象
ZSET有序集合添加、获取、删除元素, 根据分值范围或者成员来获取元素,计算一个键的排名去重但可以排序,如获取排名前几名的用户

4. 持久化

持久化 : 就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制

- 1.RDB:是Redis DataBase缩写快照

RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期

在这里插入图片描述

RDB过程解析

Redis会单独创建(fork) 一个子进程来进行持久化,先将数据写入一个临时文件中,到持久化过程都结束了再用这个临时文件替换上次持久化好的文件, 整个过程中主进程是不进行任何IO操作的, 这样确保的及极高的性能. 如果需要进行大规模的恢复, 且对于数据恢复的完整性不是非常敏感, 那RDB方式要比AOF方式更加高效. RDB的缺点是最后一次持久化后的数据可能丢失
在这里插入图片描述

RDB触发机制

1.手动触发:

  • save命令 : 使用Redis处于阻塞状态, 直到RDB持久化完成, 才会响应其他客户端发来的命令, 在生产环境一定要慎用
  • bgsave命令 : fork出一个子进程执行持久化, 主进程只在fork过程中有短暂的阻塞, 子进程创建后, 主进程就可以响应客户端请求了

2.自动触发:

  • save m n : 在m秒内, 如果有n个键发生改变, 则自动触发持久化, 通过bgsave执行, 如果设置多个, 只要满足其一就会触发
  • flushall : 用于清空redis所有的数据库, 会清空RDB文件, 同时也会生成dumo.rdb, 内容为空
  • 主从同步 : 全量同步时会自动触发bgsave命令, 生成rdb发送给从命令

优点:

1.适合大规模数据恢复!
2.只有一个文件 dump.rdb,方便持久化
3.容灾性好, 方便备份
4.性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能

缺点:

1.数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候
2.fork进程的时候,会占用一定的内存空间!!

- 2.AOF(Append-only file)

AOF: Append Only File以日志的形式记录服务器所处理的每一个写,删除操作查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录,调操作系统命令进程刷盘

1、所有的写命令会追加到AOF缓冲中。

2、AOF 缓冲区根据对应的策略向硬盘进行同步操作。

3、随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。

4、当Redis重启时,可以加载AOF文件进行数据恢复。

同步策略:

●每秒同步:异步完成,效率非常高,一旦系统出现宕机现象,那么这一 秒钟之内修改的数据将会丢失
●每修改同步:同步持久化,每次发生的数据变化都会被立即记录到磁盘中,最多丢一条
●不同步:由操作系统控制,可能丢失较多数据

在这里插入图片描述
AOF执行流程
在这里插入图片描述
优点:
1、数据安全
2、过append模式写文件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过redis-check-aof工具解决数据一致性问题。
3、AOF机制的rewrite模式。定期对AOF文件进行重写,以达到压缩的目的

缺点:
1、AOF文件比RDB文件大,且恢复速度慢。
2、数据集大的时候,比rdb启动效率低。
3、运行效率没有RDB高

RDB与AOF
  • AOF文件比RDB更新频率高,优先使用AOF还原数据
  • AOF比RDB更安全也更大
  • RDB性能比AOF好
  • 如果两个都配了优先加载AOF

-3.如何选择合适的持久化方式

  • 一般来说, 如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。

  • 如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。

  • 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。

  • 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

5.主从同步机制(更新版本2.8之后)

复制过程

客户端向服务器端发送:SLAVEOF

1、设置主服务器的地址和端口

存到masterhost和mastterport两个属性里之后,向客户端发送ok,然后开始复制工作。

2、建立套接字链接

从服务器根据命令设置的地址和端口,创建链接,并且为这个套接字创建一个专门处理复制工作的文件事件处理器。

主服务器也会为套接字创建相应的客户端状态,并且把从服务器当作一个客户端来对待。

3、发送ping命令(检查)

检查套接字状态是否正常

检查主服务器是否能正确处理请求。(如果不能,就重连)

4、身份认证

5、发送端口信息

从服务器向主服务器发送信息,主服务器记录。

6、同步

从服务器向主服务器发送psync命令。(主服务器也成为从服务器的客户端,因为主服务器会发送写命令给从服务器)

7、命令传播

完成同步后,进入传播阶段,主服务器一直发送写命令,从服务器一直接受,保证和主服务器一致。

心跳检测

默认一秒一次,从服务器向主服务器发送命令:REPLCONF ACK

三个作用:

  1. 检测网络连接状态:如果主服务器一秒没收到命令,就说明出问题了

  2. 辅助实现min-slaves配置:min-slaves-to-write 3 min-slaves-max-log 10:当从服务器小于3个或延迟都大于10,主服务器拒绝写命令。

  3. 检测命令丢失:如果命令丢失,主服务器会发现偏移量不一样,然后它就会根据偏移量,去积压缓冲区找到缺少的数据并发给从服务器。

三个参数:

1.runld: 每个redis节点启动都会生成长度为40的唯一字符串来标识当前运行的redis节点,查看此id可通过命令info server查看

2.offset(复制偏移量):

  • 主库和从库分别各自维护一个复制偏移量(可以使用info replication查看),用于标识自己复制的情况:
    在主库中代表主节点向从节点传递的字节数,在从库中代表从库同步的字节数。

  • 每当主库向从节点发送N个字节数据时,主节点的offset增加N

  • 从库每收到主节点传来的N个字节数据时,从库的offset增加N。

  • 因此offset总是不断增大,这也是判断主从数据是否同步的标志,若主从的offset相同则表示数据同步量,不通则表示数据不同步。

3.replication backlog buffer(复制积压缓冲区):
复制积压缓冲区是一个固定长度的FIFO队列,大小由配置参数repl-backlog-size指定,默认大小1MB。

注意: 该缓冲区由master维护并且有且只有一个,所有slave共享此缓冲区,其作用在于备份最近主库发送给从库的数据。

在主从命令传播阶段,主节点除了将写命令发送给从节点外,还会发送一份到复制积压缓冲区,作为写命令的备份。

除了存储最近的写命令,复制积压缓冲区中还存储了每个字节相应的复制偏移量,由于复制积压缓冲区固定大小先进先出的队列,所以它总是保存的是最近redis执行的命令

在这里插入图片描述

所以,重连服务器后,从服务器会发送自己的复制偏移量offset给主服务器,

如果offset偏移量之后的数据仍然存在于复制挤压缓冲区,就执行部分重同步操作; 相反,执行完整重同步操作。

psync流程:

在这里插入图片描述

全量复制:

  • 从节点发送psync命令, psync runid offset (由于是第一次, runid为? ,offset为-1)
  • 主节点返回FULLRESYNC runld offset, runld是 主节点的runld, offset是主节点目前的offset。从节点保存信息
  • 主节点启动bgsave命令fork子 进程进行RDB持久化
  • 主节点将RDB文件发送给从节点,到从节点加载数据完成之前,写命令写入缓冲区
  • 从节点清理本地数据并加载RDB,如果开启了AOF会重写AOF

部分复制:
1.复制偏移量: psync runid offset

2.复制积压缓冲区:当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。

  • 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部
    分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况);
  • 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的
    主节点,只能进行全量复制。

6. Key的过期淘汰机制

Redis可以对存储在Redis中的缓存数据设置过期时间,比如我们获取的短信验证码一般十分钟过期,我们这时候就需要在验证码存进Redis时添加一个key的过期时间,但是这里有一个需要格外注意的问题就是:并非key过期时间到了就一定会被Redis给删除。

1.定期删除

Redis默认是每隔100ms就随机抽取一些设置了过期时间的Key,检查其是否过期,如果过期就删除。为什么是随机抽取而不是检查所有key?因为你如果设置的key成千上万,每100毫秒都将所有存在的key检查一遍,会给CPU带来比较大的压力。

2.惰性删除

定期删除由于是随机抽取可能会导致很多过期Key到了过期时间并没有被删除。所以用户在从缓存获取数据的时候,redis会检查这个key是否过期了,如果过期就删除这个key。这时候就会在查询的时候将过期key从缓存中清

3.内存淘汰机制

仅仅使用定期删除+惰性删除机制还是会留下一个严重的隐患: 如果定期删除留下了很多已经过期的key,而且用户长时间都没有使用过这些过期key,导致过期key无法被惰性删除,从而导致过期key一直堆积在内存里,最终造成Redis内存块被消耗殆尽。那这个问题如何解决呢?这个时候Redis内存淘汰机制应运而生了。Redis内存淘汰机制提供了6种数据淘汰策略:

●volatile-lru: 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
●volatile-ttl: 从已设置过期时间的数据集中挑选将要过期的数据淘汰。
●volatile-random: 从已设置过期时间的数据集中任意选择数据淘汰。
●allkeys-lru: 当内存不足以容纳新写入数据时移除最近最少使用的key。
●allkeys-random: 从数据集中任意选择数据淘汰。
●no-enviction (默认) : 当内存不足以容纳新写入数据时,新写入操作会报错。
一般情况下,推荐使用volatile-lru策略, 对于配置信息等重要数据,不应该设置过期时间,这样Redis就永远还会淘汰这些重要数据。对于一般数据可以添加一个缓存时间,当数据失效则请求会从DB中获取并重新存入Redis中。

7.缓存击穿

首先我们来看下请求是如何取到数据的:当接收到用户请求,首先先尝试从Redis缓存中获取到数据, 如果缓存中能取到数据则直接返回结果,当缓存中不存在数据时从DB获取数据,如果数据库成功取到数据,则更新Redis,
然后返回数据
在这里插入图片描述

定义: 高并发的情况下,某个热门key突然过期,导致大量请求在Redis未找到缓存数据,进而全部去访问DB请求数据,引起DB压力瞬间增大。

解决方案: 缓存击穿的情况下一般不容易造成DB的宕机,只是会造成对DB的周期性压力。对缓存击穿的解决方案一般可以这样:

  • Redis中的数据不设置过期时间,然后在缓存的对象上添加一个属性标识过期时间,每次获取到数据时,校验对象中的过期时间属性,如果数据即将过期,则异步发起一个线程主动更新缓存中的数据。但是这种方案可能会导致有些请求会拿到过期的值,就得看业务能否可以接受,

  • 如果要求数据必须是新数据,则最好的方案则为热点数据设置为永不过期,然后加一个互斥锁保证缓存的单线程写
    互斥锁大致代码:
    在这里插入图片描述

8.缓存穿透

定义: 缓存穿透是指查询缓存和DB中都不存在的数据。比如通过id查询商品信息,id一般大于0,攻击者会故意传id为-1去查询,由于缓存是不命中则从DB中获取数据,这将会导致每次缓存都不命中数据导致每个请求都访问DB,造成缓存穿透。

解决方案:

  • 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试

  • 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。

  • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一 系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。

  • 如果从数据库查询的对象为空,也放入缓存,只是设定的缓存过期时间较短,比如设置为60秒。

9.缓存雪崩

定义: 缓存中如果大量缓存在一段时间内集中过期了,这时候会发生大量的缓存击穿现象,所有的请求都落在了DB上,由于查询数据量巨大,引起DB压力过大甚至导致DB宕机。

解决方案:

  • 给缓存的失效时间,加上一个随机值,避免集体失效。如果Redis是集群部署, 将热点数据均匀分布在不同的Redis库中也能避免全部失效的问题 (推荐)

  • 使用互斥锁,但是该方案吞吐量明显下降了。

  • 设置热点数据永远不过期。

  • 双缓存。我们有两个缓存,缓存A和缓存B。设缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。(推荐)
    然后细分以下几个小点:

    1. 从缓存A读数据库,有则直接返回
    2. A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。
    3. 更新线程同时更新缓存A和缓存B。
      在这里插入图片描述

在这里插入图片描述在这里插入图片描述

参考资料:
1.兔老大RabbitMQ – 这是全网最硬核redis总结,谁赞成,谁反对?
2.ThinkWon --Redis面试题(2020最新版)
3.敖 丙 --面了BAT,我总结了他们会问的Redis基础知识
4.缓存穿透、缓存击穿、缓存雪崩区别和解决方案
5.乐字节-Redis入门到精通,深入剖析Redis底层原理,程序员学习提升必看的Redis教程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Free的午后

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值