一、Dict 整体结构
Redis 底层字典 Dict,用来实现 K-V 映射,是 Redis 核心底层结构,数组 + 单向链表(链地址法解决哈希冲突),由 3 个结构体组成:
-
dict(字典主体)
typedef struct dict {
dictType *type; // 哈希函数类型
void *privdata; // 私有参数
dictht ht[2]; // 两个哈希表:ht[0]正常使用;ht[1]仅rehash时用
long rehashidx; // rehash进度,-1代表不在rehash
int16_t pauserehash; // rehash暂停标记,1暂停、0继续
} dict; -
dictht(哈希表)
typedef struct dictht {
dictEntry **table; // entry指针数组
unsigned long size; // 数组容量,永远是2的n次方
unsigned long sizemask;// 掩码 = size -1,用来计算下标 hash & sizemask
unsigned long used; // 现存键值对数量
} dictht; -
dictEntry(哈希节点)
typedef struct dictEntry {
void *key; // key
union { // value共用体,节省内存
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;// 链表指针,处理哈希冲突
} dictEntry;
寻址规则:key 算出 hash 值,
hash & sizemask得到数组下标;哈希冲突时,挂在单链表后面。
二、Dict 扩容(触发条件)
负载因子:LoadFactor = used / size,新增 key 的时候检查:
LoadFactor >=1,并且没有 BGSAVE/BGREWRITEAOF 后台进程,触发扩容;LoadFactor >5,强制扩容,不管有没有后台进程。
扩容新容量:取第一个 ≥ used+1 的 2 的 n 次方。
三、Dict 收缩(缩容)
删除元素后检查负载因子,LoadFactor < 0.1 ,触发哈希表收缩。 缩容新容量:取第一个 ≥ used 的 2 的 n 次方,最小不低于 4。
缩容也不能在 BGSAVE/BGREWRITEAOF、正在 rehash 时执行。
四、渐进式 rehash(核心面试重点)
目的:一次性迁移百万级数据会阻塞 Redis 主线程,所以分多次慢慢迁移,叫渐进式 rehash
完整流程
- 根据扩容 / 收缩,计算新哈希表 size,开辟内存给
ht[1]; rehashidx = 0,标记开始 rehash;- 每次执行增、查、改、删命令时,顺带迁移 ht 0rehashidx 整条链表到 ht 1,rehashidx 自增;
- rehash 过程读写规则:
- ✅ 新增 key:直接写入 ht 1,保证 ht 0 数据只减不增;
- ✅ 查询 / 修改 / 删除:先查 ht 0,找不到再查 ht 1;
- 全部迁移完成:
ht[1]赋值给ht[0],清空ht[1],释放旧 ht 0 内存; rehashidx = -1,rehash 结束。