Redis面试题总结

Redis 为什么这么快呢?

众所周知,redis 的速度非常的快,单机的 redis 就可以支撑每秒 10 几万的并发,相对于 mysql 来说,性能是 mysql 的几十倍。速度快的原因主要有几点:

  1. 完全基于内存操作
  2. C 语言实现,优化过的数据结构,基于几种基础的数据结构,redis 做了大量的优化,性能极高
  3. 使用单线程,无上下文的切换成本
  4. 基于非阻塞的 IO 多路复用机制

你给我说一下Redis都有哪些数据结构?

Redis主要有5种数据类型,包括StringListSetZsetHash,满足大部分的使用要求。当然还有一些其他的数据结构,比如说HyperLogLogGeoPub/Sub。当然还有Redis Module,像BloomFilterRedisSearchRedis-ML

Redis持久化了解吗?把你知道的说一下

Redis 持久化方案分为 RDBAOF 两种。

  1. RDB 持久化可以手动执行也可以根据配置定期执行,它的作用是将某个时间点上的数据库状态保存到 RDB 文件中,RDB 文件是一个压缩的二进制文件,通过它可以还原某个时刻数据库的状态。由于 RDB 文件是保存在硬盘上的,所以即使 redis 崩溃或者退出,只要 RDB 文件存在,就可以用它来恢复还原数 据库的状态。
    可以通过 SAVE 或者 BGSAVE 来生成 RDB 文件。
    SAVE 命令会阻塞 redis 进程,直到 RDB 文件生成完毕,在进程阻塞期间,redis 不能处理任何命令请 求,这显然是不合适的。
    BGSAVE 则是会 fork 出一个子进程,然后由子进程去负责生成 RDB 文件,父进程还可以继续处理命令请 求,不会阻塞进程。

  2. AOF 和 RDB 不同,AOF 是通过保存 redis 服务器所执行的写命令来记录数据库状态的。 先执行命令后记录命令。AOF 通过追加、写入、同步三个步骤来实现持久化机制。
    当 AOF 持久化处于激活状态,服务器执行完写命令之后,写命令将会被追加 append 到 aof_buf 缓 冲区的末尾
    在服务器每结束一个事件循环之前,将会调用 flushAppendOnlyFile 函数决定是否要将 aof_buf 的 内容保存到 AOF 文件中,可以通过配置 appendfsync 来决定。
    AOF 配置项 appendfsync 的三个可选值:
    Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;数据基本不丢失,性能较差。
    Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;宕机时丢失 1s 内的数据,性能较好。
    No,操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘;宕机时丢失数据较多。

你知道Redis 如何实现事务吗?

事务的执行过程包含三个步骤,Redis 提供了 MULTI、EXEC 两个命令来完成这三个步骤。

  1. 第一步,客户端要使用一个命令显式地表示一个事务的开启。在 Redis 中,这个命令就是 MULTI
  2. 第二步,客户端把事务中本身要执行的具体操作(例如增删改数据)发送给服务器端。这些操作就是 Redis 本身提供的数据读写命令,例如 GET、SET 等。不过,这些命令虽然被客户端发送到了服务器端,但 Redis 实例只是把这些命令暂存到一个命令队列中,并不会立即执行。
  3. 第三步,客户端向服务器端发送提交事务的命令,让数据库实际执行第二步中发送的具体操作。Redis 提供的 EXEC 命令就是执行事务提交的。当服务器端收到 EXEC 命令后,才会实际执行命令队列中的所有命令。

你知道Redis的 watch 机制的作用吗?

一个事务的 EXEC 命令还没有执行时,事务的命令操作是暂存在命令队列中的。此时,如果有其它的并发操作,我们就需要看事务是否使用了 WATCH 机制。

WATCH 机制的作用是,在事务执行前,监控一个或多个键的值变化情况,当事务调用 EXEC 命令执行时,WATCH 机制会先检查监控的键是否被其它客户端修改了。如果修改了,就放弃事务执行,避免事务的隔离性被破坏。然后,客户端可以再次执行事务,此时,如果没有并发修改事务数据的操作了,事务就能正常执行,隔离性也得到了保证。

Redis过期的数据的删除策略了解吗?

相关问题:如果假设你设置了一批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进行删除的呢?

一般常用的过期数据删除策略有两个,分别是
惰性删除:只会在取出key的时候对数据进行过期检查
定时删除:定期取出部分数据,进行删除过期key操作(如果取出全部数据,那么数据量大的情况下会对CPU造成很大的压力)

两者各有优缺点,惰性删除不会对CPU造成很大的压力,但是可能会造成太多过期 key 没有被删除。定期删除对内存比较友好。 Redis 采用的是 定期删除+惰性删除 。
但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。 怎么解决这个问题呢?答案就是:Redis 内存淘汰机制。

Redis 内存淘汰机制了解么?

相关问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?

Redis原有6种内存淘汰机制,4.0之后又新增了两种(volatile-lfu、allkeys-lfu)
1、no-eviction: 不使用内存淘汰机制,如果内存满了,那么就返回错误信息
2、volatile-lru(least recently used): 从已经设置过期时间的数据集中,将最近最少使用的数据删除
3、volatile-random: 从已经设置过期时间的数据集中,随机删除一些数据
4、volatile-ttl: 从已经设置过期时间的数据集中,将快要过期的数据删除
5、volatile-lfu(least frequently used): 从已经设置过期时间的数据集中,将最近最不经常使用的数据删除。
6、allkeys-lru(least recently used): 从所有的数据集,将最近最少使用的数据删除
7、allkeys-random: 从所有的数据集,随机删除一些数据
8、allkeys-lfu(least frequently used):** 从所有的数据,将最近最不经常使用的数据删除。

LRU和LFU算法的区别:

LFU (Least Frequently Used) : 最近最不频繁使用,跟使用的次数有关,淘汰使用次数最少的。
LRU (Least Recently Used): 最近最少使用,跟使用的最后一次时间有关,淘汰最近使用时间离现在最久的。

LFU是在Redis4.0后出现的,它的核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来。LFU算法能更好的表示一个key被访问的热度。假如你使用的是LRU算法,一个key很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被访问到的则被淘汰了。如果使用LFU算法则不会出现这种情况,因为使用一次并不会使一个key成为热点数据。

说说 Redis 的缓存雪崩?

缓存雪崩是指我们缓存多条数据时,采用了相同的过期时间,比如 00:00:00 过期,如果这个时刻缓存同时失效,而有大量请求进来了,因未缓存数据,所以都去查询数据库了,数据库压力增大,最终导致系统崩溃。

那怎么解决呢?

  1. 在原有的实效时间基础上增加一个随机值,比如 1-5 分钟随机,降低缓存的过期时间的重复率,避免发生缓存集体实效。
  2. Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
  3. 限流,如果 redis 宕机,可以限流,避免同时刻大量请求打崩 DB。
  4. Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
  5. 热点key识别+本地缓存。
  6. singleflight降低缓存并发重复读。

说说 Redis 的缓存穿透?

缓存穿透指一个一定不存在的数据,由于缓存未命中这条数据,就会去查询数据库,数据库也没有这条数据,所以返回结果是 null。如果每次查询都走数据库,则缓存就失去了意义,就像穿透了缓存一样。

那怎么解决呢?

  1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
  2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
  3. 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力

说说Redis 的缓存击穿?

某个 key 设置了过期时间,但在正好失效的时候,有大量请求进来了,导致请求都到数据库查询了。就像把一面墙击穿了一个洞。注意:和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

那怎么解决呢?
不同场景下的解决方式可如下:

  1. 若缓存的数据是基本不会发生更新的,尝试将该热点数据设置为永不过期。
  2. 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于Redis、Zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
  3. 若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。

以上内容仅供参考,这些也是自己从一些优质文章中积累出来的。希望大家持续学习,不断进步!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值