Redis 的哈希 hash是什么?

Redis 的哈希 hash是什么?

重要内容

在 Redis 中,哈希(Hash)是一种键值对的集合,其中每个键 对应的值是多对字段 - 值(field - value)的映射

如:Key → { Field1: Value1, Field2: Value2, ... }

  • 内部表示 :哈希本质上是一个二维的数据结构,一个键对应多个字段,每个字段关联一个值
  • 操作特性:可以独立地对每个字段进行操作,而不需要对整个哈希进行操作。这使得哈希非常适合存储对象,因为对象的每个属性可以作为一个字段

扩展知识

Hash 常用命令

命令 简述 使用
HSET 设置哈希表中字段的值 HSET key field value
HGET 获取字段值 HGET key field
HGETALL 获取所有字段和值 HGETALL key
HDEL 删除指定字段 HDEL key field

Hash 底层实现解析

Hash是一种数据基础数据结构,类似于数据结构中的哈希表,一个哈希可以存储2的32次方-1个键值对

底层结构需要分成两个情况

  • Redis6及之前,Hash的底层是压缩列表加上哈希表的数据结构(ziplist+hashtable)
  • Redis7之后,Hash的底层是紧凑列表加上哈希表的数据结构(Listpack+hashtable)

压缩列表和紧凑列表的比较

两者时间复杂度都是O(n),其主要区别就在于 listpack 解决了 ziplist 的级联更新问题

紧凑列表、压缩列表 与 哈希表转换的条件

哈希键的字段个数(默认512)以及每个字段名和字段值的长度(默认64)

注意:在使用hashtable结构之后,就不会再退化成ziplist或listpack,之后都是使用hashtable进行存储

Hashtable 底层数据结构

Redis 的 hashtable 通过字典(dict)实现,字典包含两个哈希表(dictht)和渐进式 rehash 机制

字典的结构

c 复制代码
typedef struct dict {
    dictType *type;        // 类型特定函数(哈希计算、键值复制等)
    void *privdata;        // 私有数据
    dictht ht[2];          // 两个哈希表(用于渐进式 rehash)
    long rehashidx;        // rehash 进度索引(-1 表示未进行)
    unsigned long iterators; // 当前运行的迭代器数量
} dict;

哈希表的结构

c 复制代码
typedef struct dictht {
    //哈希表数组
    dictEntry **table;
    //哈希表大小
    unsigned long size;  
    //哈希表大小掩码,用于计算索引值
    unsigned long sizemask;
    //该哈希表已有的节点数量
    unsigned long used;
} dictht;

哈希节点的结构

c 复制代码
typedef struct dictEntry {
    //键值对中的键
    void *key;
  
    //键值对中的值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
          	
    //指向下一个哈希表节点,形成链表
    struct dictEntry *next;
} dictEntry;

结构图

通过图片能够完整的展示 hashtable 内部的数据结构

哈希表的核心实现机制

哈希函数与索引计算
  • 哈希函数 :Redis 使用 MurmurHash2 算法(非加密型哈希)将键(key)转换为 64 位或 32 位哈希值

  • 索引计算

    c 复制代码
    index = hash & d->ht[table].sizemask; // 等价于 hash % size

    通过位掩码(sizemask)将哈希值映射到哈希表数组的索引位置

  • 示例:假设哈希表的大小为 8,哈希值为 101

    • 位掩码为 7(即 8−1)
    • 计算索引:index = 101 & 7 = 5
    • 因此,键 101 被映射到索引 5
负载因子与扩容/缩容
  • 负载因子 : used(已用节点数)/ size(桶总数)
  • 扩容触发条件
    • 负载因子 >= 1,这个时候说明空间非常紧张,新数据是在哈希节点的链表上找到的,这个时候如果服务器没有执行 RDB快照或者AOF重写这两个持久化机制的时候,就会进行rehash操作
    • 当负载因子 >= 5,这个时候说明哈希冲突非常严重了,这个时候无论有没有进行AOF重写或者RDB快照,都会强制执行rehash操作
  • 缩容触发条件
    • used < size / 10(负载因子 < 0.1)时,缩容至 used 最近的 2 的幂

渐进式 Rehash 机制

为避免一次性迁移大量键值对导致服务阻塞,Redis 采用 渐进式 rehash,将迁移分摊到多次操作中

Rehash 流程

  1. 准备阶段
    • 分配新哈希表 ht[1],大小为满足扩容/缩容条件的 2 的幂
    • 设置 rehashidx = 0,表示开始从索引 0 迁移数据
  2. 迁移阶段
    • 每次操作触发迁移 :在增删改查操作中,每次迁移 ht[0].table[rehashidx] 桶内的所有节点到 ht[1]
    • 更新索引rehashidx++,直至所有桶迁移完成
  3. 完成阶段
    • 释放 ht[0] 的内存,将 ht[1] 设置为 ht[0]
    • 重置 ht[1] 为空哈希表,设置 rehashidx = -1
相关推荐
Leo.yuan3 分钟前
实时数据仓库是什么?数据仓库设计怎么做?
大数据·数据库·数据仓库·数据分析·spark
艾露z3 分钟前
深度解析Mysql中MVCC的工作机制
java·数据库·后端·mysql
gs8014019 分钟前
[闭源saas选项]Pinecone:为向量数据库而生的实时语义搜索引擎
数据库·搜索引擎
大熊猫侯佩1 小时前
使用令牌(Token)进一步优化 SwiftData 2.0 中历史记录追踪(History Trace)的使用
数据库·swift·apple
网安小张1 小时前
解锁FastAPI与MongoDB聚合管道的性能奥秘
数据库·python·django
老兵发新帖2 小时前
Flyway
数据库
薛晓刚2 小时前
一次Oracle的非正常关闭
数据库
文牧之2 小时前
Oracle 的 SEC_CASE_SENSITIVE_LOGON 参数
运维·数据库·oracle
平行绳2 小时前
零基础玩转 Coze 数据库,看这篇就够了!
数据库·人工智能·coze