Redis经典面试题总结

Redis经典面试题总结,第1张

1. 什么是Redis?它主要用来什么的?

Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

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

2.Redis的基本数据结构类型

Redis有以下这五种基本类型:

  • String(字符串)
  • Hash(哈希)
  • List(列表)
  • Set(集合)
  • zset(有序集合)
3. Redis为什么这么快?

3.1 基于内存存储实现
Redis基于内存存储实现的数据库,相对于数据存在磁盘的MySQL数据库,省去磁盘I/O的消耗。
3.2 合理的数据编码
譬如:String类型如果存储数字的话,是用int类型的编码
3.3 合理的线程模型

  • I/O 多路复用: IO多路复用其实就是一种同步IO模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写 *** 作;而没有文件句柄就绪时,就会阻塞应用程序,交出cpu。
  • 单线程模型: Redis是单线程模型的,而单线程避免了CPU不必要的上下文切换和竞争锁的消耗。也正因为是单线程,如果某个命令执行过长(如hgetall命令),会造成阻塞。Redis是面向快速执行场景的数据库。所以要慎用如smembers和lrange、hgetall等命令。
4. 什么是缓存击穿、缓存穿透、缓存雪崩?

4.1 缓存击穿问题
缓存击穿: 指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到db。缓存击穿和缓存雪崩区别在于击穿针对某一热点key缓存,雪崩则是很多key。

如何避免缓存击穿问题,解决方案就有两种:

  • 使用互斥锁方案。缓存失效时,不是立即去加载db数据,而是先使用某些带成功返回的原子 *** 作命令,如(edis的setnx()方法或者redisTemplate的setIfAbsent()方法)去 *** 作,成功的时候,再去加载db数据库数据和设置缓存。否则就去重试获取缓存。
  • “永不过期”,是指没有设置过期时间,但是热点数据快要过期时,异步线程去更新和设置过期时间。

4.2 缓存穿透问题
缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。

如何避免缓存穿透呢? 一般有三种方法:

  • 如果是非法请求,我们在API入口,对参数进行校验,过滤非法值。
  • 如果查询数据库为空,我们可以给缓存设置个空值,或者默认值。
  • 使用布隆过滤器快速判断数据是否存在。即一个查询请求过来时,先通过布隆过滤器判断值是否存在,存在才继续往下查。

4.2 缓存雪奔问题
缓存雪奔: 指缓存中数据大批量到过期时间,而查询数据量巨大,请求都直接访问数据库,引起数据库压力过大甚至down机。
如何避免缓存雪崩问题:

  • 缓存雪奔一般是由于大量数据同时过期造成的。
  • Redis 故障宕机也可能引起缓存雪奔,这就需要构造Redis高可用集群。
5. Redis 过期策略和内存淘汰策略

5.1 Redis的过期策略

  1. 定时过期 每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即对key进行清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
  2. 惰性过期 只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
  3. 定期过期 每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

Redis中同时使用了惰性过期和定期过期两种过期策略。

5.2 Redis 内存淘汰策略(逐出算法)
Redis使用内存存储数据,在执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。
5.2.1 常用的逐出算法:

5.2.2 影响数据逐出的相关配置

  • maxmemory (最大可使用内存): 占用物理内存的比例,默认值为0,表示不限制。生产环境中根据需求设定,通常设置在50%以上。
  • maxmemory-samples ( 每次选取待删除数据的个数) : 选取数据时并不会全库扫描,导致严重的性能消耗,降低读写性能。因此采用随机获取数据的方式作为待检测删除数据。
  • maxmemory-policy ( 删除策略) : 达到最大内存后的,对被挑选出来的数据进行删除的策略。
6. Redis 的持久化机制有哪些?

Redis提供了RDB(Redis DataBase)和AOF(append only file)两种持久化机制。

6.1 RDB
RDB持久化,是指在指定的时间间隔内,执行指定次数的写 *** 作,将内存中的数据集快照写入磁盘中,它是Redis默认的持久化方式。执行完 *** 作后,在指定目录下会生成一个dump.rdb文件,Redis 重启的时候,通过加载dump.rdb文件来恢复数据。RDB触发机制主要有以下几种:

RDB 的优点:
RDB是一个紧凑压缩的二进制文件,存储效率较高
适合大规模的数据恢复场景,如备份,全量复制等
RDB恢复数据的速度要比AOF快很多

RDB缺点:
没办法做到实时持久化/秒级持久化。
新老版本存在RDB格式兼容问题

6.2 AOF
AOF持久化,采用日志的形式来记录每个写 *** 作并追加到文件中,重启时再重新执行AOF文件中的命令来恢复数据。它主要解决数据持久化的实时性问题,默认是不开启的。

AOF的优点:
数据的一致性和完整性更高

AOF的缺点:
AOF记录的内容越多,文件越大,数据恢复变慢。

7.怎么实现Redis的高可用?

Redis 实现高可用有三种部署模式:主从模式,哨兵模式,集群模式。
7.1 主从模式
主从模式中,Redis部署了多台机器,有主节点,负责读写 *** 作,有从节点,只负责读 *** 作。从节点的数据来自主节点,实现原理就是主从复制机制

主从复制包括全量复制,增量复制两种。一般当slave第一次启动连接master,或者认为是第一次连接,就采用全量复制,全量复制流程如下:

redis2.8版本之后,已经使用psync来替代sync,因为sync命令非常消耗系统资源,psync的效率更高。

slave与master全量同步之后,master上的数据,如果再次发生更新,就会触发增量复制。
当master节点发生数据增减时,就会触发replicationFeedSalves()函数,接下来在 Master节点上调用的每一个命令会使用replicationFeedSlaves()来同步到Slave节点。执行此函数之前呢,master节点会判断用户执行的命令是否有数据更新,如果有数据更新的话,并且slave节点不为空,就会执行此函数。这个函数作用就是:把用户执行的命令发送到所有的slave节点,让slave节点执行。流程如下:

7.2 哨兵模式
哨兵模式,由一个或多个Sentinel实例组成的Sentinel系统,它可以监视所有的Redis主节点和从节点,并在被监视的主节点进入下线状态时,自动将下线主服务器属下的某个从节点升级为新的主节点。但是呢,一个哨兵进程对Redis节点进行监控,就可能会出现问题(单点问题),因此,可以使用多个哨兵来进行监控Redis节点,并且各个哨兵之间还会进行监控。

简单来说,哨兵模式就三个作用:

  • 发送命令,等待Redis服务器(包括主服务器和从服务器)返回监控其运行状态;
  • 哨兵监测到主节点宕机,会自动将从节点切换成主节点,然后通过发布订阅模式通知其他的从节点,修改配置文件,让它们切换主机;
  • 哨兵之间还会相互监控,从而达到高可用。
8. 使用过Redis分布式锁嘛?有哪些注意点呢?

lua脚本如下:

if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end;

一般情况下,已经可以使用这种实现方式。但是存在锁过期释放了,业务还没执行完的问题(实际上,估算个业务处理的时间,一般没啥问题了)。

错误实现:

  1. 命令setnx + expire分开写:
    执行完setnx加锁,正要执行expire设置过期时间时,进程crash掉或者要重启维护了,那这个锁就“长生不老”了,别的线程永远获取不到锁啦,所以分布式锁不能这么实现。
  2. set ex px nx + 校验唯一随机值,再删除:
    判断当前线程加的锁和释放锁是不是一个原子 *** 作。如果调用jedis.del()释放锁的时候,可能这把锁已经不属于当前客户端,会解除他人加的锁。
9. 使用过Redisson嘛?说说它的原理

分布式锁可能存在锁过期释放,业务没执行完的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。

当前开源框架Redisson就解决了这个分布式锁问题。我们一起来看下Redisson底层原理是怎样的吧:

只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了锁过期释放,业务没执行完问题。

10. MySQL与Redis 如何保证双写一致性

外部链接: redis缓存为什么要延时双删

10.1 缓存为啥是删除,⽽不是更新?
如果是更新,存在分布式事务问题,可能出现修改了缓存,数据库修改失败的情况。只是删除缓存的话,就算数据库修改失败,下次查询会直接取数据库的数据,也不会出现脏数据。
10.2 延时双删是什么?
就是在增删改某实体类的时候,要对该实体类的缓存进行清空,清空的位置在数据库 *** 作方法的前后。

流程:

  • 先删除缓存
  • 再更新数据库
  • 休眠一会(比如1秒),再次删除缓存。

这个休眠时间 = 读业务逻辑数据的耗时 + 几百毫秒。为了确保读请求结束,写请求可以删除读请求可能带来的缓存脏数据。这种方案只有休眠那一会(比如就那1秒),可能有脏数据,一般业务也会接受的。

11. 聊聊Redis 事务机制

Redis通过MULTI、EXEC、WATCH等一组命令集合,来实现事务机制。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

简言之,Redis事务就是顺序性、一次性、排他性的执行一个队列中的一系列命令。

Redis执行事务的流程如下:
开始事务(MULTI)
命令入队
执行事务(EXEC)、撤销事务(DISCARD )

布隆过滤器

布隆过滤器是一种占用空间很小的数据结构,它由一个很长的二进制向量和一组Hash映射函数组成,它用于检索一个元素是否在一个集合中,空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

布隆过滤器原理是?假设我们有个集合A,A中有n个元素。利用k个哈希散列函数,将A中的每个元素映射到一个长度为a位的数组B中的不同位置上,这些位置上的二进制数均设置为1。如果待检查的元素,经过这k个哈希散列函数的映射后,发现其k个位置上的二进制数全部为1,这个元素很可能属于集合A,反之,一定不属于集合A。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/905244.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-15
下一篇 2022-05-15

发表评论

登录后才能评论

评论列表(0条)

保存