MYSQL INNODB中hash查找表的实现
发表于:2024-09-23 作者:千家信息网编辑
千家信息网最后更新 2024年09月23日,原创有误请指出:版本:5.7.14源码位置为hash0hash.h hash0hash.cc作为一种时间复杂度最优为O(1)的数据结构,但是最坏时间复杂对位O(n)的一种数据结构,但是在良好的设计ha
千家信息网最后更新 2024年09月23日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安全错误
数据库的锁怎样保障安全
彩皇网络技术有限公司
软件开发收入确认分录
我国网络安全 问题
大学生网络安全周宣传资料
软件开发方法常用的
9母根服务器
网络安全警示成语
dayz服务器夜晚设置
武汉大学网络安全待遇
华为服务器显示心跳线
对网络安全的看法和总结
修改服务器的值
软件开发公司的成本如何做账
怎样关闭电脑无线网络安全
冒险岛单机版数据库
网络安全人才缺口温州
网络技术信息咨询合同范本
北京乐呗互联网科技有限公司
网络技术专业技能考啥
2008数据库的注册表
云南定制软件开发费用
ftp服务器云资源管理器
网络资源管理服务器
诸暨网络技术需求排名
网络安全自查情况一般怎么填写
郧西公司软件开发服务保障
特斯拉软件开发团队
软件开发报价差异大
数据库公有云
闵行区优势软件开发加工厂