Skip to content
基础

一句话答案

缓存(String/Hash)、分布式锁(SETNX)、计数器(INCR)、排行榜(ZSet)、Session、延迟队列(ZSet)、限流。

核心要点

Hash 的底层编码(两种):

  • listpack(JDK 7.0 前叫 ziplist):小数据量时用,连续内存,节省内存
    • 触发条件:字段数 ≤ 128 且每个值 ≤ 64 字节
  • hashtable(字典 dict):大数据量时用,支持 O(1) 查找

hashtable(dict)结构:

c
typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];       // 两个哈希表(用于渐进式 rehash)
    long rehashidx;     // -1 表示未在 rehash;否则表示当前 rehash 到的桶索引
    int iterators;
} dict;

渐进式 rehash(Redis 的扩容机制):

为什么用渐进式:

  • 如果一次性 rehash 所有 key,数据量大时会阻塞 Redis 服务(Redis 单线程),导致响应超时

渐进式 rehash 过程:

触发条件:
  ht[0] 的 used/size(负载因子)> 1(安全模式下 > 5)

开始 rehash:
  1. 分配 ht[1](容量为下一个 >= ht[0].used*2 的 2 的幂)
  2. 设置 rehashidx = 0(表示从桶 0 开始 rehash)

渐进迁移(每次操作时顺带迁移):
  每次对 dict 的增删改查操作,额外将 ht[0].table[rehashidx] 桶的数据迁移到 ht[1]
  迁移完毕,rehashidx++

迁移期间的查询:
  先查 ht[0],找不到再查 ht[1]

迁移期间的写入:
  新数据直接写入 ht[1](保证 ht[0] 只减不增,最终清空)

rehash 完成:
  ht[1] 成为新的 ht[0],重置 rehashidx = -1
追问与易错

追问方向:

  • 排行榜数据量大怎么办?
  • 延迟队列用 ZSet 什么问题?
  • 分布式限流用 Redis 怎么做?

易错点:

  • ❌ Redis 能解决所有缓存问题——需考虑容量和一致性
  • ❌ 所有场景都用 String——选对数据结构很重要

💡 记忆锚点

数据结构选型口诀:String存对象/计数,Hash存字段(用户画像),List做队列,Set做去重/交集(共同好友),ZSet做排行榜/延迟队列。记住"选错数据结构=用螺丝刀钉钉子"。