MySQL版本: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));

}

可以看到这里实际上和你的键值和你hash的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;    /*

ulint    n_cells;/* number of cells in the hash table */

hash_cell_t*    array;    /*!< pointer to cell array */

mem_heap_t*    heap;

};

可以看到hash_cell_t* array;就是这样一个元素,他实际上就是hash_cell_t就是一个元素void*。

typedef struct hash_cell_struct{

void*    node;    /*!< hash chain node, NULL if none */

} hash_cell_t;

那么通过这个元素他能够指向具体的hash表了。那么user_str(用户自己的结构体)->array->node就指向了一个

具体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(mem_alloc(sizeof(hash_table_t)));//为hash表头分配内存

array = static_cast(

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表

就不详细解释了,当然我只是对基本实现和常用的方法进行了描述,其他方面遇到再说吧。

mysql 散列查询_MySQL InnoDB中hash查找表的实现相关推荐

  1. mysql多个子查询_mysql(5)多表--子查询

    商品分类==goods cate.商品品牌==brand name.是否上架==is_show.是否销售一空==is_saleoff [子查询--subquery] 子查询是指出现在其他sql语句内的 ...

  2. mysql gps数据查询_Mysql数据库中计算两GPS坐标的距离

    Mysql数据库中计算两GPS坐标的距离有两种方式: 1.直接使用SQL语句:#lat为纬度, lng为经度, 一定不要弄错 declare @lng1 float; declare @lat1 fl ...

  3. mysql 建表报错_mysql innodb 引擎 ,建表时报错:ERROR 1118 (42000)

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 mysql 5.7.20 当我在建表时,完整报错信息如下: ERROR 1118 (42000): Row size too large (> 81 ...

  4. mysql根据字长查询_MYSQL常用查命令

    MYSQL常用查命令: mysql> select version();        查看MySQL的版本号 mysql> select current_date();        查 ...

  5. mysql多列索引_MySQL的多列索引

    什么是索引? 索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存.如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录.表里面 ...

  6. mysql 内嵌查询_mysql嵌套查询_MySQL

    mysql嵌套查询_MySQL 一:创建ecs_goods表插入以下数据: +----------+------------------------------+--------+---------- ...

  7. mysql 在sql查询语句结果中,数字加千分逗号

    mysql 在sql查询语句结果中,数字加千分逗号 FORMAT(X,D) 将number X设置为格式 '#,###,###.##', 以四舍五入的方式保留到小数点后D位, 而返回结果为一个字符串. ...

  8. mysql到mysqli转变表_利用mysql和mysqli取得mysql的所有数据库和库中的所有表

    /*** mysqli  op mysql demo*//***公用函数*//***功能:列出一个数据库的所有表*@param$dbname*@param$con*/functionlist_tabl ...

  9. sql 查询目标数据库中所有的表以其关键信息

    1.查询目标库中的所有表 SELECT obj.name tablename, ---表名 schem.name schemname, ---表所属的方案 idx.rows, ---一共有几行数组 C ...

最新文章

  1. C++知识点1——基础
  2. All is about C!
  3. Adadelta原文解读
  4. SQL Server 2005中的分区表(一):什么是分区表?为什么要用分区表?如何创建分区表?...
  5. Bootstrap 工具提示插件Tooltip的方法
  6. Fiddler改包场景04——先拦截请求,修改请求,再拦截响应,修改响应,放行响应
  7. USACO 2.33 Zero Sum
  8. java注释html警告,Javadoc中允许的HTML标记
  9. 重要数据 | 数据分类和分级概念解析
  10. Android Protect-0.luyten+jadx+simplify简单介绍
  11. vs2010 vs2012 插件小番茄 visual assist x破解版下载
  12. 利用msf 对靶机metasploittable2 进行信息收集
  13. 台式计算机的电流是多少,电脑台式机一天耗电大概是多少
  14. 什么样男人必出轨 震惊!出轨男子名字中居然都有这字
  15. zxing换行_让文字自动换行
  16. CTF-Crypto必备自动解密神器-Ciphey
  17. 2-github术语介绍
  18. IP地址的规划和设计方法(一)
  19. Android UI动画 仿直播点赞飘心动画效果
  20. 记2018年北理计算机夏令营+机试

热门文章

  1. 不讲武德,拿到户口后立马辞职...
  2. Spring 官方发起Spring Authorization Server 项目
  3. Windows 7 下月停止支持,微软重申将全屏通知敦促用户升级系统
  4. 那些做中台的程序员,后来都怎么样了?
  5. 我最常用的Intellij IDEA快捷键
  6. java中执行cmd命令_如何通过Java执行cmd命令
  7. 设计模式之简介及其分类
  8. yolov5 多版本共存
  9. c2065 未声明的标识符 解决ok
  10. python操作gif