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函式的呢?很簡單如下:
上層呼叫為
可以看到這裡實際上和你的鍵值和你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中如下,我去掉了一些用處不大的:
可以看到hash_cell_t* array;就是這樣一個元素,他實際上就是hash_cell_t就是
一個元素void*。
那麼透過這個元素他能夠指向具體的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,這裡不用太多解釋
注意:下面都是透過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表
就不詳細解釋了,當然我只是對基本實現和常用的方法進行了描述,其他方面遇到再說吧。
作者微信:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2141627/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL 5.7 查詢InnoDB鎖表MySql
- MySQL 中 MyISAM 中的查詢為什麼比 InnoDB 快?MySql
- Access查詢實現Mysql的 limit 查詢MySqlMIT
- 教你幾招HASH表查詢的方法
- MySQL中MyISAM為什麼比InnoDB查詢快MySql
- MySQL 查詢所有表中的記錄數MySql
- 用hash cluster表提高查詢效能 (一)
- 關於innodb中查詢的定位方法
- mysql多表查詢如何實現MySql
- mysql實現隨機查詢MySql隨機
- MYSQL SQLServer分頁查詢的實現MySqlServer
- 一個hash表的實現
- MySQL單表查詢MySql
- mysql鎖表查詢MySql
- MySQL 單表查詢MySql
- innodb查詢鎖
- MySQL innodb 事務的實現MySql
- Mysql建表、索引、函式、查詢使用中的坑!!!MySql索引函式
- MySQL查詢資料庫中沒有主鍵的表MySql資料庫
- MYSQL INNODB 中通用雙向連結串列的實現MySql
- MySQL InnoDB表的限制MySql
- MySQL 合併查詢union 查詢出的行合併到一個表中MySql
- MySql 鎖表 查詢 命令MySql
- mysql表碎片的查詢自己回收MySql
- 查詢MySQL資料庫,MySQL表的大小MySql資料庫
- mysql 從一個表中查詢,插入到另一個表中MySql
- Mysql 實現樹狀遞迴查詢MySql遞迴
- mysql連表查詢出現資料重複MySql
- MySQL 查詢結果取交集的實現方法MySql
- MySQL 合併查詢join 查詢出的不同列合併到一個表中MySql
- hash 表在 go 語言中的實現Go
- MySQL中InnoDB儲存引擎的實現和執行原理MySql儲存引擎
- [MySQL] - 聯表查詢,查詢一個不在另一個表的記錄MySql
- mysql 三表關聯查詢MySql
- mysql三表關聯查詢MySql
- MySQL join連表查詢示例MySql
- mysql查詢表基礎資訊MySql
- MYSQL和INNODB分層實現MySql