当前位置: 首页 > ai >正文

redis相关面试题

1.缓存穿透(Cache Penetration)

  • 问题描述: 查询一个数据库中根本不存在的数据。由于缓存中不会有该数据(未命中),导致每次请求都直接访问数据库,给数据库造成巨大压力,甚至压垮数据库。

  • 原因:

    • 恶意攻击:攻击者故意构造大量不存在的ID进行查询。

    • 业务逻辑错误:程序BUG导致产生大量无效请求。

  • 解决方案:

    • 缓存空对象(Null Caching / Bloom Filter Pre-caching): 即使数据库查询为空,也将这个空结果(比如 null)进行短暂的缓存(设置一个较短的过期时间,如 1-5 分钟)。这样后续相同的无效请求在缓存过期前会命中空对象,保护数据库。

      • 优点: 实现简单。

      • 缺点: 1) 消耗额外的内存存储大量空值。2) 存在短期的数据不一致(如果这个key在数据库里被创建了,在空对象过期前可能查不到)

    • 布隆过滤器(Bloom Filter): 在访问缓存之前,先经过一个布隆过滤器。

      • 原理: 布隆过滤器是一个概率型数据结构,可以高效地判断一个元素一定不存在可能存在于某个集合中。

      • 工作流程:

        1. 将所有可能存在的有效键(或键的哈希值)预先加载到布隆过滤器中。

        2. 请求到来时,先用布隆过滤器判断请求的Key:

          • 如果布隆过滤器说不存在 -> 则直接返回空或错误,无需查询缓存和数据库(拦截无效请求)。

          • 如果布隆过滤器说可能存在 -> 则继续走正常的缓存查询逻辑(查询缓存,未命中则查数据库)。

      • 优点: 内存占用远小于缓存空对象,能有效拦截绝大部分无效请求。

      • 缺点: 1) 存在一定的误判率(False Positive - 判断为可能存在,但实际上不存在,这种情况仍然会走到数据库查询,但概率可控)。2) 删除数据困难(需要维护布隆过滤器的更新,通常结合空对象缓存使用)。3) 需要预加载有效键集合。

    • 接口层校验: 对请求参数进行基础校验(如ID范围、格式校验),拦截明显无效的请求。

 2.缓存击穿(Cache Breakdown)

  • 问题描述: 某个热点数据Key在缓存中恰好过期的瞬间,有大量的并发请求同时涌来。这些请求发现缓存失效,都会去后端数据库加载数据,导致数据库瞬间压力剧增甚至崩溃。

  • 原因: 热点Key + 缓存同时失效 + 高并发。

  • 与穿透的区别: 击穿针对的是存在但暂时失效的热点数据;穿透是针对根本不存在的数据。

  • 解决方案:

    • 设置热点数据永不过期: 对于一些极热点的数据,可以设置永不过期(或物理上不设置过期时间)。通过逻辑过期(在Value中存储一个过期时间字段)或后台异步更新策略来保证数据的更新。

      • 工作流程:

        1. 缓存永不过期,Value中包含逻辑过期时间 expireTime

        2. 查询时,先返回缓存数据。

        3. 程序判断 expireTime 是否已过:

          • 未过:直接返回数据。

          • 已过:触发异步线程去更新缓存,当前请求仍返回旧数据(或稍作等待)。

      • 优点: 避免缓存同时失效。

      • 缺点: 需要额外字段和异步更新逻辑,可能返回短暂旧数据。

    • 互斥锁(Mutex Lock):

      • 工作流程:

        1. 当缓存失效时,不是所有线程都去查数据库。

        2. 第一个发现失效的线程尝试获取一个分布式锁(例如使用Redis的 SET key value NX PX milliseconds 命令)。

        3. 获取锁成功的线程负责查询数据库并更新缓存。

        4. 其他线程等待(轮询或订阅通知)或者短暂休眠后重试读取缓存。

      • 优点: 保证只有一个线程去查数据库,保护数据库。

      • 缺点: 1) 未获取锁的线程需要等待,增加延迟。2) 分布式锁的实现复杂度。3) 锁失效时间设置不当可能导致死锁或重复查询。

    • 缓存预热(Cache Warm-up): 在系统启动或低峰期,提前将热点数据加载到缓存中,尽量避免在高峰期出现缓存失效。

 3.缓存雪崩(Cache Avalanche)

  • 问题描述: 在某一时刻,大量的缓存Key同时失效(或Redis服务不可用),导致所有原本应该命中缓存的请求都涌向后端数据库,造成数据库压力骤增甚至崩溃。

  • 原因:

    • 大量Key设置了相同的过期时间(例如系统初始化时批量加载数据,设置了相同的TTL)。

    • Redis服务宕机集群故障

  • 与击穿的区别: 雪崩是大量Key同时失效;击穿是单个热点Key失效。

  • 解决方案:

    • 设置不同的过期时间: 这是最常用且有效的方法。给缓存数据的过期时间加上一个随机值(例如基础过期时间 + 随机几分钟)。确保数据不会在同一时间大面积失效,而是分散失效。

    • 构建高可用缓存集群:

      • Redis Sentinel(哨兵): 实现主从切换和故障转移,提高可用性。

      • Redis Cluster(集群): 提供数据分片(Sharding)和高可用,即使部分节点宕机,集群整体仍能提供服务(部分数据可能暂时不可用)。

    • 服务熔断与降级:

      • 熔断(Circuit Breaker): 当检测到数据库访问失败率高或响应过慢时,暂时“熔断”对数据库的访问,直接返回预设的默认值(或错误信息),给数据库恢复的时间。

      • 降级(Degradation): 在系统压力过大时,暂时关闭一些非核心功能或返回简化数据,优先保证核心功能的可用性(可能核心功能也会用到缓存,但压力会小些)。

    • 多级缓存: 在应用层(如本地缓存Guava Cache/Caffeine)和分布式缓存(Redis)之间再加一层缓存。即使Redis挂了,本地缓存还能抵挡一部分流量,为恢复争取时间。需要注意本地缓存的一致性管理。

    • 缓存永不过期 + 后台更新: 类似击穿的解决方案,对关键数据使用。

 4.内存淘汰策略决策

策略作用范围淘汰依据适用场景
allkeys-lru所有Key最近最少使用通用缓存(保留热点数据)
volatile-lru带过期时间的Key最近最少使用区分永久/临时数据
allkeys-lfu所有Key访问频率最低防扫描访问导致缓存污染
volatile-ttl带过期时间的KeyTTL最短优先清理即将过期数据
noeviction-拒绝写入数据绝对不可丢失场景

5. 缓存的数据结构

常用结构及典型场景:

  • String:

    • 场景:简单KV缓存(用户信息序列化JSON存储)、计数器(INCR/DECR)、分布式锁(SETNX)、位操作(签到统计)。

  • Hash:

    • 场景:存储对象(如用户信息,每个field对应一个属性)。适合需要部分修改HSET单个field)或部分读取HGET单个field)的场景。比String序列化整个对象更节省网络流量和内存(Redis内部优化)。

  • List:

    • 场景:消息队列(LPUSH/RPOPBRPOP阻塞版本)、最新消息/动态列表(LPUSH+LTRIM保持固定长度)、栈(LPUSH/LPOP)。

  • Set:

    • 场景:无序集合(标签Tag、用户关注列表)、去重(UV统计初步去重)、集合运算(交集SINTER/并集SUNION/差集SDIFF - 共同好友、推荐)。

  • Sorted Set (ZSet):

    • 场景:排行榜(ZADD+ZREVRANGE)、带权重的队列(延迟队列 - 时间戳作为score)、范围查找(按分数区间ZRANGEBYSCORE)。

6.Redis为什么快?

  • 内存操作: 数据存储在内存,读写速度极快。

  • 单线程模型:

    • 避免多线程上下文切换和锁竞争开销。

    • I/O多路复用 (epoll/kqueue): 单线程高效处理大量并发连接。

  • 高效的数据结构: 精心设计的底层数据结构(SDS, HashTable, SkipList, ZipList等)。

 7.缓存与数据库的数据一致性

问题本质: 如何保证缓存中的数据和数据库中的数据在更新操作后保持一致

  • 延迟双删(Double Delete): (Cache-Aside的增强)

    1. 更新数据库。

    2. 删除缓存。

    3. 等待一小段时间 (比如几百毫秒,根据业务主从延迟估算)。

    4. 再次删除缓存。

    • 目的:清除在步骤1-2期间可能被其他读请求写入缓存的旧数据。

    • 步骤操作目的注意事项
      1更新数据库确保数据持久化更新主库
      2首次删除缓存清除旧缓存立即执行
      3等待一段时间等待主从同步完成时间需根据业务主从延迟设置
      4再次删除缓存清除期间可能写入的旧数据确保最终一致性
  • 为什么需要二次删除:避免脏读回填

  • 脏读回填问题图示

  • 订阅数据库变更日志(Binlog): (如Canal, Debezium)

    • 通过监听数据库的变更日志(如MySQL的Binlog),将变更事件发布到消息队列(如Kafka)。

    • 独立的消费者程序消费消息,更新或删除Redis中的缓存。

    • 优点: 解耦应用,通用性强,能保证最终一致性。

    • 缺点: 架构复杂,引入新组件,延迟比Cache-Aside高。

  • 关键点:

    • 强一致性(CP)代价很高,通常选择最终一致性(AP)。

    • 选择删除缓存而不是更新缓存,能有效避免复杂的并发更新时序问题(如两个并发写操作更新DB和缓存的顺序错乱导致脏数据)。

    • 在高并发写场景下,任何策略都可能存在短暂不一致,需要业务容忍度或结合其他机制。

 

http://www.xdnf.cn/news/14572.html

相关文章:

  • 使用模板创建uniapp提示未关联uniCloud问题
  • vscode+react+ESLint解决不引入组件,vscode不会报错的问题
  • 小孙学变频学习笔记(四)变频器的逆变器件—IGBT管(下)
  • linux 远程终端执行qt应用显示到接入的物理显示器上
  • 如何仅用AI开发完整的小程序<5>—让AI制作开始页面
  • C++ Programming Language —— 第2章:数据类型
  • C#.NET HttpClient 使用教程
  • 【Dicom标准】dicom数据中pixelData显示处理流程详细介绍
  • Linux 服务器运维:磁盘管理与网络配置
  • 一个免费的视频、音频、文本、图片多媒体处理工具
  • ICM-20948 Wake on Motion功能开发全过程(8)
  • Python 的内置函数 hash
  • python模块常用语法sys、traceback、QApplication
  • 操作系统内核态和用户态--2-系统调用是什么?
  • 决策树:化繁为简的智能决策利器
  • GO语言---数组
  • 【Docker基础】Docker镜像管理:docker rmi、prune详解
  • 经典:在浏览器地址栏输入信息到最终看到网页的全过程,涉及网络协议以及前后端技术
  • Vue状态管理实践:使用Vuex进行前端状态管理
  • FVISION 未来视界工作室:AI驱动的创新与智能外包平台
  • TodoList 案例(Vue3): 使用Composition API
  • Snapchat矩阵运营新策略:亚矩阵云手机打造高效社交网络
  • 基于SpringBoot+Uniapp的活动中心预约小程序(协同过滤算法、腾讯地图、二维码识别)
  • 【论文笔记】【强化微调】TinyLLaVA-Video-R1:小参数模型也能视频推理
  • SQLite 数据库操作完整指南
  • Spring Boot邮件发送终极指南:从基础到高级应用
  • AI大模型学习之基础数学:高斯分布-AI大模型概率统计的基石
  • RocketMQ--为什么性能不如Kafka?
  • Mac电脑-Markdown编辑器-Typora
  • springboot垃圾分类网站