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

Redis的基础,经典,高级问题解答篇

目录

一,基础

二,经典

缓存雪崩:

1. Redis事务的原子性

2. 与MySQL事务的区别

1. 主从复制原理

2. 哨兵模式故障转移流程

3. 客户端感知故障转移

三,高级


  • 一,基础

  1. Redis的5种基础数据类型及使用场景?Zset底层实现原理?

    1. String(字符串)

      • 场景:缓存简单键值对(如用户会话信息、计数器)、分布式锁(如SETNX)、二进制数据存储(如图片Base64)。

      • 特点:支持原子操作(如INCRDECR),最大存储512MB。

    2. List(列表)

      • 场景:消息队列(LPUSH/RPOP实现生产者-消费者)、最新消息排行(如微博时间线)、阻塞式任务调度。

      • 特点:双向链表,支持按索引操作(但时间复杂度高)。

    3. Hash(哈希表)

      • 场景:存储对象(如用户信息字段:HSET user:1 name "Alice")、聚合数据(如购物车商品信息)。

      • 特点:支持单字段读写,内存优化(底层为ziplist或hashtable)。

    4. Set(集合)

      • 场景:去重数据(如用户标签)、共同好友(SINTER求交集)、随机抽奖(SRANDMEMBER)。

      • 特点:无序、自动去重,支持集合运算(并/交/差)。

    5. Zset(有序集合)

      • 场景:排行榜(按分数排序)、延迟队列(以时间戳为score)、带权重的任务调度。

      • 特点:元素唯一但score可重复,支持范围查询(ZRANGEBYSCORE)。

    6. Zset的底层由 跳跃表(Skip List) 和 字典(Dict) 组成,或在小数据量时使用 listpack(替代旧版的ziplist)

      • 跳跃表

        • 多层链表结构,高层链表作为“快速通道”,支持O(log N)的查询、插入、删除。

        • 每个节点包含scorevalue,按score排序,支持高效范围操作(如ZRANGE)。

      • 字典

        • 维护value -> score的映射,实现O(1)的单元素查询。

      • 内存优化

        • 元素数≤zset-max-listpack-entries且元素大小≤zset-max-listpack-value时,使用listpack存储。

  2. Redis持久化机制RDB和AOF的区别?如何选择?

    1. 特性RDBAOF
      原理定时生成全量数据快照(二进制文件)记录所有写命令(追加日志文件)
      文件体积小(压缩二进制)大(文本命令,需重写优化)
      恢复速度快(直接加载内存)慢(重放命令)
      数据安全可能丢失最后一次快照后的数据可配置fsync策略(无/秒级/每次写)
      资源消耗高(fork子进程内存开销)低(追加日志,重写时才有fork开销)
    2. 选择策略

      1. 高数据安全:选择AOF(appendfsync everysec),容忍秒级丢失。

      2. 快速恢复/备份:选择RDB(如每日备份)。

      3. 混合模式:Redis 4.0+支持RDB+AOF,AOF记录增量,RDB做全量备份。

  3. 如何用Redis实现分布式锁?需要注意哪些问题?

    1. 实现步骤

    2. // 加锁:SET key unique_value NX PX 30000
      String result = jedis.set("lock_key", "client1", "NX", "PX", 30000);
      if ("OK".equals(result)) {
          // 执行业务逻辑
      } 
      
      // 解锁:Lua脚本保证原子性
      String script = 
          "if redis.call('get', KEYS[1]) == ARGV[1] then " +
          "   return redis.call('del', KEYS[1]) " +
          "else " +
          "   return 0 " +
          "end";
      Object unlockResult = jedis.eval(script, Collections.singletonList("lock_key"), Collections.singletonList("client1"));

      注意事项

      1. 锁过期时间:需预估业务耗时,建议设置自动续期(如Redisson的看门狗)。

      2. 唯一标识:value使用唯一值(如UUID+线程ID),避免误删其他客户端的锁。

      3. 优化方案

      4. 使用Redisson库,内置看门狗、可重入锁、红锁(RedLock)实现。

      5. 对锁操作添加重试机制(如指数退避)。

      6. 原子性操作:加锁(SET NX PX)和释放锁(Lua脚本)必须原子化。

      7. 集群问题

        • 主从异步复制:主节点宕机可能导致锁丢失,可考虑RedLock算法(需部署多实例)。

        • RedLock争议:依赖系统时钟一致性,需权衡CAP。

  • 二,经典

  1. 什么是缓存穿透/雪崩/击穿?分别给出解决方案
    1. 缓存穿透:
      1. 问题:大量请求查询数据库中不存在的数据(如非法ID),绕过缓存直接压垮数据库。
        解决方案:        

        1. 布隆过滤器(Bloom Filter):在缓存层前加布隆过滤器,预存所有合法键的哈希值,拦截非法请求。

          • 特点:存在一定误判率(可能将非法请求误判为合法),需权衡内存占用。

        2. 缓存空值:对查询结果为null的键,缓存空值并设置短过期时间(如30秒)。

          • 注意:需定期清理无效空值,避免内存浪费。

    2. 缓存雪崩:
      1. 问题:大量缓存同时过期或缓存服务宕机,导致请求全部涌向数据库。
        解决方案

        1. 随机过期时间:为缓存设置基础过期时间 + 随机偏移值(如 TTL = 24h + random(0, 1h))。

        2. 永不过期 + 异步更新

          • 缓存不设过期时间,通过后台线程定期更新。

          • 结合双缓存策略:主缓存永不过期,备份缓存设置过期时间作为兜底。

        3. 熔断降级:使用Hystrix等工具,在数据库压力过大时直接返回默认值或限流。

    3. 缓存击穿:
      1. 问题热点数据过期时,高并发请求瞬间击穿缓存,直接访问数据库。
        解决方案

      2. 互斥锁(Mutex Lock)

        • 当缓存失效时,通过分布式锁(如Redis的SETNX)控制只有一个线程重建缓存,其他线程等待。

        • 示例代码:

        • public String getData(String key) {
              String value = redis.get(key);
              if (value == null) {
                  if (redis.setnx("lock_" + key, "1", 10)) { // 获取锁
                      value = db.query(key);
                      redis.setex(key, 3600, value);
                      redis.del("lock_" + key); // 释放锁
                  } else {
                      Thread.sleep(100); // 等待后重试
                      return getData(key);
                  }
              }
              return value;
          }
        • 逻辑过期:缓存永不过期,但存储的数据中增加逻辑过期时间字段,由业务代码判断是否需要异步更新。

  2. Redis事务的原子性如何理解?与MySQL事务的区别?
    1. 1. Redis事务的原子性
      • 实现方式:通过MULTI(开启事务)、EXEC(提交事务)、DISCARD(取消事务)命令实现。

      • 原子性含义

        • 命令队列的原子执行:事务中的命令会被序列化并按顺序执行,不会被其他客户端命令打断。

        • 不支持回滚:若事务中某条命令执行失败(如语法错误),其他命令仍会继续执行,无回滚机制。

        • 示例:

        • MULTI
          SET key1 "A"  # 入队
          INCR key2     # 入队(若key2非数值类型,执行时报错)
          EXEC          # 提交事务,第二条命令执行失败,但第一条仍生效
      • 2. 与MySQL事务的区别
        特性RedisMySQL
        原子性仅保证命令队列的批量执行,无回滚支持ACID,失败时自动回滚
        隔离性无隔离级别,事务执行期间可能被其他客户端修改数据支持多隔离级别(如读已提交、可重复读)
        持久性依赖持久化机制(RDB/AOF)依赖Redo Log和Binlog保证持久性
        使用场景简单批量操作(如批量SET)复杂业务逻辑(如转账、订单处理)
  3. Redis主从复制原理?哨兵模式如何实现故障转移?
    1. 1. 主从复制原理
    2. 核心流程

      1. 全量同步(首次连接)

        • 从节点发送PSYNC ? -1命令请求同步。

        • 主节点执行BGSAVE生成RDB文件并发送给从节点,同时缓存期间的写命令到复制缓冲区。

        • 从节点加载RDB后,主节点发送缓冲区中的写命令使其追上最新状态。

      2. 增量同步(断线重连)

        • 从节点发送PSYNC <runid> <offset>,主节点根据offset从复制缓冲区发送增量数据。

        • 若复制缓冲区数据丢失(如缓冲区溢出),触发全量同步。

    3. 关键配置

      • repl-backlog-size:调整复制缓冲区大小,避免频繁全量同步。

    4. 2. 哨兵模式故障转移流程

      哨兵(Sentinel)是Redis的高可用解决方案,负责监控、通知和自动故障转移:

    5. 监控

      • 每个哨兵节点定期向主节点、从节点和其他哨兵发送PING命令检测存活状态。

    6. 主观下线(SDOWN)

      • 若哨兵在down-after-milliseconds时间内未收到主节点的有效响应,标记其为主观下线。

    7. 客观下线(ODOWN)

      • 当超过半数哨兵认为主节点主观下线,则标记为客观下线。

    8. 选举Leader哨兵

      • 通过Raft算法选举一个Leader哨兵来执行故障转移。

    9. 故障转移

      • Leader哨兵从从节点列表中选出一个新的主节点(基于优先级、复制偏移量等)。

      • 向其他从节点发送SLAVEOF命令,使其复制新主节点。

      • 更新客户端配置,通知其连接新主节点。

    10. 3. 客户端感知故障转移
    11. 客户端通过订阅哨兵的+switch-master事件获取新主节点地址,或通过哨兵API动态查询。

  • 三,高级

  1. Redis Cluster集群模式的数据分片原理?如何实现动态扩容?
    1. 数据分片原理
      Redis Cluster采用哈希槽(Hash Slot)机制进行数据分片,共有16384个槽位。每个键通过CRC16(key)计算哈希值,再对16384取模确定所属槽位。集群中的每个节点负责一部分槽,数据分布由槽分配决定。客户端请求时,若连接节点不负责该槽,会返回MOVED重定向错误,引导客户端访问正确节点。节点间通过Gossip协议交换集群状态,维护槽分配信息的一致性。

      动态扩容实现

    2. 添加新节点:使用CLUSTER MEET将新节点加入集群。

    3. 迁移槽数据

      • 使用redis-cli --cluster reshard触发槽重新分片。

      • 源节点将槽内键逐个迁移到目标节点,期间对正在迁移的键的请求,源节点返回ASK重定向,客户端需重试到目标节点。

    4. 更新槽分配:迁移完成后,槽归属信息通过Gossip协议同步到整个集群。

    5. 副本扩展:新增副本节点通过CLUSTER REPLICATE同步主节点数据。

  2. Redis内存淘汰策略有哪些?如何设计大Key热Key的解决方案?
    1. 内存淘汰策略(通过maxmemory-policy配置)

    2. noeviction:拒绝写入新数据(默认)。

    3. volatile-ttl:淘汰过期时间最近的键。

    4. LRU/LFU策略

      • allkeys-lru/volatile-lru:基于最近最少使用。

      • allkeys-lfu/volatile-lfu:基于访问频率(4.0+)。

    5. 随机淘汰allkeys-random/volatile-random

    6. 大Key解决方案

    7. 拆分:如将大Hash拆分为多个小Hash,通过键名后缀分片。

    8. 异步删除:使用UNLINK代替DEL,非阻塞释放内存。

    9. 压缩:对Value进行压缩(如GZIP),但会增加CPU开销。

    10. 监控:通过redis-cli --bigkeys定期扫描大Key。

    11. 热Key解决方案

    12. 多级缓存:本地缓存(如Guava)减少Redis访问。

    13. 读写分离:通过副本节点分散读压力。

    14. 分片打散:使用Hash Tag(如{user1000}.profile)强制热Key分布到同一节点,并增加副本。

    15. Proxy支持:如Twemproxy或Codis自动分散请求。

  3. Redis多线程模型演进(从单线程到IO多线程)?管道技术原理?
    1. 多线程演进

    2. 单线程模型(6.0前):单线程处理所有网络I/O、命令解析和执行,避免锁竞争,但无法利用多核CPU。

    3. I/O多线程(6.0+)

      • 多线程网络I/O:主线程负责接收连接,I/O线程处理读写(配置io-threads启用)。

      • 单线程命令执行:命令执行仍为单线程,保证原子性。

    4. 性能提升:高并发场景下,网络I/O瓶颈缓解,吞吐量显著提升。

    5. 管道技术(Pipeline)原理

    6. 批量发送:客户端将多个命令打包发送,减少网络往返次数(RTT)。

    7. 服务端顺序执行:服务器按顺序依次执行命令,全部完成后一次性返回结果。

    8. 优势:适用于批量操作(如批量写入),提升吞吐量。

    9. 注意点:不保证原子性,且返回结果需客户端按序解析。


http://www.kler.cn/a/611696.html

相关文章:

  • ④(上网管理行为-ACG)主备/主主
  • 11:00开始面试,11:08就出来了,问的问题有点变态。。。
  • 零基础使用AI从0到1开发一个微信小程序
  • 十一、JavaScript简单数据类型和复杂数据类型
  • sqlmap 源码阅读与流程分析
  • LeetCode 第36、37题(数独问题)
  • Linux安装Ipanel
  • 反向 SSH 隧道技术实现内网穿透
  • Golang io模块详细功能介绍与示例
  • 数据大屏点亮工业互联网的智慧之眼
  • linux网络编程以及epoll IO多路复用
  • 计算机网络基础:量子通信技术在网络中的应用前景
  • 解决Cubemx生产的 .ioc文件不能外部打开的方法
  • Vulhub靶机--FAll
  • 数据湖的崛起:从大数据到智能未来的钥匙
  • CMake入门及生成windows下的项目示例讲解
  • Postman 请求头详解:快速掌握
  • flutter 获取设备的唯一标识
  • 国产 FPGA 的崛起之路,能否打破 Xilinx 的垄断?
  • nodejs-原型污染链