Redis常见面试题

67 阅读11分钟

Redis常见面试题

缓存穿透是什么

缓存穿透指查询时缓存中没有数据,就会到数据库中查询数据,数据库也没有这个数据,就无法更新缓存,之后每次查询这个数据都要查询数据库(查询缓存中不存在的数据时,每次都要查询数据库。)

缓存穿透解决方法

给所有key都设置一个默认值Null,如果查询数据缓存和数据库都不存在,则返回Null,并且缓存更新这个key为Null

缓存并发是什么

在高并发场景下,当缓存失效或未命中时,多个请求同时访问数据库,导致数据库压力剧增甚至崩溃

缓存并发解决方法

通过分布式锁或双key机制限制单一线程更新缓存,防止多个请求同时击穿缓存访问数据库

分布式锁:缓存失效时,仅允许一个线程访问数据库并更新缓存,其他线程等待或返回旧数据

双key机制:使用两个缓存键,主键失效时通过时间键判断是否可更新,其他线程返回旧数据,避免数据库压力

缓存雪崩是什么

缓存集体过期或redis宕机,导致大面积请求数据库,造成数据库压力瞬增

缓存雪崩解决方法

  • 将缓存失效时间随机打散,防止出现集体过期。
  • 设置缓存不过期:缓存更新的操作交给后台线程定时更新

怎么设计一个动态缓存热点数据的策略?

通过判断数据最新访问时间来做排名,并过滤掉不常访问的数据,只留下经常访问的数据

  • 先用缓存做一个排序队列,根据商品访问时间更新队列,越是最近访问的商品排名越靠前
  • 同时定期过滤排名靠后的商品,在随机从数据库读取商品加入列队
  • 这样请求每次达到时,会先从队列获取商品ID。如果命中,根据ID从另一个缓存中读取商品信息返回
  • redis中用zadd和zrange实现排序队列和获取商品操作

怎么设计一个缓存操作与业务分离的架构?

MySQL Binlog + Canal + MQ 的方式。

缓存击穿是什么

某个热点数据过期,大量请求无法从缓存中读取,直接访问数据库

缓存击穿解决方法

互斥锁:同一时间只有一个业务线程更新缓存,其他的等待锁或返回默认值

热点数据不设置过期时间

如何保证缓存一致性?

方法一:等待过期

mysql更新时,redis不处理,缓存过期后重新拉去缓存

优点:开发成本低,易实现。管理成本低,难出现问题

缺点:完全依赖过期时间,太短缓存平凡失效,太长不一致时间长

方法二:尝试删除

mysql更新时尝试删除缓存,删除成功下次访问数据时直接访问mysql,再更新缓存

优点:最终一致性延迟更小,成本低

缺点:更新mysql成功,删除redis失败就变成了方案一。高并发场景浪费连接资源

方法三:主动更新

将更新操作交给消息列队,再搭建一个消费服务订阅消息列队异步隔更新redis数据

优点:消息列队具有可靠性,可以保证更新操作被执行

缺点:两个服务同时修改一个数据,由于网络延迟,无法保证谁先进消息队列,导致产生数据不一致。增加消费服务成本高。

方法四:订阅日志

订阅mysql的binlog日志,更新到redis

优点:同步压力不大时延迟低。解决了时序性,可靠性强

缺点:需要搭建同步服务,引入binlog同步,成本大

方法一和二完全可以满足读多写少场景,对延时要求高则使用方法三和四

-----------------------

什么是redis

Redis是一个非关系型数据库,存储结构是key-value,直接存在内存中,读写非常快

NoSQL的BASE理论是什么

BASE理论是CAP中一致性的妥协,允许数据在一段时间内不一致,但要达到最终一致性

说说常用的Redis命令

最常用的读操作是get a,获取a对应的数据;最常用的写操作setex a t b,将a的数据设置为b,t秒后过期

redis过期清除策略是什么

过期清除策略有三种,定时清理,懒惰清理,定期清理。redis采用的是懒惰清理+定期清理

什么是定时清理

设置过期时间同时创建一个定时器,定时器在过期时间来临时立即执行删除操作

什么事懒惰清理

不主动清理,下次访问时发现过期再删除

什么是定期清理

每个一段时间检查一次,删除里面过期键

如果过期键没有被访问,周期性删除跟不上新键产生速度,内存不就慢慢耗尽了吗?

redis支持内存淘汰,配置文件中提供八种淘汰策略

redis支持哪八种内存淘汰策略

1、不开启数据淘汰

2、开启数据淘汰策略

  • 基于有过期时间的数据

    • 随机淘汰
    • 淘汰过期时间最早
    • 淘汰最近最少使用
    • 淘汰最近最不常使用
  • 所有数据

    • 随机淘汰
    • 淘汰最近最少使用
    • 淘汰最近最不常使用

内存淘汰用到的是LRU算法吗?

Redis用到近似LRU算法,LRU需要双向链表记录最近被访问顺序。为了节省内存,Redis的LRU不是完整实现

Redis是怎么实现近似LRU算法?

通过对少量键取样,然后和现在维持的淘汰池比较,回收其中最久未被访问的键。调整采样数量可以实现调整算法的精度

Redis有多少种数据结构?

基本数据结构有5种,String、List、Hash、Set、Zset。底层实现是SDS、压缩链表、跳表,字典等更基础的数据结构

String底层采用什么数据结构

整数int和字符串sds

List底层采用什么数据结构

元素小于64字节;个数不超过512用压缩链表。

其他用双向链表

Hash底层采用什么数据结构

元素小于64字节;个数不超过512用压缩链表。

其他用字典

Set底层采用什么数据结构

元素为整数;元素个数不超过512用整数集合

其他用字典

Zset底层采用什么数据结构

元素小于64字节;个数不超过128用压缩链表

其他用跳表+字典

Redis字符串有什么特点?

如果保存整数类型,就用int存储。不然就用sds,sds通过记录长度和预分配空间,可以高效计算长度和append操作

Hash扩容过程是怎样的?

有两张hash表,平时起作用的是0号表,当快装满时就会Rehash,将0号上的数据慢慢移动到1号表,所以叫渐进式Rehash

详细说说Rehash过程

发现快装满时就会进行Rehash。Rehash分三步

  • 生成新Hash表1,给Hash表1分配空间。 这时同时拥有两个哈希表,Rehash开始工作
  • 迁移Hash表0数据到表1。每次对字典进行增删查改,程序会顺带迁移一个表0上的数据,并更新偏移索引。同时周期函数也定时迁移一批
  • 最后,表1和表0指针对象互换

如果Rehash时有请求过来,Redis怎么处理

新增key直接插入表1;读请求先去表0中读取,没有再去表1;删除和更新需要先找到位置再操作,所以先找表0,再找表1,找到后再操作

讲讲跳表实现

跳表是对链表的优化,通过逐层跳步采样构建索引,加快查找速度。可以通过高层索引,一次跳过多个节点,如果跳过头,进入下层索引查找。

每个节点有多少层?

Redis用随机函数决定层数,默认1层,最大32层

Zset为什么同时需要字典和跳表实现?

Zset是有序列表,字典和跳表对应两种查询场景。字典支持按成员查询数据;跳表实现高效范围查询

Redis服务器重启数据不就丢失了吗?

可以通过持久化机制备份数据。RDB快照和AOF日志

RDB是二进制快照,占用空间小,恢复快。备份采用子进程fork模式,对性能影响小。

AOF记录操作命令,占用空间大,恢复慢,但是丢失数据少。

AOF有三种写回模式:每秒定期刷入、执行命令后立刻出发、关闭时刷入。本质都是调用操作系统的fsync方法

AOF文件会越来越大,最后磁盘会装不下吗?

不会,AOF有重写机制,后面覆盖前面

Redis宕机怎么办?

可以采用主从复制,从机会同步主机数据,主机挂了可以把从机升级为主机。

主从可以自动切换吗?

不行,需要引入哨兵模式来监测。哨兵模式担任监测、选举和通知的职责。

如果主机挂了,会选择哪个从机?

哨兵集群会先选出哨兵Leader,哨兵Learder会从从节点中选择一个作为主节点

  • 过滤故障节点
  • 选择优先级最大的从节点,不存在则继续
  • 选择复制偏移量最大的从节点,就是同步内容最多的从节点
  • 选择runid最小的从节点,Redis启动时随机生成的标识

哨兵leader怎么来的

一个哨兵发现主节点主观下线后,会请求其他哨兵选举自己为Leader,其他哨兵进行投票。如果票数超过节点数一半,并且大于quorum值则为leader

Redis性能怎么样?

在十万级数据性能很高,实际情况受限带宽、负载、数据大小、是否开启多线程等因素影响

Redis是多线程的吗?

主要工作是单线程的,但是后面受网络影响,对网络IO相关工作使用了多线程优化。

详细说下Redis6.0版本发布的多线程功能?

多线程主要用在提高解包效率,只负责处理网络IO相关的内容(解包和协议转换)

数据太大,Redis存不下怎么办?

使用集群模式。 将数据分片,不同的key根据hash路由到不同的节点。集群索引通过一致性hash算法实现,核心目标是水平扩展和分布式存储,包含了主从+哨兵的优点

详细讲解一致性hash

将数据和服务器以相同的hash函数,映射到同一个hash环上,针对一个对象找距离最近的机器来处理相关请求。

增加节点只会分流后面一个节点的数据;减少节点,请求会由后一个节点继承。最多影响后面一个节点的数据

Redis经常用作缓存,那数据一致性怎么保证?

从设计思路来说,有旁路缓存和读写穿透模式。

旁路缓存模式把缓存责任交给应用层。

读写穿透模式把缓存责任交给服务提供方。

两种模式哪种更好?

从透明性考虑,服务方合适。从性能考虑业务方合适

如果数据发生变化怎么更新缓存?

  • 数据存到数据库后,再让缓存失效,等到缓存不命中后再加载进去
  • 消息队列更新缓存
  • 先更新缓存,再更新服务(把Cache当做Buffer使用)
  • 起一个同步服务,解析MySQL的binlog同步缓存

布隆过滤器的实现

通过一个位数组和多个哈希函数,将元素哈希到多个位置并设置为1,查询时若所有位置均为1则可能存在(允许误判),否则必定不存在

那Redis可以做消息队列吗?

可以,但是不适合,redis不支持AMQP规范

谈谈Redis在秒杀场景的应用吗?

redis起到选拔流量的作用,记录商品总数和已下单数,达到总数之后拦截所有请求。