Redis八股文面试题

170 阅读10分钟

Redis

Redis(Remote Dictionary Server)是一个开源的基于内存的高性能键值存储数据库

image-20240403161928352-1712132977986-1.png

redis应用场景

image-20240403154321172.png

Redis 是一种开源的内存数据库,它支持多种数据结构(如字符串、列表、哈希、集合、有序集合等),并提供了丰富的功能和特性。由于其高性能、高可用性和丰富的功能,Redis 在各种应用场景中得到了广泛的应用,以下是一些常见的 Redis 应用场景:

任何需要短期存储,快速访问的地方都可以使用redis

  1. 缓存

    • Redis 最常见的用途就是作为缓存服务器,用于缓存热门数据、数据库查询结果、计算结果等,从而加速应用的访问速度和响应速度。Redis 的高性能和内置的数据结构使得它非常适合作为缓存服务器使用。
  2. 存储token

    • Redis 可以用作会话存储,用于存储用户会话信息、登录状态、用户登录凭证等。由于 Redis 的快速读写能力和持久化特性,它可以有效地管理会话数据,并且支持集群和主从复制,保证了高可用性和数据安全性。
  3. 分布式锁

    • Redis 的原子操作和分布式特性可以用于实现分布式锁,保证多个客户端之间的数据操作的原子性和互斥性。通过 Redis 的 SETNX、EXPIRE 等命令,可以实现简单有效的分布式锁。
  4. 幂等性:同一个人在短时间多次请求一个接口,去redis查找这个key,如果存在,说明5秒内请求过,返回错误信息“请勿重复提交”;如果redis不存在这个key,说明可以放行,放行之前把这个key写到redis,过期时间5秒,再放行执行业务方法。

使用缓存带来的问题

数据一致性/双写一致性/Redis作为缓存,MySQL的数据如何与redis进行同步呢?

当底层数据源发生变化时(如更新、删除操作),缓存中的数据可能没有及时更新,导致缓存数据与底层数据不一致。解决这个问题通常需要采用缓存失效、更新策略、缓存预热等技术来保证数据一致性。

解决数据一致性问题:异步方案

  1. 保证强一致性,但性能可能不高:利用读写锁

image-20240403163242761.png

image-20240403163346276.png

  1. 利用异步通知保证数据最终一致性

    使用MQ中间件,更新数据之后,通知缓存删除。

image-20240403163532797.png

image-20240403164111270.png

缓存击穿

  • 缓存击穿是指某个热点数据设置了过期时间,当key过期时,正好大量的请求直接击穿缓存,直接访问底层数据源,导致底层系统负载急剧增加。为了解决缓存击穿问题,可以设置热点数据永不过期;采用加互斥锁方法来保护热点数据。

image-20240403160058924.png

缓存雪崩:

  • 缓存雪崩是指在同时段大量的缓存key同时失效或Redis服务器宕机,大量请求直接访问底层数据源,导致底层系统负载激增,甚至宕机。

    过期时间添加随机值

    为了避免缓存雪崩问题,可以在设置缓存数据的过期时间时引入随机因素,使得缓存数据的过期时间具有一定的波动性,从而分散了缓存失效的时间。这样可以有效地减少大量缓存数据在同一时间失效的情况,降低了缓存雪崩的发生概率。

    利用Redis集群提高服务的可用性

    哨兵模式、集群模式

缓存穿透:

  • 缓存穿透是指恶意的查询请求,查询的数据在缓存和数据库中都不存在,导致大量的请求直接访问底层数据源,从而增加了底层系统的负载。为了解决缓存穿透问题,可以采用布隆过滤器、空值缓存、缓存数据预热等方法来过滤无效请求。

    空值缓存

    空值缓存是一种缓存机制,用于缓存查询结果为空(即查询的数据不存在)的情况。在实际应用中,有时候查询的数据可能不存在,但是频繁地查询这些不存在的数据会给系统带来额外的负担,因此可以使用空值缓存来缓解这种情况。

    空值缓存的基本思想是,当系统查询某个键对应的值为空时(即查询结果为null或者空集合等),将这个空值结果也缓存起来,设置一个较短的过期时间。这样,在下一次查询相同的键时,如果查询结果为空,可以直接从空值缓存中获取结果,而不需要再次查询底层数据源,从而节省了查询时间和系统资源。

    布隆过滤器

image-20240403154848645.png

布隆过滤器(Bloom Filter)用于在请求redis之前,判断一个元素是否存在于一个集合中。它通过哈希函数将元素映射到一个位数组中,并在判断元素是否存在时,检查位数组中相应的位置是否都为1。布隆过滤器具有一定的误判率,即在判断元素存在时可能会出现误判,但是不存在误判的情况。

布隆过滤器的主要特点包括:

0.  **空间效率高**:布隆过滤器只需要占用较少的内存空间,且空间占用与存储元素数量无关,只与预期的误判率和位数组的长度有关。
0.  **时间复杂度低**:布隆过滤器的插入和查询操作都是常数时间复杂度的,即 O(1)。
0.  **存在误判率**:由于布隆过滤器使用的是哈希函数将元素映射到位数组中,因此存在一定的误判率。当判断元素存在时,可能会出现误判,但不存在误判的情况。
0.  **不支持删除操作**:布隆过滤器不支持删除元素操作,因为删除一个元素可能会影响其他元素的判断结果。

布隆过滤器适用于需要快速判断一个元素是否存在于一个大规模集合中的场景,例如网络爬虫中的URL去重、拦截器中的恶意IP检测等。虽然布隆过滤器存在一定的误判率,但在很多场景下可以通过合理设置参数来控制误判率,并在一定程度上提高系统性能。

数据持久化:RDB(Redis DataBase)快照和AOF(Append Only File)日志文件

RDB(Redis DataBase)快照和AOF(Append Only File)日志文件是Redis持久化机制的两种实现方式。

  1. RDB快照

    • RDB快照是通过将Redis在内存中的数据状态定期保存到磁盘上的一个快照文件中。这个快照文件是一个二进制文件,包含了Redis在某个时间点上的数据状态。RDB快照可以通过配置定期保存的策略(如根据时间间隔、达到一定的修改次数等)来触发生成。
    • 优点:RDB快照生成的文件较小,适合用于备份和恢复大量数据;在恢复数据时,快照文件可以更快地加载到内存中。
    • 缺点:如果Redis发生宕机或者故障,可能会丢失最后一次快照之后的所有修改数据。
  2. AOF日志文件

    • AOF日志文件是一种追加写(append-only)的日志文件,记录了Redis服务器接收到的所有写命令操作(如SET、DEL等)。AOF日志文件以文本形式记录命令操作,每条命令操作以协议格式写入文件。
    • 优点:AOF日志文件记录了Redis的操作日志,可以用于恢复数据,可以提供更好的持久化数据保证。另外,AOF文件的操作是追加写的方式,因此相对于RDB快照,AOF文件更加安全,且不会导致数据丢失。
    • 缺点:AOF日志文件通常比RDB快照文件更大,且恢复数据时可能需要更长的时间。

综合考虑,通常情况下,可以同时使用RDB快照和AOF日志文件来进行持久化数据的备份和恢复。这样可以兼顾快速备份和恢复数据(使用RDB快照),以及提供更好的数据保证(使用AOF日志文件)。

数据过期策略/假如redis的key过期之后,会立即删除吗?

Redis对数据设置数据的有效时间,数据过期以后,就需要从内存中删除掉。可以按照不同的规则进行删除,这种删除规则就被称为数据的删除策略。

惰性删除:设置key过期时间后,不去管它。访问key的时候再判断是否过期,如果过期就删除

定期删除:每隔一段时间,就会对一些key进行检查,删除里面过期的key(SLOW模式+FAST模式)

Redis是将惰性删除和定期删除结合使用。

数据淘汰策略/假如缓存过多,内存有限,内存被沾满了怎么办?

Redis中的内存不够,此时依然向Redis中添加新的key,那么Redis按照某一种规则(数据淘汰策略)将内存中的数据删除掉。

image-20240404100009409.png

image-20240404095928917.png

image-20240404100321466.png

如何实现分布式锁

应用程序运行多个服务器上时,syncronized锁是属于jvm的,而每一个服务都有各自的jvm,这个锁只能解决同一个jvm下线程的互斥,解决不了多个jvm下线程的互斥,因此使用分布式锁方式,对多个服务控制。

image-20240404110440701.png

Redis实现分布式锁---基于SETNX命令

使用SETNX(SET if Not eXists)命令可以实现分布式锁的最简单方式。具体步骤如下:

  • 客户端尝试使用SETNX命令设置一个指定的键(作为锁)和值(作为唯一标识),如果设置成功,则表示获取到锁,设置的键值对的过期时间即为锁的持有时间;

    注意:如果不设置过期时间,可能导致死锁。

    //获取锁
    SET lock value NX EX 10 //添加锁,key:lock,值:value,NX是互斥,EX设置超时时间
    
  • 如果设置失败,则表示锁已经被其他客户端持有,客户端可以选择等待一段时间后重试,或者直接返回获取锁失败的结果。

释放锁时,客户端可以使用DEL命令删除锁对应的键。

//释放锁
DEL key

image-20240404102511256.png

所有执行的Redis命令是基于Lua脚本完成的,保证执行的原子性。

image-20240404102736528.png

redisson实现分布式锁-可重入(同一客户端的同一线程,获得锁后能够再次获得锁)

通过线程id判断是否是同一个线程

key:锁

field:当前线程

value:重入次数

进入add1:+1

进入add2:+1

执行unlock:-1

image-20240404105204893.png

redisson实现分布式锁-主从一致性

主节点:写数据,主节点需要同步数据到从节点

从节点:读数据

背景:还未同步数据,主节点宕机,哨兵模式会从两个从节点选择一个当作主节点,当有新线程请求主节点时,新线程(要去同步数据)也能够加锁成功,有可能会出现脏数据现象。

image-20240404110858092.png

解决方案

红锁:不能只在一个redis实例创建锁,应该在多个redis实例上创建(n/2+1)个锁

Redis集群有哪些方案?

blog.csdn.net/m0_70386582…

主从复制

哨兵模式

分片集群

Redis是单线程的,为什么还这儿快?

blog.csdn.net/XingXing_Ja…

1.redis是基于内存的,内存的读写速度非常快;

2.redis是单线程的,省去了很多上下文切换线程的时间;

3.redis使用多路复用技术,可以处理并发的连接。非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间。