【手写数据库内核组件】0201 哈希表hashtable的实战演练,多种非加密算法,hash桶的冲突处理,查找插入删除操作的代码实现

news2024/11/16 10:32:17

hash表原理与实战

专栏内容

  • postgresql使用入门基础
  • 手写数据库toadb
  • 并发编程

个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.

文章目录

  • hash表原理与实战
  • 一、概述
  • 二、hash表整体介绍
    • 2.1 hash表的应用场景
    • 2.2 整体架构
  • 三、hash算法选择
  • 四、hash表操作
    • 4.1 冲突处理
    • 4.2 查找操作
    • 4.3 插入操作
    • 4.4 删除操作
  • 五、总结
  • 结尾

一、概述


hash表的应用非常广泛,在网上也可以看到分享的各种hash表的实现,都比较概念化。

本章节从实战的角度出发,以数据库内核中的应用为例,来看看hash表的原理与实现。

二、hash表整体介绍


哈希算法(Hash)又称摘要算法,它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。

哈希算法最重要的特点就是:

  • 相同的输入一定得到相同的输出;
  • 不同的输入大概率得到不同的输出;

我们想利用hash算法的这一特性,将输入的一组数据,经过hash算法计算后,输出唯一的 32位或64位的整形值key。

当我们需要找到存储的数据时,通过这个key查找,而查找整型值的效率就很高了,可以用二分法进行查找。

这样一个存储数据的结构,我们叫它hash表,也就是通常说的key-value形式的存储,它的查找效率与数据的类型无关。

2.1 hash表的应用场景

hash表一般用于存储大量的数据,而数据的类型是字符串,或者更复杂的复合类型结构体,或者是更大的数据;

直接通过原始数据进行查找时,代价非常高,将它们转换为hash 值后,就可以通过恒定的效率进行查找。

在数据库中的应用有:

  • 数据块缓存,某个数据块是否已经在缓存中,通过对数据块编号的hash值进行查找;
  • 系统字典的查找,某个表是否已经创建了,通过表的hash值进行查找;
  • hash索引,记录数据的hash值,查找时按hash值进行查找;

2.2 整体架构

hash表的实现一般由几方面组成,hash算法,bucket计算,冲突处理,key-value对应形式,以及三种操作。

在这里插入图片描述

  • 既然是一个table,那么内部基本存储结构是一个数组,数组的最大元素个数就是capacity;
  • 数组中的每个元索叫做bucket桶,来存储key-value对数据;
  • bucket位置的计算,一般会采用 hash值 % capacity 来计算;hash值一般是一个32位,64位或者128位的整数,取余后得到数组中的下标,这就是当前key-value要存储的位置;

三、hash算法选择


查找主要依赖高效的hash值的计算,一个高效,碰撞少的算法,能让hashtable的效率大大提升。

常见的hash算法有,MD5, sha-256等,这些常用于加密,而hashtable并不需要对数据进行加密,更看重计算的效率。

由此出现了一些快速hash算法,比较有名的如:

  • murmurhash3, 这是第三个版本,速度公认的非常快,开源了各种语言实现;
  • Spookyhash,这个目前支持128位;
  • cityhash,是google发布的,会利用现代CPU的特性进行性能提升,对于低于64位的输入处理比较复杂;

建议使用murmurhash3,算法简单高效,对于较少的输入也能高效处理。

这些算法都可以在github上下载得到,加入.c,.h文件后就可以直接调用使用。

类似如下调用:

seed = 123456789
data = "example data"
hash_value = murmur_hash(seed, data)

四、hash表操作


hash表的操作一般有插入,查找,删除三类基本操作。

对于修改操作可以分解为这三项的组合,先查找,再删除,然后插入,因为修改后的键值发生变化,对于它在hash表中的位置也会发生变化。

4.1 冲突处理

在开始操作之前,需要注意一种情况,因为我们数组元素个数有限,在取余之后难免会出现多个key-value数据在相同位置的情况,也就是key产生了冲突。

一般有两种处理方式:

  • 一是在冲突位置往后继续找空位置存储;
  • 二是在当前桶内以链表的形式存储;

两种不同的冲突处理,对应了后面操作的不同。这里采用第二种方法,如果有多个相同数据在同一桶中时,以单链表的形式存储。

在这里插入图片描述

图中可以看到,出现冲突时,key4,key5直接追加到key1后面。

那么定义数组元素类型时,就要定义为链表形式。

typedef unsigned long long HASHKEY; 

typedef struct HashElement
{
    struct HashElement *link;
    HASHKEY             hashKey;
    char                *value;
}HashElement;

这里定义hash为64位的整形,当然可以是其它位数。

4.2 查找操作

查找一个key-value值是否在hashtable中的步骤如下:

  • 调用hash算法接口,计算value的hash值;
  • 按找hash值计算bucket位置;
  • 找到bucket,查看是否为空;
  • 如果bucket中有多个元素,遍历链表进行比对hash值;
  • 如果存在相同的hash值元素,则找到;否则没有找到。

获取hashkey函数

#define Hash_capacity 100
HashElement * hashtable[Hash_capacity];

HASHKEY getHashKey(char *value, int valueSize)
{
    return spooky_hash64(value, valueSize, 0);
}

获取bucket函数

int GetBucketIndex(HASHKEY key, PHashTableInfo hashTableInfo)
{
    int bucket = key & Hash_capacity;

    return bucket;
}

查找函数

HashElement* HashFindEntry(char *value)
{
    HashElement *entry = NULL;
    int bucket = 0;
    HASHKEY key = 0;

    key = getHashKey(value, strlen(value));
    bucket = GetBucketIndex(key);
    entry = GetHashEntryFromBucket(hashtable[bucket], key);

    return entry;
}

从bucket链中查找

HashElement* GetHashEntryFromBucket(HashElement* bucket, HASHKEY key)
{
    HashElement* element = bucket;

    while(element != NULL)
    {
        if(element->hashKey == key) 
        {
            return element;
        }

        element = element->link;
    }

    return NULL;
}

当然这里,除取比较key值外,还可以对value定义比较函数,这样避免hash值冲突的情况。

4.3 插入操作

插入操作就比较简单,步骤如下:

  • 计算hash 值;
  • 根据hash值获取bucket位置;
  • 存储对应bucket,如果已经有元素,存到链到头部;
HashElement* HashInsertEntry(char *value)
{
    HashElement *entry = NULL;
    int bucket = 0;
    HASHKEY key = 0;

    key = getHashKey(value, strlen(value));
    bucket = GetBucketIndex(key);

    entry = malloc(sizeof(HashElement));
    if(NULL == entry)
    {
      return NULL;
    }

    entry->link = NULL;
    entry->hashKey = key;
    entry->value = value;

    if(NULL != hashtable[bucket])
      entry->link = hashtable[bucket];
    
    hashtable[bucket] = entry;
    return entry;
}

hash节点数量不确定,故采用动态内存分配;

在冲突时采用了头插法,这样操作比较简单;

4.4 删除操作

从hash表中找到并删除一个元素的步骤如下:

  • 计算value的hash值;
  • 计算对应的bucket位置
  • 从bucket链中进行查找,同时记录下它的前继;
  • 将对应key的元素从链表中删除;注意链表只有一个元素的情况;
  • 将删除的元素返回,由调用者释放内存空间;
HashElement* DeleteHashEntry(char *value)
{
    HashElement *pre = NULL;
    HashElement* element = NULL;
    int bucket = 0;
    HASHKEY key = 0;

    key = getHashKey(value, strlen(value));
    bucket = GetBucketIndex(key);

    pre = element = hashtable[bucket];
    while(element != NULL)
    {
        if(element->hashKey == key) 
        {
            if(pre == element)
            {
              hashtable[bucket] = NULL;
            }
            else
            {
              pre->link = element->link;
            }
            return element;
        }

        pre = element;
        element = element->link;
    }
    return NULL;
}

五、总结


本文介绍了哈希表的实现及原理,同时介绍了几种hash计算方法。

当然本节介绍的内容,都是在没有并发冲突的情况下使用,如果多线程操作时,需要进行加锁处理。

如果需要更高效的并发场景下的hash表,后面章节会继续介绍。

结尾


非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!

作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1906245.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如果国产BI工具也有顶流,它们一定会上榜

在数据驱动的今天,商业智能(BI)工具已成为企业不可或缺的助手,它们通过强大的数据处理和分析能力,帮助企业洞察市场趋势,优化运营决策。如果BI工具界也有“顶流”,那么奥威BI、帆软BI&#xff0…

世优科技获新锐商业价值奖,数字人阿央入选北京市元宇宙“名人”

2024全球经济大会元宇宙创新发展论坛暨2024第九届“创客中国”元宇宙中小企业创新创业大赛,由工业和信息化部网络安全产业发展中心、北京市经济和信息化局、石景山区人民政府、首钢集团有限公司主办,围绕元宇宙底层技术端和产业应用端两个方向&#xff0…

ROS2 分布式 及 ssh远程控制 和 上传下载文件或文件夹

问题1. 多台计算机连接同一wifi后 ,运行ROS2的小乌龟案例,自己的计算机,无法控制其他电脑的小乌龟 按照正常的情况来说,ROS2是DDS的自发现通信机制,只要处在同一wifi网络中, A计算机执行启动小乌龟的命…

计算机网络-组播分发树与组播协议

一、组播分发树 前面我们大致了解了下组播的转发原理,通过RPF反向路径检查可以形成无环的组播转发路径,今天继续学习下组播分发树和组播协议。 组播数据转发需要保证转发路径无环,无次优路径且无重复包。通过RPF机制与组播路由协议&#xff0…

【撤稿资讯】国家杰青被撤稿23篇文章,主要原因图片重复使用等

本周投稿推荐 SCI • 能源科学类,1.5-2.0(来稿即录25天) • 计算机类,2.0-3.0(纯正刊29天录用) EI • 各领域沾边均可(2天录用) 知网 • 7天录用-检索(急录友好&a…

论文图片模糊怎么办?科研绘图小track解决你的困扰

论文图片模糊怎么办?科研绘图小track解决你的困扰 一、 使用draw.io 绘图二、使用在线压缩工具,尽可能的无损压缩(推荐迅捷图片转换器)三、当然你也可以用svg 一、 使用draw.io 绘图 网址:https://draw.io/ 解决方法: 加大图片的分…

延时双删两种实现对比分析

前言 延时双删(Delayed Double Deletion)是一种在分布式系统或缓存一致性处理中使用的技术,目的是确保缓存与数据库之间的数据一致性。它主要用于处理在高并发情况下,缓存和数据库可能出现的数据不一致问题。 常见更新策略的问题…

5分钟微课视频制作方法 微课录制后期制作方法

微课视频是一种短小精悍的在线教育视频形式,通常时长在5到10分钟左右,观众可以在短暂的时间内获取到有用的信息。微课视频的目的是通过简洁明了的内容,向观众传递特定的知识点或技能,它的特点在于紧凑、便于消化和分享&#xff0c…

ESP32CAM物联网教学10

ESP32CAM物联网教学10 MicroPython 应用体验 小智偶然地发现,有一种新兴的编程模式MicroPython,也能编写ESP32Cam的应用程序了,于是欣然地体验了一把。 编程环境搭建 小智偶然地从下面这家店铺买了一块ESP32Cam,并从客服那里得到…

【人工智能】-- 智能家居

个人主页:欢迎来到 Papicatch的博客 课设专栏 :学生成绩管理系统 专业知识专栏: 专业知识 文章目录 🍉引言 🍉基于深度卷积神经网络的表情识别 🍈流程图 🍈模型设计 🍍网络架…

安全防御(防火墙)

第二天: 1.恶意程序---一般会具有一下多个或则全部特点 1.非法性:你未经授权它自动运行或者自动下载的,这都属于非法的。那恶意程序一般它会具有这种特点, 2.隐蔽性:一般隐藏的会比较深,目的就是为了防止…

UML建模工具Draw.io简介

新书速览|《UML 2.5基础、建模与设计实践 Draw.io是一个非常出色的免费、开源、简洁、方便的绘图软件,利用这款软件可以绘制出生动有趣的图形,包括流程图、地图、网络架构图、UML用例图、流程图等。它支持各种快捷键,免费提供了1000多张画图…

80+ ChatGPT 文献综述指令

进行文献综述通常似乎是一项艰巨的任务。它是学术和研究工作的重要组成部分,涉及对先前发表的与特定主题相关的研究进行全面和批判性分析。目标是深入了解该主题的知识状况,找出差距,并为进一步研究奠定基础。 传统上,文献综述是…

GuLi商城-商品服务-API-品牌管理-OSS整合测试(续)

Spring Alibaba简介 Spring官网:https://spring.io/projects/spring-cloud-alibaba GitHub:https://github.com/alibaba/spring-cloud-alibaba GitHub中文文档:https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md…

气膜建筑如何在文化旅游行业中应用—轻空间

一、气膜建筑简介 气膜建筑是一种新型建筑形式,其主要结构由高强度膜材、空气支撑系统和固定系统组成。通过不断向膜体内部充气,使其形成稳定的内部压力来支撑整个建筑结构。气膜建筑因其建设速度快、成本相对较低、环保节能等优点,近年来在各…

怎么给电子文档批量盖骑缝章或公章?

怎么给电子文档批量盖骑缝章或公章?假如你有100个PDF电子文档要同时盖缝章,如果不借助专业的盖电子骑缝章软件,还真不好干。下面讲述如何利用e-章宝批量盖电子骑缝章。 1.在软件中导入待批量盖章的PDF文件 如下图,在“待盖章PDF文件”区域…

MySQL之表的约束(上)

目录 空属性(NULL) 实例建表 插入操作 默认值(default) 建表 插入操作 NULL与default的结合 列描述 建表 zerofill 建表 插入操作 主键 建表 插入 主键的增加与去掉 去掉 增加 复合主键 插入的影响 真正约束字段的是数据类型,但是数据类型约束很单一&a…

阶段三:项目开发---搭建项目前后端系统基础架构:任务13:实现基本的登录功能

任务描述 任务名称: 实现基本的登录功能 知识点: 了解前端Vue项目的基本执行过程 重 点: 构建项目的基本登陆功能 内 容: 通过实现项目的基本登录功能,来了解前端Vue项目的基本执行过程,并完成基…

【机器学习】属性降维:揭示数据的简化之美

🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​💫个人格言: "如无必要,勿增实体" 文章目录 属性降维:揭示数据的简化之美引言什么是属性降维?为何降…

(二)前端javascript中的数据结构之栈

栈是一种遵从后进先出(LIFO)原则的有序集合。新添加的或待删除的元素都保存在栈的 同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。 栈是限定仅在表的一端进行插入和删除操作…