[Redis]常见面试题

发布于 2022-04-07  5 次阅读


[Redis]常见面试题


1.为什么用Redis作缓存?为什么快?为什么不用本地缓存?
Redis是基于内存的,并具备高并发的特点,Redis是单线程的且支持I/O多路复用
Redis可以保证数据一致性,支持持久化集中管理,且功能丰富。

2.Redis常见数据结构?底层实现?
String:基于C的简单动态字符串SDS,支持O(1)下得到长度,可以保存二进制数据,可以存储数据缓存、分布式锁、序列化的对象数据;
List:基于压缩列表(<512&&<64)和双向链表;
Hash:基于压缩列表(<512&&<64)和哈希表;
Set:基于整数集合(<512)和哈希表;
ZSet:基于压缩列表(<128&&<64)和跳表;

3.Redis线程模型?I/O多路复用?为什么6.0之后采用多线程?
Redis的单线程是指 接受请求-处理请求-读写文件-返回请求 这个过程是单线程的,但同时也有后台线程来处理文件关闭内存释放AOF刷盘,这些任务是放在队列中处理的,因为比较耗时;
I/O多路复用是指单个线程持续监听多个连接,这是一种非阻塞I/O,且一有请求到达就处理;
Redis的瓶颈主要存在于网络的I/O处理上,主线程仍执行命令,3个后台线程如上述,3个I/O线程用来分担网络压力;

4.Redis持久化实现?

  • AOF追加写日志:执行完一次写操作追加一个记录日志硬盘文件,重启时可以恢复。这样的顺序可以避免额外的语法检查和阻塞写操作进程。具体过程为:
    先执行完写操作,再将写命令追加到缓冲区,然后调用系统write函数写入AOP文件但没同步到磁盘(此时存在内核缓冲区),再根据持久化策略做磁盘同步。AOF文件过大时需要定期重写。
    而持久化策略又分为:Always(写操作后立即同步AOF刷盘,效率低),Everysec(写入AOF文件后,隔一秒刷盘)和No(写到AOF文件后立刻返回,由操作系统决定何时刷盘)。
  • AOF重写是指将旧的AOF文件写到一个新的AOF文件,通过读取键值对,覆盖旧的、冗余的读写命令。AOF重写依靠一个子进程,并维护一个重写缓冲区,在创建新的AOF文件期间,主进程操作也会写到重写缓冲区中,文件创建完成之后,直接将重写缓冲区的内容加到新AOF文件的末尾
    -RDB快照:在某个时间点存一个副本备份,是默认的持久化方式,可以通过配置文件进行修改。可以通过save(阻塞主进程)和bgsave(默认,开个子进程)来存一个快照;
    -混合持久化:AOF文件的前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据。

5.Redis的主从同步?
Redis做集群的时候,主服务器负责写和读,子服务器一般只负责读,第一次同步时采用全量复制,建立长连接之后主服务器发给子服务器写命令来同步,网络断开时采用增量复制。

6.Redis的缓存过期删除策略?
Redis维护一个过期字典,键指向数据的键,值为过期时间;
Redis采用惰性删除(每次访问key的时候检查是否过期,是否需要删除)+定期删除(定期随机抽查,删除过期的key),其中定期删除一般抽20个,一旦有超过5个(设定的比例)过期了,就再抽20个,否则就不继续抽。并且一旦删除时间太久则立刻停止,防止占用cpu时间。
主从模式中,从服务器不会主动删除过期的键,而是主库删除key之后,在AOF中追加一个删除命令并同步到从服务器。
如果大Key集中过期,考虑:设置键的过期时间尽可能随机;或开启lazy free机制,后台异步删除过期key。

7.Redis的缓存问题?一致性问题?缓存更新策略?
缓存雪崩:大量缓存同时失效,或缓存服务器崩溃——设置缓存不过期,后台更新/提前针对热点数据预热/针对过期时间加上随机数,打散过期时间/采用redis集群;
缓存击穿:热点数据同时失效——设置缓存不过期,后台更新/针对热点数据提起预热,活动结束后再失效/用setNX加互斥锁,同时只有一个业务线程可以访问;
缓存穿透:非法或不存在的数据请求——前端或请求端先检查是否合法/缓存无效key,过期时间1min/布隆过滤器/接口限流;
缓存一致性问题:

  • 经典做法:先更新数据库,再删除缓存;
  • 采用读写分离,在缓存中存储数据的版本号或时间戳,读取数据时检查版本号是否与数据库一致。
  • 采用分布式锁,确保同一时间只有一个进程可以更新数据;
  • 设置缓存失效时间,并定期更新缓存或者强制更新;
    追求一致性可以考虑:
  • 引入消息队列,数据库更新完成后,将更新/删除缓存事件发送到消息队列,由监听服务负责更新缓存,失败了就重试;
  • 延迟双删,先删缓存,再更新数据库,再删一次缓存,防止脏数据;

缓存更新策略包括:旁路缓存(常用)、读写穿透、写回策略。
旁路缓存:先写数据库再删缓存,读缓存不存在则从数据库拿数据,再更新缓存;
读写穿透:先写/更新缓存,由缓存更新数据库,读缓存不存在则从数据库更新缓存再返回响应;
写回策略:类似于读写穿透,但更新数据库的操作是异步执行的。适合写多的场景,容易丢失数据。

8.如何用Redis做一个分布式锁?为什么用Redis做?

SET lock_key unique_value NX EX 10000 

其中lock_key是锁的键,unique_value为每个客户端的唯一识别id,NX(键不存在时才执行操作)是允许读取-检查-设置锁变量的原子操作,EX用来设置过期时间防止异常无法释放。
解锁则需要先检查unique_value值是否为加锁客户端,再执行删除键的操作,需要用lua脚本来实现原子性的操作。
集群下如何保证分布式锁可靠性
红锁:采用多个Redis节点,客户端和多个Redis节点分别请求加锁,如果半数以上加锁成功,则认为客户端加分布式锁成功。