MySQL數據庫——自適應哈希索引,全文索引

1、自適應哈希索引

InnoDB 引擎下的主鍵索引是 b+樹 索引,b + 樹的特點是非葉子節點只存儲主鍵索引值,葉子節點既存儲了索引值,還存儲了對應的行記錄,所以數據都在葉子節點,而 InnoDB 是用頁爲單位去存儲 b + 樹的節點,一頁存一個節點,故一個節點可以存很多條索引信息,樹的高度一般最多也就在 3 ~ 4 層,精準查詢的話,會有 3 ~ 4 次查詢。

數據庫裏存儲的數據非常多,被訪問頻繁非常高的數據被稱爲熱點數據,針對熱點數據,InnoDB 做了一個性能的提升,就是採用了自適應哈希索引,爲熱點頁建立一個哈希表,哈希表的表項對應着該頁的每條記錄,當我們精準查詢時,會根據查詢的條件值計算出哈希,以時間複雜度 O(1) 就能找到哈希項,哈希項又指向了一條記錄,即找到了數據。自適應哈希索引說的是 InnoDB 自己內部自動創建的,跟我們用戶沒有關係,是它內部爲了優化而創建的一種結構,判斷一個頁是否爲熱點頁,可能是個什麼算法,如果某個頁晉升爲熱點頁,自然會爲它創建自適應哈希索引,如果某個頁降級了,不再是熱點頁,就會刪除掉對應的哈希表。

優點:提升了查詢性能。

缺點:只適合等值查詢(=, IN),併發性不行,因爲哈希表是有鎖機制的,把整個表鎖起來(這是舊版本的)。

我們可以設置是否開啓自適應哈希索引,innodb_adaptive_hash_index 默認是開啓。 innodb_adaptive_hash_index_parts 參數爲新版本的,用於將哈希表分爲多個子表,這樣,可以分別對子表進行加鎖,大大提升併發性,默認爲 8, 最大爲 512。

2、倒排索引

InnoDB 引擎的全文檢索需要倒排索引,將我們將某一列設置成 fulltext key ,就可以對這一列進行全文檢索,原因是數據庫對這一列創建了額外的倒排索引,有兩種形式。

第一種形式。索引存儲的是 { 詞,所在的該條記錄標識(主鍵)}。

第二種形式。索引存儲的是 { 詞,所在的該條記錄標識,詞在該條記錄中的該字段裏的位置 }。

所以在進行全文檢索時,是根據這個倒排索引去查詢的,找到哪些記錄包含了要檢索的詞,甚至詞所處的位置。

全文檢索就是基於倒排索引的,這個倒排索引也是非常大的,所以動態維護起來也是不容易的,如果沒修改一次,那就要維護一次倒排索引,那還是很難受的。因此,InnoDB 給出了一個全文檢索索引緩存,當需要對倒排索引進行修改維護時,將修改先緩存起來,等某個時機(緩存滿了等等)再一次性進行更新修改。 

我們可以設置全局參數 innodb_ft_cache_size 來設置全文檢索索引緩存的大小,越大,當然性能也越好,當然也不宜太大,因爲一旦內存宕機,緩存裏的數據就沒有了,InnoDB 需要更多的時間去回覆。

全文索引是有限制要求的:1、每張表只能有一個全文索引。 2、聯合全文索引包含的列必須使用相同的字符集和排序規則。  3、不支持沒有單詞界定符的語言,如 中文,日文,韓文等。

3、全文檢索

要想對某個字段使用全文檢索,就要爲該字段創建全文索引,FULLTEXT KEY。

3.1、全文搜索 NATURAL LANGUAGE 模式 

NATURAL LANGUAGE MODEL 是默認的模式(自然語言模型)。

舉個例子就明白了,SELECT * FROM student WHERE MATCH(interest) AGAINST('football' IN NATURAL LANGUAGE MODE);    查詢student表裏興趣 interest 裏面包含有 football 單詞的記錄。

也可以直接寫成, SELECT * FROM student WHERE MATCH(interest) AGAINST('football'); 

檢索出來如果有多條記錄,這些記錄的順序是怎麼樣的呢? 按照一定的相關性,相關性計算規則是:

1、關鍵詞是否出現在文檔中。

2、關鍵詞出現在文檔中的次數。

3、關鍵詞在索引列中的數量。

4、多少個文檔包含指定的關鍵詞

3.2、聯合全文索引

不僅可以把一列作爲全文索引,而且還可以將多列作爲聯合全文索引,舉個例子你就明白了。student 表,name 字段 和 interest 字段聯合設置一個全文索引。

SELECT * FROM `student` WHERE MATCH(interest) AGAINST('football');  這樣運行會報錯的, 錯誤信息:Can't find FULLTEXT index matching the column list。

爲什麼報錯,因爲你都將2列作爲一個索引了,所以要將2列看成一個整體,不能夠再單獨地檢索一列了。

SELECT * FROM `student` WHERE MATCH(`name`, interest) AGAINST('kkk football'); 這樣就可以,運行結果如下:

再說明一下,AGAINST() 裏的內容是以詞爲單位的,詞只包含字母和數字,除此之外的字符都被認爲是分割符,比如 kkk football 被認爲是兩個詞,因爲中間有一個空格, kkk&football 也被認爲是兩個詞,因爲中間有個&字符。AGAINST() 裏面的內容沒有順序之分,比如 MATCH(`name`, interest) AGAINST('kkk football'); 這句話的意思是,name 和 interest 字段中只要包含了kkk football 其中的一個關鍵詞都行,name 字段包含了 football 也行,name 字段包含了 kkk 也行,name 字段什麼都包含,但是 interest 字段包含了關鍵詞也行。

3.3、全文檢索 BOOLEAN 模式

自然語言模式只是單純檢索是否含有關鍵詞,功能單一,BOOLEAN 模式的功能更加豐富。

+ 表示該詞必須被包含。

- 表示該詞必須不被包含。

(no operator) 表示該詞可被包含,也可不被包含,被好含的話,相關性更高。

@distance 表示檢索的詞之間必須在間隔多少個字符以內。

> 表示出現該詞時增加相關性。

< 表示出現該詞時降低相關性。

~ 表示出現該詞時,相關性直接爲負。

* 表示以該詞開頭的單詞。

" 表示短語。"" 擴起來的內容看成一個整體,看成一個詞,不可拆分。

舉例說明,還是以 student 表爲例:

SELECT * FROM `student` WHERE MATCH(`name`, interest) AGAINST('+kkk +football' IN BOOLEAN MODE);  這句執行結果爲空,因爲既包含kkk ,又包含 football 的記錄不存在。

SELECT * FROM `student` WHERE MATCH(`name`, interest) AGAINST('-kkk +football' IN BOOLEAN MODE);  這句執行結果如下,不包含 kkk,包含 football 的記錄有兩條。

SELECT * FROM `student` WHERE MATCH(`name`, interest) AGAINST('(no operator)sing dance' IN BOOLEAN MODE); 這句執行結果如下,包含了 sing 的記錄相關性更高,排在前面。

至於 @distance 用法,我測試了一下,感覺有點問題,希望知道這個知識點的大佬能夠講解一下,謝謝。

SELECT * FROM `student` WHERE MATCH(`name`, interest) AGAINST('>sing dance' IN BOOLEAN MODE); 這句執行結果如下,包含了 sing 的記錄相關性增加,因此,排在了前面。

SELECT * FROM `student` WHERE MATCH(`name`, interest) AGAINST('<sing dance' IN BOOLEAN MODE);  結果如下,因爲包含 sing 的記錄被降低了相關性,因此,排在了後面。

SELECT * FROM `student` WHERE MATCH(`name`, interest) AGAINST('si*' IN BOOLEAN MODE); 結果如下, si 開頭的記錄有。 我不建議這樣模糊匹配,因爲全文檢索的模糊查詢也會導致全表掃描,這是我自己的理解,如果不正確,請在評論給出正確的原理,非常感謝

SELECT * FROM `student` WHERE MATCH(`name`, interest) AGAINST('"sing dance"' IN BOOLEAN MODE); 這句的執行結果爲空,因爲 sing dance 爲一個整體。

3.4、擴展檢索

擴展檢索 是對 全文檢索 的一個擴展,什麼是擴展檢索呢?比如你全文檢索一個詞 “鼠標”,但是也許不止是鼠標呢?也許你還想檢索出來一點和鼠標有關的信息呢?比如桌墊,電腦等等,你用擴展檢索就能辦到。

擴展檢索原理:第一次使用全文檢索,查詢出包含 “鼠標” 的文本,然後將查詢出這些文本進行分詞,第二次再將這些詞進行全文檢索。 等於是在第一次全文檢索的基礎上再進行一次全文檢索。

語法(舉例說明):SELECT * FROM student WHERE MATCH(interest) AGAINST('football' IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION);  

                      或者 :SELECT * FROM student WHERE MATCH(interest) AGAINST('football' WITH QUERY EXPANSION); 

WITH QUERY EXPANSION 只能用於自然語言模式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章