【Redis】基础概念及常见面试题

发布时间:2024-09-27 13:01

掌握完这些应该可以说自己熟悉Redis了吧
图片资源https://www.processon.com/view/626910710e3e742d46183bbc

\"【Redis】基础概念及常见面试题_第1张图片\"

简单介绍一下Redis

简单来说Redis就是一个使用C语言开发的非关系型NoSQL的键值对数据库。与传统数据库不同的是,它的数据是存在内存中的,所以读写速度非常快,可以用作数据库、缓存、分布式锁和消息中间件。
Redis提供了多种数据类型来支持不同的业务场景。Redis还支持事务、持久化、Lua脚本、和多种集群方案。

哪些数据适合放入缓存?为什么要用缓存?

  1. 即时性和数据一致性要求不高的数据
  2. 访问量大且更新频率不高的数据
    【电商项目中, 将商品分类目录, 商品SPU信息都放到Redis中了】

因为Redis是存在内存中的,内存的读取速度远大于硬盘,我们可以将热点数据存放在Redis中,等再次查询直接在缓存中查询,性能更高,并发更高。

缓存数据的处理流程是怎么样的

  1. 如果用户请求的数据在缓存中就直接返回
  2. 如果缓存中不存在就看数据库是否存在
  3. 数据库存在的话就更新缓存数据,并返回数据
  4. 数据库不存在就返回空数据 (不存空值可能会有缓存穿透风险)

Redis单线程模型

Redis基于 Reactor 模式开发了网络事件处理器,这个处理器叫 文件事件处理器, 是单线程的, 采用IO多路复用机制同时监听多个socket, 根据socket上的事件来选择对应的事件处理器来处理这个事件。
Redis客户端对服务端的每次调用都经历了 发送命令、执行命令、返回结果三个过程。 由于Redis是单线程的,所以每一条到达服务器的命名不会被立刻执行,所有命令都会进入一个队列中,然后被逐个执行。且多个客户端执行顺序是不确定的,但是一定不会有两条命令同时执行。所以不会有并发问题。

为什么Redis能够快速运行

  1. 绝大部分请求是纯粹的内存操作(非常快速)
  2. 采用单线程执行命令,避免了上下文切换和资源竞争。
  3. 多路复用的高性能I/O模型。

为什么Redis是单线程执行命令?

因为Redis是基于内存的操作,CPU不受Redis的瓶颈, 内存和网络是它的瓶颈。 单线程容易实现,且避免了不必要的上下文切换,不用考虑各种锁的问题,不会出现死锁导致性能消耗。

Redis6.0之后Redis引入了多线程, 因为随着硬件性能提升,Redis 的性能瓶颈可能出现网络 IO 的读写,引入多线程只是为了在网络数据的读写上,执行命令仍然是单线程顺序执行。

【重点】Redis中常见数据结构与使用场景

  1. string: string数据结构是简单的key-value类型。 value不仅可以说string 也可以是数字。
    一般常用在需要计数的场景,比如用户的访问次数,热点文章的点赞、转发数量等
  2. list:list 是一个双向链表,可以按照插入的顺序排序, 使用list 列表可以轻松实现最新消息排行等功能, 还可以用于在消息队列
  3. hash:结构化信息可以存成hash, 比如把商品属性存成一个hash, 如果要修改某个属性,可以直接修改这个属性的值, 而不是把信息打包成hashMap然后序列化存入Redis,改动的话就得取出来,反序列化,修改,再存回去。
  4. set:就是一个集合,里面一堆不重复值得组合, 比如在微博应用中,可以将一个用户所有得关注人存在一个集合中,将其所有粉丝存在一个集合中。Redis的集合还提供了求交集、并集、差集等操作,这样就可以非常方便的实现共同关注等功能。
  5. sorted set:和set比 sorted set 增加了一个权重参数score, 使得集合中的元素能按score排序。 可以用在各种礼物排行榜,热点评论等

【重点】过期键的删除策略

Redis 通过⼀个叫做过期字典(可以看作是hash表)来保存数据过期的时间。过期字典的键指向Redis数据库中的某个key(键),过期字典的值是⼀个long long类型的整数,这个整数保存了key所指向的数据库键的过期时间。

常用的过期数据的删除策略有两个:

  1. 定时删除: 每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略对内存友好,但是会占用大量CPU资源,取处理过期数据,从而影响缓存的响应时间和吞吐量
  2. 惰性删除: 只会再取出key的时候,才对数据进行过期检查, 这样对CPU最友好,但是可能导致太多过期key 没有被删除。
  3. 定期删除:每隔一定的时间,抽取一批Key执行删除过期key的操作,并且Redis底层会通过限制删除操作执行的时长和频率来减少操作对CPU时间的影响。

Redis 中同时使用了惰性删除和定期过期两种删除策略。
但是还是有可能存在定期删除和惰性删除都漏掉了很多过期key的情况,导致大量过期key堆积在内存,然后就OOM了。
Redis采用内存淘汰机制解决这个问题

Redis内存淘汰机制

内存淘汰机制即内存溢出控制策略,是指在Redis的用于缓存的内存不足时,怎么处理需要重新写入且需要申请额外空间的数据。
内存达到maxmemory上限时,触发内存溢出控制策略

  1. noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
  2. volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,挑将要过期的移除
  3. allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
  4. allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
  5. allkeys-lfu: 最不经常使用的
  6. volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
  7. volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
  8. volatile-lfu: 最不经常使用的

相关问题: MySQL里有2000w数据, Redis中只存20w数据, 如何保证Redis中的数据都是热点数据?
我们可以使用Redis内存淘汰机制中的allkeys-lru淘汰策略, 该策略是冲Redis中挑选最近最少使用的数据进行删除,这样被频繁访问的数据就可以保留下来

Redis内存回收使用的LRU算法

【重点】Redis持久化机制(怎么保证Redis挂掉只会,再重启数据可以进行恢复)

很多时候需要持久化数据,Redis支持两种持久化操作:
快照(RDB)

  1. RDB是Redis默认的持久化方式,按照一定时间将内存数据以快照的形式保存到硬盘中, 对应产生的数据文件为dump.rdb, 恢复时只需将rdb文件重新读入内存即可。
  2. Redis会单独创建一个子进程来fork进行持久化,采用写时赋值技术, fork会先将数据写入到一个临时文件中,等持久化过程结束了,再用临时文件替换上次持久化好文件。整个过程主进程不进行任何IO操作,性能很高
  3. RDB的缺点是最后一次持久化的数据可能丢失。所以对需要大规模数据恢复,且对数据恢复完整性不是很敏感用RDB比AOF高效。

只追加文件(AOF)
以日志的形式记录每个写操作, 将Redis执行过的所有写指令记录下来,读操作不记录, 只许追加文件但不可改写文件。Redis启动之初会读取该文件重新构建数据。

优缺点:

  1. RDB可能造成数据的丢失,如果对数据敏感,需要选择AOF
  2. AOF占用磁盘空间更大,备份速度慢,如果采用always持久化策略,每次写操作都会同步,有性能压力
  3. 不建议单独使用AOF,可能会有bug。 如果只是做纯内存缓存,可以都不用。

Redis事务

Redis事务ACID只支持 I 隔离性、 D一致性。 Reids事务不支持回滚。 简单来说Redis事务就是一个将多种请求打包,然后按顺序执行的命令

缓存预热方案

缓存预热就是系统上线后,将相关缓存数据直接加载到缓存系统。这样用户请求的时候可以直接查询事先被预热的缓存数据。
解决方案

  1. 写个缓存刷新页面,上线时手动操作
  2. 数据量不大时,可以在项目启动时自动进行加载
  3. 用定时任务去刷新缓存

缓存穿透、击穿、雪崩

  1. 缓存穿透:高并发系统下, 大量请求查询不存在的值,缓存不会命中,请求直接落到数据库上。比如商品系统里查询不存在的商品。
    【解决方法:用户鉴权校验和参数校验,不合法是参数直接返回。 将不存在的数据也写入缓存,并设置的短暂的过期时间】
  2. 缓存击穿:某个热点数据被大量请求时,缓存突然过期,请求瞬间全部落到数据库上。比如秒杀进行过程中,秒杀商品缓存突然过期
    【加锁,大量并发只让一个线程去查,其他人等待,查到以后放入缓存,释放锁,其他人拿到锁先查缓存,如果有数据就不去数据库查】
  3. 缓存雪崩:在高并发下, 大量的缓存key在同一时间失效,导致大量请求落在数据库上。比如商品系统首页缓存突然过期。
    【给缓存原有的失效时间基础上增加一个随机值,比如1-5分钟的随机值,这样每个缓存的过期时间重复率就会降低,
    或者干脆不设置过期时间,更新内容的时候刷新缓存】

布隆过滤器

为了防止缓存穿透,布隆过滤器可以快速判断一个元素是否在一个集合中。
布隆过滤器原理:当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组(Bit array)中的 K 个点,把它们置为 1 。检索时,只要看看这些点是不是都是1就知道元素是否在集合中;如果这些点有任何一个 0,则被检元素一定不在;如果都是1,则被检元素很可能在(之所以说“可能”是误差的存在)。

【重点】如何保证缓存和数据库的一致性?

双写模式

数据库和缓存都修改,不推荐这种方式,因为万一是写多读少,会带来大量的无效写入,浪费性能,删除的话,最多多一次查数据库。
而且也会有数据不一致情况,比如两个线程,顺序是:线程1写入数据库,线程2写入数据库,线程2更新缓存,线程1更新缓存。这样就出现了数据不一致。

失效模式

修改数据库,删除缓存
先更新数据库,再删除缓存。
数据库更新失败,捕获异常不再继续删除缓存,不会出现数据不一致
缓存删除失败或还没删除导致数据库是新数据,缓存是旧数据,出现数据不一致。
这种情况如果对数据实时性要求不高,可以给缓存加上过期时间,实现最终一致性

先删除缓存,再更新数据库
删除缓存失败,可以捕获异常,直接返回结果,不再更新数据库,不会出现数据不一致
删除缓存成功,更新数据库失败或还没更新。可能会出现数据不一致。
比如一个线程删了缓存,数据更新还没成功,另一个线程取数据发现缓存没了,从数据库读取旧数据放入缓存,然后数据库更新了数据,导致数据库和缓存数据不一致
可以用延迟双删来解决这个问题

延迟双删
先删除缓存,再更新数据库,再延长3-5秒后删除缓存,实现最终一致性
【这个延迟时间是业务读数据的时间+几百毫秒】

订阅MySQL二进制日志

可以使用canal订阅binlog的方式,只要发现数据库有更新,canal就会自动更新缓存

Redis集群

【重点】Redis replication是什么?

Redistribution replication是一种主从模式复制机制, 这种机制使得从节点可以成为和主节点完全相同的副本
一个主节点用于写,多个从节点用于分摊读的压力,如果主节点挂了,可以提升一个从节点成为新的主节点,实现故障转移

【重点】如何保证主从数据一致性?

1.当从服务器连上主服务器只会,从服务器会向主服务器发送数据同步消息
2.主服务器接到从服务器发送过来的同步消息,把主服务器数据进行持久化,生成RDB文件,把RDB文件发给从服务器,从服务器拿到RDB文件进行读取
3.每次主服务器进行写操作只会,和从服务器进行数据同步

主从复制时,网络突然中断怎么办

从节点第一次连接主节点,主节点会生成RDB文件进行全量复制,同时将新写入的命令存储进缓冲区,发送给从节点,从而保证数据一致性。
网络中断,重新连接后,采用增量复制的方式,与全量复制不同的是,它是根据从节点的偏移量来进行数据同步的。主从节点分别会维护一个偏移量offset。刚开始主从节点写的位置和从节点读的位置在同一起点,随着主节点不断写入,偏移量会逐渐增大,同样的,从节点复制完数据后,偏移量也在不断增加。当网络断开,从节点不再同步,主节点还在不断写数据,偏移量就会大于从节点的偏移量,当连接恢复时,根据偏移量将未同步的进行同步

Redis哈希槽

Redis cluster没有用一致性hash, 而是引入了哈希槽的概念。Redis 集群有16384个哈希槽,
每个key通过CRC16循环校验算法与上16383来决定位置放哪个槽,集群的每个节点负责一部分hash槽

【重点】 如果设置Redis分布式锁

SET lock_key unique_value NX PX 10000
加锁过程就是给一个键设置值,setnx 在lock_key不存在时才能占到位
占锁的时候,值设置为UUID, 每个人只能删除自己上的锁
为了避免死锁,一定要设置锁自动过期。而且设置过期时间和占位必须是原子操作用,lua脚本确保原子操作

Redis思维导图:https://www.processon.com/view/link/6274dfa5e0b34d07585eb845

如果发现有错误的地方,欢迎大家提出批评指正

致力于分享记录各种知识干货,关注我,让我们一起进步,互相学习,不断创作更优秀的文章
希望大家能多多支持,你们的支持是我最大的动力!不要忘了三连哦 ⭐️

下篇:【MySQL数据库】2022年MySQL必知必会,基础内容与常见面试题
后期预告:Spring全家桶

\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号