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
相关推荐
鱼儿也有烦恼11 分钟前
Redis最新入门教程
数据库·redis·缓存
牛马程序小猿猴19 分钟前
17.thinkphp的分页功能
前端·数据库
我科绝伦(Huanhuan Zhou)25 分钟前
Oracle免费认证来袭
数据库·oracle
@CLoudbays_Martin111 小时前
CF后台如何设置TCP 和 UDP 端口?
大数据·运维·服务器·网络·数据库
步、步、为营2 小时前
.NET中使用HTTPS全解析
数据库·https·.net
漠月瑾-西安2 小时前
信创背景下的分布式数据库备份难题及解决之道
数据库·分布式·信创·数据备份
风象南2 小时前
Redis中6种缓存更新策略
redis·后端
码码哈哈0.02 小时前
2025最新:3分钟使用Docker快速部署Redis集群
redis·docker·容器
神仙别闹2 小时前
基于QT(C++)实现(图形界面)校园导览系统
数据库·c++·qt
三流搬砖艺术家2 小时前
Windows 下 MongoDB 安装指南
数据库·mongodb