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
相关推荐
小白勇闯网安圈16 分钟前
Django Form 组件详解:从表单生成到 ModelForm 数据校验
数据库·django·sqlite
梦想不只是梦与想1 小时前
MySQL 数据库(二):数据类型
数据库·mysql·数据类型
野生技术架构师1 小时前
Redis 和 MySQL 如何保证数据一致性?先更新数据库还是先删缓存,延迟双删、MQ、Canal 一次讲透
数据库·redis·缓存
Devin~Y1 小时前
从内容社区到AI智能客服:Spring Boot + Spring Cloud + Spring AI 全栈实战面试拆解
java·spring boot·redis·elasticsearch·spring cloud·kafka·mybatis
鸽芷咕1 小时前
SQL Server数据迁移到金仓数据库复盘:从怕性能翻车,到TPS提升60%
数据库
2602_959960921 小时前
电商大厂Java面试:从Spring Boot、JPA、微服务到Redis、Kafka、Spring Security与监控,谢飞机的爆笑三轮问答
java·jvm·spring boot·redis·面试题
HAYDENR2 小时前
数据库如何做性能优化?数据库性能调优有哪些常见注意事项?
数据库·性能优化
Light Gao2 小时前
企业级灰度发布技术方案
网络·数据库·oracle
一水2 小时前
AI 时代审查思维:审查第一篇
java·jvm·数据库·spring
赵广陆2 小时前
企业实战:Milvues向量数据库实践
数据库·pycharm·langchain