面试题答案
一键面试字典的创建
- 数据结构:Redis 字典使用哈希表作为底层实现,一个哈希表由
dict.h/dictht
结构体表示,包含数组table
,存储哈希表节点dictEntry
,还有记录已使用节点数used
和哈希表大小size
等信息。 - 哈希函数:Redis 使用 MurmurHash2 算法来计算键的哈希值,将键映射到哈希表的某个槽位。这种哈希函数具有较好的散列性,能减少哈希冲突。
- 初始化:创建字典时,初始化哈希表的
size
为初始值(通常为 4),used
为 0,table
数组的每个元素初始化为NULL
。
扩容机制
- 触发条件:当哈希表的负载因子(
used / size
)大于等于 1,并且dict_can_resize
标志为真时,会触发扩容。此外,当服务器执行BGSAVE
或BGREWRITEAOF
命令时,如果负载因子大于等于 5 也会触发扩容。 - 扩容过程:分配一个大小为原哈希表
size
两倍的新哈希表,重新计算所有键值对的哈希值和索引,将原哈希表中的所有键值对重新插入到新哈希表中,最后释放原哈希表的内存空间。 - 渐进式 rehash:为避免一次性 rehash 对服务器性能造成过大影响,Redis 采用渐进式 rehash。在
dict
结构体中使用rehashidx
记录 rehash 进度,每次对字典执行增删改查操作时,会顺带将rehashidx
指向的槽位上的所有键值对迁移到新哈希表,并将rehashidx
加 1。在此期间,字典同时使用新旧两个哈希表,查找操作会在两个表中都进行,直到rehashidx
等于-1
,表示 rehash 完成,释放旧哈希表。
缩容机制
- 触发条件:当哈希表的负载因子小于 0.1 时,会触发缩容。
- 缩容过程:分配一个大小为原哈希表
size
四分之一的新哈希表(如果size
小于 4 则不改变),重新计算所有键值对的哈希值和索引,将原哈希表中的所有键值对重新插入到新哈希表中,最后释放原哈希表的内存空间。同样采用渐进式 rehash 方式进行,与扩容类似。
对缓存系统动态变化需求的适应
- 动态数据量变化:扩容和缩容机制能够根据缓存中键值对数量的动态变化,自动调整哈希表的大小,保证负载因子在合理范围内,从而提高哈希表的查找效率。当缓存数据量增加时,及时扩容避免哈希冲突加剧;当数据量减少时,缩容节省内存空间。
- 避免性能抖动:渐进式 rehash 机制使得哈希表的调整过程分散在多次操作中,避免了一次性大量数据迁移对缓存系统性能造成的瞬间抖动,保证了缓存系统的稳定性和响应及时性,满足缓存系统对高性能和低延迟的要求。