MYSQL INNODB中hash查找表的实现
发表于:2025-11-10 作者:千家信息网编辑
千家信息网最后更新 2025年11月10日,原创有误请指出:版本:5.7.14源码位置为hash0hash.h hash0hash.cc作为一种时间复杂度最优为O(1)的数据结构,但是最坏时间复杂对位O(n)的一种数据结构,但是在良好的设计ha
千家信息网最后更新 2025年11月10日MYSQL INNODB中hash查找表的实现原创有误请指出:
版本:5.7.14
源码位置为hash0hash.h hash0hash.cc
作为一种时间复杂度最优为O(1)的数据结构,但是最坏时间复杂对位O(n)的一种数据结构,但是在
良好的设计hash函数的情况下性能还是非常好的。关于hash表的图在最后给出。在innodb中各种数据
结构都使用hash表查找比如LOCK_T结构,还有我们特别熟悉的自适应hash索引等等,下面我们进行一些
探讨。
一、innodb hash函数
首先我们不得不研究一下innodb的hash函数,hash函数的设计至少有2个要求
1、计算简单,否则如果计算花费了太多时间你的hash查找表也是不成功的
2、计算能够尽可能的分散值
那么innodb是如何设计这个hash函数的呢?很简单如下:
cells(桶数量)进行取模操作,非常简单实用。
二、处理冲突
hash表避免不了冲突,而数据库中往往也利用这一点,将多个链表合并起来,innodb当然也就采用了
链表的方式来处理冲突。那么言外之意每一个数据结构中必须包含一个如普通链表中 data_struct* next
的指针,当然这里也可以用void*泛型指针,我们来看看lock_t结构体中:
hash_node_t hash; /*!< hash chain node for a record lock */
确实如此。这也是单项链表实现的基础。
三、HASH表头
一个hash表当然需要一个hash表头这个表头指向了具体的cell 数组(内存相似但在heap空间不再栈上),
innodb中如下,我去掉了一些用处不大的:
一个元素void*。
具体cell的地址了,后面的只是地址指针++就可以了。那么我们user_str也至少包含这样一个
hash_table_t*的指针来指向整个hash表,确实如此在innodb lock_sys_t中包含了
hash_table_t* rec_hash
那么我们可以lock_sys_t和lock_t为列子画一张展示图如下:

四、hash表的建立
这里主要涉及到cell的计算,计算函数为ut_find_prime,这里不用太多解释
注意:下面都是通过LOCK部分hash表的实现来注释的,其他其实也是一样的。
五、插入一个元素
这部分是通过宏定义来做的如下,我写了详细的解释
这部分也是通过宏定义来做的如下,我写了详细的解释
其他函数还包含:
HASH_SEARCH_ALL:宏实现在整个hash表中查找一个元素,相当于真个cell个链表查找
HASH_SEARCH:宏实现在有建值的情况下查找一个元素、言外之意cell(桶)确定了,相当于链表查找
hash_table_clear: 清空一个hash表
就不详细解释了,当然我只是对基本实现和常用的方法进行了描述,其他方面遇到再说吧。
作者微信:

版本:5.7.14
源码位置为hash0hash.h hash0hash.cc
作为一种时间复杂度最优为O(1)的数据结构,但是最坏时间复杂对位O(n)的一种数据结构,但是在
良好的设计hash函数的情况下性能还是非常好的。关于hash表的图在最后给出。在innodb中各种数据
结构都使用hash表查找比如LOCK_T结构,还有我们特别熟悉的自适应hash索引等等,下面我们进行一些
探讨。
一、innodb hash函数
首先我们不得不研究一下innodb的hash函数,hash函数的设计至少有2个要求
1、计算简单,否则如果计算花费了太多时间你的hash查找表也是不成功的
2、计算能够尽可能的分散值
那么innodb是如何设计这个hash函数的呢?很简单如下:
点击(此处)折叠或打开
- ulint
- ut_hash_ulint(
- /*==========*/
- ulint key, /*!< in: value to be hashed */
- ulint table_size) /*!< in: hash table size */
- {
- ut_ad(table_size);
- key = key ^ UT_HASH_RANDOM_MASK2;
- return(key % table_size);
- }
点击(此处)折叠或打开
- ulint
- hash_calc_hash(
- /*===========*/
- ulint fold, /*!< in: folded value */
- hash_table_t* table) /*!< in: hash table */
- {
- ut_ad(table);
- ut_ad(table->magic_n == HASH_TABLE_MAGIC_N);
- return(ut_hash_ulint(fold, table->n_cells));
- }
cells(桶数量)进行取模操作,非常简单实用。
二、处理冲突
hash表避免不了冲突,而数据库中往往也利用这一点,将多个链表合并起来,innodb当然也就采用了
链表的方式来处理冲突。那么言外之意每一个数据结构中必须包含一个如普通链表中 data_struct* next
的指针,当然这里也可以用void*泛型指针,我们来看看lock_t结构体中:
hash_node_t hash; /*!< hash chain node for a record lock */
确实如此。这也是单项链表实现的基础。
三、HASH表头
一个hash表当然需要一个hash表头这个表头指向了具体的cell 数组(内存相似但在heap空间不再栈上),
innodb中如下,我去掉了一些用处不大的:
点击(此处)折叠或打开
- struct hash_table_t {
- enum hash_table_sync_t type; /*<! type of hash_table. */
- ulint n_cells;/* number of cells in the hash table */
- hash_cell_t* array; /*!< pointer to cell array */
- mem_heap_t* heap;
- };
一个元素void*。
点击(此处)折叠或打开
- typedef struct hash_cell_struct{
- void* node; /*!< hash chain node, NULL if none */
- } hash_cell_t;
具体cell的地址了,后面的只是地址指针++就可以了。那么我们user_str也至少包含这样一个
hash_table_t*的指针来指向整个hash表,确实如此在innodb lock_sys_t中包含了
hash_table_t* rec_hash
那么我们可以lock_sys_t和lock_t为列子画一张展示图如下:

四、hash表的建立
这里主要涉及到cell的计算,计算函数为ut_find_prime,这里不用太多解释
点击(此处)折叠或打开
- hash_create(
- /*========*/
- ulint n) /*!< in: number of array cells */
- {
- hash_cell_t* array;
- ulint prime;
- hash_table_t* table;
- prime = ut_find_prime(n);//计算cell桶的数量
- table = static_cast<hash_table_t*>(mem_alloc(sizeof(hash_table_t)));//为hash表头分配内存
- array = static_cast<hash_cell_t*>(
- ut_malloc(sizeof(hash_cell_t) * prime));//为hash表分配内存
- /* The default type of hash_table is HASH_TABLE_SYNC_NONE i.e.:
- the caller is responsible for access control to the table. */
- table->type = HASH_TABLE_SYNC_NONE;
- table->array = array;//hash表头指向hash表
- table->n_cells = prime;//设置
- table->heap = NULL;
- ut_d(table->magic_n = HASH_TABLE_MAGIC_N);
- /* Initialize the cell array */
- hash_table_clear(table); //memset 0x00整个hash表
- return(table);
- }
注意:下面都是通过LOCK部分hash表的实现来注释的,其他其实也是一样的。
五、插入一个元素
这部分是通过宏定义来做的如下,我写了详细的解释
点击(此处)折叠或打开
- /*******************************************************************//**
- Inserts a struct to a hash table. */
- /*
- HASH_INSERT(lock_t, hash, lock_sys->rec_hash,lock_rec_fold(space, page_no), lock);
- TYPE=lock_t:代表数据类型
- NAME=hash:代表lock_t下面有一个hash元素指针,其实这个指针和我们平时用的链表的struct* NEXT没什么区别
- 唯一区别就是他是void*的
- (hash_node_t hash;
- typedef void* hash_node_t;)
- TABLE=lock_sys->rec_hash:代表hash表的地址指针,输入参数
- (hash_table_t* rec_hash;)
- FOLD=lock_rec_fold(space, page_no):函数lock_rec_fold通过表空间和页号得到一个unsigned long数字
- DATA=lock:这实际上就是你的数据的指针,当然这里就是lock_t* 输入参数
- */
- #define HASH_INSERT(TYPE, NAME, TABLE, FOLD, DATA)\
- do {\
- hash_cell_t* cell3333;\//实际上就是void*
- TYPE* struct3333;\ //lock_t* struct3333;
- \
- HASH_ASSERT_OWN(TABLE, FOLD)\//断言不考虑
- \
- (DATA)->NAME = NULL;\//lock->hash = NULL;
- \
- cell3333 = hash_get_nth_cell(TABLE, hash_calc_hash(FOLD, TABLE));\
- \
- if (cell3333->node == NULL) {\ //如果为NULL没有元素挂载到这个cell下
- cell3333->node = DATA;\ //则我们挂载到这个cell下
- } else {\
- struct3333 = (TYPE*) cell3333->node;\ //否则说明有元素了取到这个元素的指针 lock_t* struct3333 = (lock_t*)cell3333->node;
- \
- while (struct3333->NAME != NULL) {\ //如果struct3333->hash 不等于NULL 说明他下面有元素了
- \
- struct3333 = (TYPE*) struct3333->NAME;\ //那么我们需要做的是指针像链表下一个元素移动
- }\
- \
- struct3333->NAME = DATA;\ //最后找到链表末尾 将数据节点挂载到下面 struct3333->hash = lock(lock是lock_t*)
- }\
- } while (0)
这部分也是通过宏定义来做的如下,我写了详细的解释
点击(此处)折叠或打开
- /*******************************************************************//**
- Deletes a struct from a hash table. */
- /*
- 有了上面基础也就比较简单了,这里直接在代码进行注释
- HASH_DELETE(lock_t, hash, lock_sys->rec_hash,lock_rec_fold(space, page_no), in_lock);
- */
- #define HASH_DELETE(TYPE, NAME, TABLE, FOLD, DATA)\
- do {\
- hash_cell_t* cell3333;\//实际上就是void*
- TYPE* struct3333;\ //lock_t* struct3333;
- \
- HASH_ASSERT_OWN(TABLE, FOLD)\//断言不考虑
- \
- cell3333 = hash_get_nth_cell(TABLE, hash_calc_hash(FOLD, TABLE));\//通过函数hash_get_nth_cell计算这个值在哪个cell也就是hash 桶中
- \
- if (cell3333->node == DATA) {\ //地址比较,如果地址相同其地址必然相同
- HASH_ASSERT_VALID(DATA->NAME);\//断言不考虑
- cell3333->node = DATA->NAME;\//如果找到 将指针移动到下一个元素 言外之意这里去掉了一个内存单元就是找到的那个
- } else {\
- struct3333 = (TYPE*) cell3333->node;\ //链表循环找
- \
- while (struct3333->NAME != DATA) {\
- \
- struct3333 = (TYPE*) struct3333->NAME;\
- ut_a(struct3333);\
- }\
- \
- struct3333->NAME = DATA->NAME;\ //最终找到 就做 链表去掉这个内存元素动作
- }\
- //最终这里涉及到一个问题就是释放问题,但是注意虽然这个数据的指针在链表中去掉了,但是指针本身还在,可以拿到做free即可
- HASH_INVALIDATE(DATA, NAME);\ //debug版本使用不考虑
- } while (0)
其他函数还包含:
HASH_SEARCH_ALL:宏实现在整个hash表中查找一个元素,相当于真个cell个链表查找
HASH_SEARCH:宏实现在有建值的情况下查找一个元素、言外之意cell(桶)确定了,相当于链表查找
hash_table_clear: 清空一个hash表
就不详细解释了,当然我只是对基本实现和常用的方法进行了描述,其他方面遇到再说吧。
作者微信:

元素
指针
就是
函数
数据
结构
地址
内存
实际
实际上
指向
表头
数据结构
解释
言外之意
代表
数量
时间
冲突
设计
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
cs不能在安全服务器
公共网络安全卡通图片
毛嗑瓜服务器地址
tracker服务器连接异常怎么解决
好视通连接到服务器失败怎么回事
html搜索查询数据库
公司如何获取软件开发订单
沈阳明哲网络技术有限公司
vfp怎么创建数据库
软件开发过程中最关键步骤
重庆邮电大学软件开发
太原的软件开发培训
网络安全管理制度范本免费下载
网络技术价格表格
山东监控服务器机柜虚拟主机
服务器通常工作领域
服务器上网文件管理器
医院管理系统软件开发项目
数据库应用技术海南联盟
2008服务器管理器
对象数据怎样存入数据库
yii 数据库主从
BMJ数据库检索规则
数据库讲义
数据库审计产品对比
网络安全专升本
开展网络安全教育的的活动目
如何判断一个软件开发工具
福建综合软件开发
北京交易软件开发