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
相关推荐
禁默8 分钟前
基于金仓KFS工具,破解多数据并存,浙人医改造实战医疗信创
数据库·人工智能·金仓数据库
pen-ai26 分钟前
【数据工程】15. Stream Query Processing
数据库
it码喽1 小时前
Redis存储经纬度信息
数据库
小马哥编程1 小时前
【软件架构】数据库系统与缓存设计:五种缓存一致性方案
数据库·缓存
DemonAvenger2 小时前
Redis持久化策略对比:RDB与AOF的最佳实践与场景选择
数据库·redis·性能优化
新手小白*2 小时前
Redis Sentinel哨兵集群
数据库·redis·sentinel
一 乐2 小时前
商城推荐系统|基于SprinBoot+vue的商城推荐系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·商城推荐系统
羑悻的小杀马特2 小时前
从零搭建群晖私有影音库:NasTool自动化追剧全流程拆解与远程访问协议优化实践
运维·数据库·自动化
TDengine (老段)5 小时前
杨凌美畅用 TDengine 时序数据库,支撑 500 条产线 2 年历史数据追溯
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
Le1Yu5 小时前
redis主从集群及其原理(优化)
redis