Redis面试高频考点拆解(2024实战版)

一、数据结构篇(必考题!)

1. String类型的底层实现

你以为的字符串其实暗藏玄机!Redis的String类型由SDS(Simple Dynamic String)实现,这个结构比C语言的char数组多了三个关键字段:

  • free:剩余可用空间(预分配策略的关键)
  • len:已用长度(O(1)时间复杂度获取长度)
  • buf:字节数组(兼容C字符串)

举个实际案例:当执行APPEND命令时,Redis会先检查free空间是否足够。如果不够就会触发扩容——不是简单的翻倍,而是采用空间预分配策略(小于1MB时翻倍,超过后每次+1MB)

2. Hash的渐进式rehash

当哈希表的负载因子超过阈值时,Redis会怎么做?传统做法是瞬间迁移数据,但这样会导致服务阻塞!于是Redis搞了个骚操作:

// 源码中的字典结构
typedef struct dict {
    dictType *type;
    dictht ht[2]; // 双哈希表结构
    long rehashidx; // rehash进度索引
} dict;

迁移过程分为三步走:

  1. 分配ht[1]空间(新容量是ht[0].used*2)
  2. 将rehashidx设为0(开始迁移标志)
  3. 每次增删改查时顺带迁移一个桶(渐进式迁移)

(注意!)此时如果执行BGSAVE,Redis会强制完成rehash,避免父进程和子进程写时复制冲突

二、持久化陷阱大全

1. RDB vs AOF生死抉择

![对比表格建议用文字描述]

维度RDBAOF
数据完整性可能丢失几分钟数据最多丢失1秒数据
恢复速度
文件体积小(二进制压缩)大(文本命令)
写盘方式内存快照追加日志
适用场景灾难恢复实时持久化

(致命坑点!)当同时开启两种持久化时,重启加载流程是:

  1. 优先加载AOF文件
  2. 如果AOF不存在才加载RDB
  3. 如果两者都不存在→空数据库

2. AOF重写黑科技

你以为的AOF重写是读旧文件?Too young!Redis直接基于内存数据生成新AOF:

# 查看当前重写状态
redis-cli info persistence | grep aof_rewrite_in_progress

触发条件:

  • 手动执行BGREWRITEAOF
  • 自动触发(根据配置的增长率阈值)

(血泪教训)当执行FLUSHALL后立即触发AOF重写,会导致所有数据永久丢失!解决方案:立即SHUTDOWN NOSAVE然后手动删除最后的FLUSHALL命令

三、集群模式深水区

1. 槽位分配算法

16384个槽不是随便定的!这个数字的奥秘在于:

  • 集群节点数最大1000时,每个节点约16个槽
  • 心跳包大小限制(16384/8=2KB)刚好不超过MTU

迁移槽位的正确姿势:

# 查看槽位分布
CLUSTER SLOTS
# 开始迁移
CLUSTER SETSLOT <slot> IMPORTING <node-id>
CLUSTER SETSLOT <slot> MIGRATING <node-id>
# 逐个迁移key
CLUSTER GETKEYSINSLOT <slot> <count>
MIGRATE <host> <port> "" 0 5000 KEYS key1 key2...

2. 脑裂问题终极解决方案

当网络分区发生时,Redis集群可能出现双主写入。Redis的应对策略是:

  1. 节点超时(cluster-node-timeout)
  2. 从节点数据延迟检查
  3. 最小主节点数验证(cluster-migration-barrier)

(必杀技)配置min-slaves-to-write 1可以强制主节点必须有至少1个从节点才能写入,有效降低数据不一致风险

四、实战场景三连击

场景1:缓存雪崩

错误做法:大量key设置相同过期时间
正确姿势:

def set_cache(key, value, expire):
    # 基础过期时间
    base_expire = 3600 
    # 随机增加0-300秒抖动
    real_expire = base_expire + random.randint(0, 300)
    redis_client.set(key, value, ex=real_expire)

场景2:热点Key发现

使用redis-cli --hotkeys命令(需开启LFU算法)
监控代码示例:

// 使用Jedis的监控功能
jedis.monitor(new JedisMonitor() {
    @Override
    public void onCommand(String command) {
        if(command.startsWith("GET ")) {
            String key = command.split(" ")[1];
            hotKeyCache.increment(key);
        }
    }
});

场景3:分布式锁进阶

Redlock算法的正确实现姿势:

  1. 获取当前毫秒级时间戳
  2. 依次向N个实例申请锁(使用相同key和随机值)
  3. 计算获取锁耗时(必须小于锁有效期)
  4. 当且仅当半数以上节点获取成功,并且总耗时小于有效期时才算成功

(超级重点!)一定要用pexpire设置毫秒级过期时间,避免服务器间时钟不同步问题

五、刁钻问题反杀指南

面试官:Redis为什么这么快?
菜鸟答案:因为是内存数据库
高手答案:

  1. IO多路复用+单线程架构(避免上下文切换)
  2. 高效数据结构(跳表、压缩链表等)
  3. 直接访问内存(相比磁盘快5个数量级)
  4. 渐进式Rehash保证平滑扩容
  5. pipeline批处理减少网络往返

面试官:如何实现延迟队列?
青铜方案:ZSET时间戳作为score
王者方案:

-- 使用Lua脚本保证原子性
local jobId = redis.call('INCR', 'delay:job:id')
redis.call('ZADD', 'delay:queue', ARGV[1], jobId)
redis.call('HSET', 'delay:jobs', jobId, ARGV[2])
return jobId

配合zrangebyscorehget实现精准投递


下次面试被问Redis时,把这些知识点甩出来,面试官绝对眼前一亮!(记得自己动手写demo验证哦)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值