《MySQL技術內幕三》-InnoDB-索引與B+樹算法

《MySQL技術內幕-InnoDB存儲引擎》學習筆記三


2019-07-06 ╮(╯▽╰)╭ 撿起來,繼續學習

第5章 索引和算法

InnoDB存儲引擎索引的概述:

InnoDB存儲引擎,支持的常見索引:B+樹索引(常用的),全文索引,哈希索引(無法干預,自動的)。
所以最常用的就是B+樹索引了,此索引並不能給定一個鍵值直接找到具體行,而是隻能找到行所在的頁,然後讀取頁數據進行查找的。


索引的數據結構與狀態簡述

二叉樹 B+樹 的介紹

**二叉樹:**就是兩個叉叉的樹結構,依據大小分叉。對應的算法是二分查找法。具體是怎麼弄的,略過。

**平衡二叉樹【AVL樹】:**就是上面的這個二叉樹做一下平衡而來的。【定義是,所有節點的兩個子樹的高度差最大爲1】平衡二叉樹的查找性能比較高,但不一定是最高的,只是維護的成本很高,每次操作都要左右旋來保證平衡。

**B+樹:**也是一種的平衡查找樹,簡單定義是:數據都在同一層葉子節點上(節點間雙向連接,數據至少兩條),非葉子節點的層記錄的是葉子節點的起始值和指針地址,並且都是按順序存放的。對於具體的定義,還是Google吧。

B+索引樹

在InnoDB存儲引擎中,數據存儲就是以B+樹的形式存在的,所以數據即索引,高度一般在2~4層。(雖然理論上無限)

然而在這裏一顆B+樹一般能存多少數據呢?

這裏,InnoDB最小的存儲單位爲頁,一個頁的大小是16K。(也就是B+樹的節點大小了)

這裏我們先假設B+樹高爲2,即存在一個根節點和若干個葉子節點,那麼這棵B+樹的存放總記錄數爲:根節點指針數*單個葉子節點記錄行數。
所以:單個葉子節點(頁)中的記錄數=16K/1K=16。(這裏假設一行記錄的數據大小爲1k,實際上現在很多互聯網業務數據記錄大小通常就是1K左右)。

接下來就是算算非葉子節點能存放多少指針地址了?
假設主鍵ID爲bigint類型,長度爲8字節,而指針大小在InnoDB源碼中設置爲6字節,這樣一共14字節,我們一個頁中能存放多少這樣的單元,其實就代表有多少指針,即16384/14=1170。那麼可以算出一棵高度爲2的B+樹,能存放1170*16=18720條這樣的數據記錄。

根據同樣的原理我們可以算出一個高度爲3的B+樹可以存放:1170 * 1170 * 16=21902400條這樣的記錄。

而在InnoDB中B+樹高度一般爲2-4層,它就能滿足千萬級的數據存儲。在查找數據時一次頁的查找代表一次IO,所以通過主鍵索引查詢通常只需要2-4次IO操作即可查找到數據。

聚集索引和輔助索引

InnoDB中的B+樹索引可以分爲:聚集索引和輔助索引,他們都是B+樹的,不過聚集索引的葉子節點存放的是完整的數據,而輔助索引存放的則是書籤(指向主鍵索引主鍵的)。

索引作爲一個B+樹的存儲結構,是邏輯上連續,物理存儲上不連續的。

這裏說明一下使用輔助索引的過程:(聚集索引就是直接就找到對應頁了)
使用輔助索引查找數據的時候,InnoDB存儲引擎會遍歷輔助索引並通過葉級別的指針獲取到指向主鍵索引的主鍵,然後再通過主鍵索引來找到一個完整的行記錄。所以對於一個3層聚集索引+3層輔助索引的表,在使用輔助索引查找數據的時候,會進行6次的邏輯IO訪問。

簡單索引管理語法

直接看代碼就好了:

--第一種:
CREATE <索引名> ON <表名> (<列名> [<長度>] [ ASC | DESC])
--第二種:
ALTER TABLE + 
ADD INDEX [<索引名>] [<索引類型>] (<列名>,)    -- 表示在修改表的同時爲該表添加索引。
ADD PRIMARY KEY [<索引類型>] (<列名>,)  --表示在修改表的同時爲該表添加主鍵。
ADD UNIQUE [ INDEX | KEY] [<索引名>] [<索引類型>] (<列名>,)  --表示在修改表的同時爲該表添加唯一性索引。
ADD FOREIGN KEY [<索引名>] (<列名>,)  --表示在修改表的同時爲該表添加外鍵。
-- 其他的各種的參數 數據庫默認的就好  不用管的 默認就很好的了

查看索引情況:

SHOW INDEX FROM <表名> [ FROM <數據庫名>]
-- 需要注意的值:Cardinality 表示的是索引中的唯一值的數量 【與數據量越接近越好】
關於Cardinality

這個表示的是索引中的唯一值的數量 ,對於索引來說Cardinality / count(1) = 1是最佳的(就是唯一索引的),越接近越好,就是屬於高選擇性【一次選擇,可以定位到最小的數據量】,如果這個值很小,辣麼可以考慮刪除這個索引,因爲沒啥用了。

這個值的計算與更新:

此值是數據庫 隨機獲取8個數據頁進行統計的,所以值並不是準確值,【有參數可以配置採集數量,還是不用去調整的好】。
因爲這個值並不是實時更新的,但是查詢優化的時候會參考這個值進行使用索引,所以,在非高峯期的時候,可以手動的去更新一下此值:ANALYZE TABLE TABLE_NAME


B+樹索引的使用

講了一些類型的索引,但是沒有具體太多的操作技巧,都是理論說明。

聯合索引

就是使用的索引是多個字段的,如ALTER TABLE test ADD KEY (P_1,p_2),這種情況下,第一層數據結構是對p_1排序的,在相同P_1的情況下,是裏面的P_2是排序的。

這裏的索引也是B+樹,存在的key是以(P_1,P_2)這種存在的,大小的比較是先P_1,在P_2的。所以這裏,(1,2)>(2,200)的。如果是3+個列的索引也是如此的。

所以,select * from test where p_1 = 1 order by p_2 desc limit 100 這種的語句會使用這個聯合索引,因爲這個索引中已經對p_2排序了的。

二對於3+個列的聯合索引,排序都只是一級對一級的 ,例如:

ALTER TABLE test ADD KEY (P_1,p_2,P_3);
select * from test where p_1 = xxx order by p_2;  			-- 這個是可以直接通過聯合索引的
select * from test where p_1=xxx and p_2 = xxx order by p_3   -- 這個是可以直接通過聯合索引的
select * from test where p_1=xxx   order by p_3 			-- 這裏是不行的,P_1與P_3沒有排序關係的
覆蓋索引

這個東東,就是如果primary key id 的情況下,在添加一個 key id 的普通輔助索引。

因爲聚集主鍵索引,存的是整條數據,而輔助索引存的只是指針,存儲大小明顯小很多的。

所以在不查詢數據的語句如:count(1)這種的情況下,優化器會選擇輔助索引來處理的。

其他優化項

強制使用索引:FORCE INDEX

select * from test FORCE INDEX (索引名) where ........

MRR優化和ICP優化:

查詢方法:
select @@optimizer_switch 
配置參數:
index_condition_pushdown=on,   
mrr=on,mrr_cost_based=on,   -- 第一個:開啓,第二個:是否通過cost based 方式選擇(自動選擇),
--默認就是這樣子的 

然後解釋一下這個兩個東東:

MMR:Multi-Range Read 優化,目的是爲了減少磁盤的隨機訪問,大概的操作:就是會對查出來的輔助索引中的主鍵值先排序在緩存中,然後再順序的訪問聚集索引。

ICP:Index Conition Pushdown:對於多條件的查詢,會在索引取出數據的同時進行數據篩選過濾。

哈希表: 這個就忽略吧,反正是控制不了的東西╮(╯_╰)╭


全文索引

簡單說明介紹:

先來說一下全文檢索(Full-Text Search),這個是對信息中的任意內容查找出來。比較像:select * from blog where content like '%xxx單詞/詞語%';這個樣子的查詢。真正的使用不是這個樣子的。

全文檢索通常使用的倒排索引來實現的,也是一個B+樹索引,然後存的key是每一個單詞/分詞,vaule爲存在的位置(文檔id【inverted file index】,或者文檔id+具體位置【full inverted index】)。

InnoDB 全文檢索

採用的是full invertedj index的方式,從MySQL 5.7.6開始,MySQL提供了一個內置的全文ngram解析器,支持中文,日文和韓文(CJK) 。官方說明:https://dev.mysql.com/doc/refman/5.7/en/fulltext-search.html

在InnoDB中,爲提高全文檢索並行性能,存在6張的Auxiliary Table(輔助表),並且還有一個FTS Index Cache的全文檢索索引緩存。(FTS Index Cache 默認大小是32M,可以用innodb_ft_cache_size來調整)

弄個簡單的代碼說明一些東東:

--創建表:
CREATE TABLE fts_a(
    -- 下面這個字段的名字 和 類型是固定的,如果不手動添加,系統好像是會自己去生成的
	FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL,   
    body TEXT,
    PRIMART KEY(FTS_DOC_ID)
);
--創建索引
CREATE FULLTEXT INDEX idx_fts ON fts_a(body);
--查看分詞信息
SET GLOBAL innodb_ft_aux_table = 'test/fts_a';
select * from information_schema.INNODB_FT_INDEX_TABLE;
--手動刪除索引 (因爲實際DML操作實際並不會刪除索引數據,會在deleted表插入記錄)
SET GLOBAL innodb_optimize_fulltext_only=1;
OPTIMIZE TABLE fts_a;    --會重組表數據和索引的物理存儲

具體使用說明

語法:
MATCH (col1,col2,...) AGAINST (expr [search_modifier])
search_modifier:
  {
       IN NATURAL LANGUAGE MODE    -- 這個是默認值  
     | IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION
     | IN BOOLEAN MODE
     | WITH QUERY EXPANSION
  }
自然語言全文搜索:(默認)
SELECT * FROM fts_a
        WHERE MATCH (body)
        AGAINST ('xxxxxxx' IN NATURAL LANGUAGE MODE);
--因爲是默認參數 所以等同於
SELECT * FROM fts_a  WHERE MATCH (body) AGAINST ('xxxxxxx');
-- 統計結果數量
select count(*) FROM fts_a  WHERE MATCH (body) AGAINST ('xxxxxxx');
select count(if(MATCH (body) AGAINST ('xxxxxxx'),1,null)) as count  FROM fts_a ; --這個速度會更快
--查看數據權重
select *MATCH (body) AGAINST ('xxxxxxx') as Relevance  FROM fts_a ;
布爾全文搜索:(IN BOOLEAN MODE)

使用此修飾符,某些字符在搜索字符串中的單詞的開頭或結尾處具有特殊含義。

來個栗子看看,再做說明:

SELECT * FROM fts_a WHERE MATCH (body)
        AGAINST ('+Pease -hot' IN BOOLEAN MODE);
-- 表示的意思是必須有‘Pease’,但沒有‘hot’的數據

可以看到,這種的語法有點像正則的說,不過並沒有辣麼複雜,規則沒有辣麼多。

支持的運算符(規則):

  • + 表示必須存在
  • - 表示必須排除
  • (no operator) 默認情況下表示可選,但如果出現,相關性會更高
  • @distance 此運算符InnoDB僅適用於表。它測試兩個或多個單詞是否都在相互指定的距離內開始,用單詞測量。 例如,在運算符之前的雙引號字符串中指定搜索詞@distanceMATCH(col1) AGAINST('"word1 word2 word3" @8' IN BOOLEAN MODE)
  • > 表示出現單詞時增加相關性
  • < 表示出現單詞時降低相關性
  • ~ 表示允許出現,出現時負相關性
  • * 表示任意字符 如:lik* 表示已lik開頭的單詞
  • " 表示短語? 例如, "test phrase"匹配"test, phrase"。兩個分詞或者關係。

這些規則的使用,就瞭解一下就可以了,如果需要使用的時候再細看使用。簡單的使用的話,應該沒有太大的難度,最多就多調調咯╮(╯_╰)╭

擴展全文搜索:(WITH QUERY EXPANSION)

這個東西怎麼說明呢,很難描述啊!!

栗子吧,不好說清楚的東東:

MATCH (body)  AGAINST ('database');
-- ↑ 上面正常全文搜索的話 會出現所有 body 中有 database 的數據行
MATCH (body)  AGAINST ('database' WITH QUERY EXPANSION );
-- ↑ 開啓擴展之後  查詢到的就不止是 database  了  ,還會包括 Mysql DB2 等等關於數據庫的 行

這裏的意思就是除了查詢與關鍵分詞一模一樣的以外,還需要查詢出隱含關聯的信息【比如這裏的database,數據庫關聯的信息就是各種數據庫:mysql ,DB2 等等的數據庫】

但是這裏我開始完全不知道這個是怎麼擴展的,中文的意思他是怎麼關聯呢?

並且,這個功能比較好損性能,還是不使用的好 罒ω罒


小結一下

關於索引,算法方面其實只要瞭解一下二叉樹和B+樹是個什麼東東,並且主要的查詢特性就可以了,其他的交給MySQL本身的去處理就可以了。

而對於索引帶來性能優化:

  • 在常用的查詢位置加一個複合索引
  • 對常用的表的ID主鍵再建立一個索引【方便管理平臺之類分頁的查詢進行count(1) 】
  • 而關於強制索引一般是用不上的,除非某一個大表並且有特定大量查詢,除此之外的話還是相信MySQL本身的優化器吧,(:з」∠)
  • 全文索引,這裏基本上只是簡單的使用,【這裏無法評價,因爲有專門的搜索引擎:solr 、ElasticSearch 、Lucene;這些我都只是聽過名字,還是技術盲區。】【有對這塊瞭解大神可以簡單的介紹評論一下的 Thanks♪(・ω・)ノ】

恩,(⊙o⊙)… 就這些了,這一篇的關係Mysql索引的內容就醬紫了。。。。。。。。


2019-07-15 小杭

這本書的內容,接下來的是:鎖、事務、備份、優化的內容了。

ヾ(◍°∇°◍)ノ゙ 感覺都是用不上的理論知識,讓自己看一些數據的時候想的更深一點【雖然沒毛線用處】
在這裏插入圖片描述

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