mysql 隐藏中间四位_MySQL知识体系——索引
本文直切主題,針對InnoDB引擎描述索引及優(yōu)化策略。在開始之前,需要讀者了解:
索引初探
要了解索引,當(dāng)然要了解其數(shù)據(jù)結(jié)構(gòu)。樹有很多應(yīng)用,流行的用法之一是包括UNIX和DOS在內(nèi)的許多常用操作系統(tǒng)中的目錄結(jié)構(gòu),二叉查找樹又是Java中兩種集合類TreeSet和TreeMap實(shí)現(xiàn)的基礎(chǔ)。那么對于數(shù)據(jù)庫,I/O是其性能瓶頸所在,減少樹的深度是直接有效的,BTree和B+Tree應(yīng)運(yùn)而生。
BTree和B+Tree(Balance-Tree,多路搜索樹,非二叉)
BTree
BTree是一種查找樹,如同二叉查找樹,紅黑樹等,都是為提高查找效率而產(chǎn)生的,BTree也是如此,可以把它看做二叉查找樹的優(yōu)化升級。二叉查找樹的特點(diǎn)是每個非葉節(jié)點(diǎn)都最多只有兩個子節(jié)點(diǎn),但是當(dāng)數(shù)據(jù)量非常大時,二叉查找樹的深度過深,搜索算法自根節(jié)點(diǎn)向下搜索時,需要訪問的節(jié)點(diǎn)也就變的相當(dāng)多。如果這些節(jié)點(diǎn)存儲在外存儲器(磁盤)中,每訪問一個節(jié)點(diǎn),相當(dāng)于就是進(jìn)行了一次I/O操作,隨著樹高度的增加,頻繁的I/O操作一定會降低查詢的效率。BTree改二叉為多叉,每個節(jié)點(diǎn)存儲更多的指針信息,以此達(dá)到減少樹的深度、降低I/O操作數(shù)。
使用BTree結(jié)構(gòu)可以顯著減少定位記錄時所經(jīng)歷的中間過程,從而加快存取速度。
定義(對于一個m階BTree)
- 根節(jié)點(diǎn)至少有兩個子節(jié)點(diǎn)(除非根結(jié)點(diǎn)為葉節(jié)點(diǎn))
- 每個節(jié)點(diǎn)有m-1個關(guān)鍵字,并且以升序排列
- 位于 m-1和m 關(guān)鍵字的子節(jié)點(diǎn)的值位于 m-1和m 關(guān)鍵字對應(yīng)的值之間
- 其它節(jié)點(diǎn)至少有m/2個子節(jié)點(diǎn)
特性
- 關(guān)鍵字集合分布在整棵樹中;
- 任何一個關(guān)鍵字出現(xiàn)且只出現(xiàn)在一個節(jié)點(diǎn)中;
- 搜索有可能在非葉節(jié)點(diǎn)結(jié)束;
- 其搜索性能等價于在關(guān)鍵字全集內(nèi)做一次二分查找;
- 自動層次控制。
B+Tree
InnoDB 存儲引擎在絕大多數(shù)情況下使用B+Tree建立索引,B+Tree也是關(guān)系型數(shù)據(jù)庫中最為常用和有效的索引結(jié)構(gòu),但是B+Tree索引并不能找到一個給定鍵對應(yīng)的具體值,它只能找到數(shù)據(jù)行對應(yīng)的頁,然后正如上一節(jié)所提到的,數(shù)據(jù)庫把整個頁讀入到內(nèi)存中,并在內(nèi)存中查找具體的數(shù)據(jù)行。
定義(其定義基本與 BTree同,除了:)
- 所有葉節(jié)點(diǎn)之間都有一個鏈指針;
- 所有關(guān)鍵字都在葉子結(jié)點(diǎn)出現(xiàn);
- 非葉子節(jié)點(diǎn)只存儲鍵值信息,數(shù)據(jù)記錄都存放在葉節(jié)點(diǎn)中。
特性
- 單節(jié)點(diǎn)可以存儲更多的元素,使得查詢磁盤IO次數(shù)更少,更加高效的單元素查找;
- 所有查詢都要查找到葉子節(jié)點(diǎn),查詢性能穩(wěn)定;
- 葉子節(jié)點(diǎn)會包含所有的關(guān)鍵字,以及指向數(shù)據(jù)記錄的指針,并且葉子節(jié)點(diǎn)本身是根據(jù)關(guān)鍵字的大小從小到大順序鏈接,范圍查找性能更優(yōu)。
區(qū)別
B+Tree是BTree的一種變形樹,它與BTree的差異在于:
- B+Tree只有達(dá)到葉子結(jié)點(diǎn)才命中(BTree可以在非葉子結(jié)點(diǎn)命中),其性能也等價于在關(guān)鍵字全集做一次二分查找;
- BTree樹每個葉子節(jié)點(diǎn)都有雙向指針;
- BTree分支節(jié)點(diǎn)和葉節(jié)點(diǎn)均保存記錄的關(guān)鍵碼和記錄的指針;B+Tree分支節(jié)點(diǎn)只保存記錄關(guān)鍵碼的復(fù)制,無記錄指針。所有記錄都集中在葉節(jié)點(diǎn)一層,并且葉節(jié)點(diǎn)可以構(gòu)成一維線性表,便于連續(xù)訪問和范圍查詢。
聚集索引和輔助索引
數(shù)據(jù)庫中的 B+Tree索引可以分為聚集索引(clustered index)和輔助索引(secondary index),它們之間的最大區(qū)別就是,聚集索引中存放著一條行記錄的全部信息,而輔助索引中只包含索引列和一個用于查找對應(yīng)行記錄的“書簽”。即在數(shù)據(jù)庫的聚集索引中,葉子節(jié)點(diǎn)直接包含衛(wèi)星數(shù)據(jù)。在輔助索引(NonClustered Index)中,葉節(jié)點(diǎn)帶有指向衛(wèi)星數(shù)據(jù)的指針。
聚集索引
InnoDB使用了聚集索引存儲數(shù)據(jù)。
與非聚集索引的區(qū)別則是,聚集索引既存儲了索引,也存儲了行值。當(dāng)一個表有一個聚集索引,它的數(shù)據(jù)是存儲在索引的葉子頁(leaf pages)上的。因此可以說InnoDB是基于索引的表。
當(dāng)我們使用聚集索引對表中的數(shù)據(jù)進(jìn)行檢索時,可以直接獲得聚集索引所對應(yīng)的整條行記錄數(shù)據(jù)所在的頁,不需要進(jìn)行第二次操作。
索引的建立規(guī)則
- 如果一個主鍵被定義了,那么這個主鍵就是作為聚集索引
- 如果沒有主鍵被定義,那么該表的第一個唯一非空索引被作為聚集索引
- 如果沒有主鍵也沒有合適的唯一索引,那么InnoDB內(nèi)部會生成一個隱藏的主鍵作為聚集索引,這個隱藏的主鍵是一個6個字節(jié)的列,改列的值會隨著數(shù)據(jù)的插入自增
輔助索引
輔助索引,也叫做非聚集索引,葉節(jié)點(diǎn)不包含行的全部數(shù)據(jù)。除了包含關(guān)鍵字外,還包含了一個標(biāo)記,這個標(biāo)記用來告訴InnoDB引擎從哪里可以找到與索引相對應(yīng)的行數(shù)據(jù)。由于InnoDB引擎是索引組織表,因此,這個標(biāo)記就是相應(yīng)的行數(shù)據(jù)的聚集索引關(guān)鍵字。
輔助索引的存在并不影響數(shù)據(jù)在聚集索引中的組織,因此一個表可以有多個輔助索引。
使用輔助索引查找一條表記錄的過程:通過輔助索引查找到對應(yīng)的關(guān)鍵字,最后在聚集索引中使用關(guān)鍵字獲取對應(yīng)的行記錄,這也是通常情況下行記錄的查找方式。
使用建議
聚集索引的優(yōu)先選擇列
不建議的聚集索引列
規(guī)范與建議
回表
先了解一個概念,MySQL對 WHERE 中條件的處理,根據(jù)索引使用情況分成三種:index key, index filter, table filter
1. index key
用于確定SQL查詢在索引中的連續(xù)范圍(起始范圍+結(jié)束范圍)的查詢條件,被稱之為Index Key。由于一個范圍,至少包含一個起始與一個終止,因此Index Key也被拆分為Index First Key和Index Last Key,分別用于定位索引查找的起始,以及索引查詢的終止條件。
2. index filter
在使用 index key 確定了起始范圍和介紹范圍之后,在此范圍之內(nèi),還有一些記錄不符合 WHERE 條件,如果這些條件可以使用索引進(jìn)行過濾,那么就是 index filter。
3. table filter
WHERE 中的條件不能使用索引進(jìn)行處理的,只能訪問table,進(jìn)行條件過濾了。
從普通索引查出主鍵索引,然后查詢出數(shù)據(jù)的過程叫做回表?;乇硪淮尉蜁?zhí)行一次查詢,所以避免回表是減少數(shù)據(jù)庫壓力、提高效率的有效手段。在InnoDB中,使用聯(lián)合索引配合主鍵索引可以直接返回結(jié)果而不需要回表查詢。
聯(lián)合索引(復(fù)合索引)與前綴索引(最左原則)
Mysql從左到右的使用索引中的字段,一個查詢可以只使用索引中的一部份,但只能是最左側(cè)部分。例如索引是(a,b,c),可以支持 a | a,b | a,b,c 3種組合進(jìn)行查找,但不支持 b,c 進(jìn)行查找。這是最左原則的第一層意思:聯(lián)合索引的多個字段中,只有當(dāng)查詢條件為聯(lián)合索引的第一個字段時,索引才會有效。
條件 WHERE a LIKE 'perfix%'; 索引也會有效。這是最左原則的第二層意思:根據(jù)字段值最左若干個字符進(jìn)行的模糊查詢,索引有效。
覆蓋索引
覆蓋索引是對聯(lián)合索引的合理利用。
比如 SELECT a, b FROM table WHERE a = 'wangnima'; ,如果我們已經(jīng)創(chuàng)建了(a)或(a,b)的聯(lián)合索引,那么這條語句會直接從索引返回而不會發(fā)生回表。即創(chuàng)建索引的字段覆蓋了查詢字段。
如果執(zhí)行 SELECT c FROM table WHERE a = 'wangnima'; ,就會發(fā)生回表,因為我們的輔助索引樹中,沒有字段 c 的數(shù)據(jù),需要拿到主鍵索引的關(guān)鍵字,去主鍵索引中回表查詢。
但是需要注意的是,索引雖好不可濫用。
索引下推(Index Condition Pushdown (ICP))
結(jié)合在 回表 概念中引出的三種索引使用情況(index key, index filter, table filter),ICP 技術(shù),就是 index filter 技術(shù)。MySQL的架構(gòu)分為服務(wù)器層和引擎層。
官方解釋(https://dev.mysql.com/doc/refman/5.6/en/index-condition-pushdown-optimization.html)
索引條件下推(ICP)是對MySQL使用索引從表中檢索行的情況的優(yōu)化。如果沒有ICP,存儲引擎將遍歷索引以定位基表中的行,并將它們返回到MySQL服務(wù)器,該服務(wù)器將計算基表行的where條件。在啟用ICP的情況下,如果部分where條件可以通過只使用索引中的列來計算,MySQL服務(wù)器會把where條件的這部分 推入 存儲引擎。然后,存儲引擎通過使用索引條目來評估所推送的索引條件,并且只有在滿足該條件時才從表中讀取行。ICP可以減少存儲引擎必須訪問基本表的次數(shù)和MySQL服務(wù)器必須訪問存儲引擎的次數(shù)。
根據(jù)官方的指導(dǎo),我們來做個驗證:
EXPLAIN SELECT * FROM people WHERE zipcode='95054' AND lastname LIKE '%lao%' AND address LIKE '%Main Street%';官方解釋:
EXPLAIN使用“索引條件下推”時,輸出顯示 Using index condition在 Extra列中。
假設(shè)一個表包含有關(guān)人員及其地址的信息,并且該表的索引定義為 INDEX (zipcode, lastname, firstname)。如果我們知道一個人的zipcode價值但不確定姓氏,我們可以這樣搜索:
SELECT * FROM people WHERE zipcode='95054' AND lastname LIKE '%etrunia%' AND address LIKE '%Main Street%';MySQL可以使用索引來掃描人 zipcode='95054'。第二部分(lastname LIKE '%etrunia%')不能用于限制必須掃描的行數(shù),因此如果沒有Index Condition Pushdown,此查詢必須為所有擁有的人檢索完整的表行 zipcode='95054'。
使用索引條件下推,MySQL lastname LIKE '%etrunia%'在讀取整個表行之前檢查該 部分。這樣可以避免讀取與索引元組相對應(yīng)的完整行,這些行匹配 zipcode條件而不是 lastname條件。
默認(rèn)情況下啟用索引條件下推??梢詏ptimizer_switch通過設(shè)置index_condition_pushdown標(biāo)志來控制 系統(tǒng)變量 :
SET optimizer_switch = 'index_condition_pushdown=off'; SET optimizer_switch = 'index_condition_pushdown=on';實(shí)踐
*注意語句中的“[ ··· ]”中括號指代變量,書寫時記得去掉
普通索引
這是最基本的索引,它沒有任何限制。它有以下幾種創(chuàng)建方式:
1. 創(chuàng)建索引
CREATE INDEX indexName ON mytable(username(length));如果不是字符類型的字段,如int,則不要指定length;如果是CHAR,VARCHAR類型,length可以不指定,也可以小于字段實(shí)際長度;如果是BLOB和TEXT類型,必須指定 length。
2. 修改表結(jié)構(gòu)(添加索引)
ALTER table tableName ADD INDEX indexName(columnName)3. 創(chuàng)建表的時候直接指定
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, INDEX [indexName] (username(length)) );唯一索引
它與前面的普通索引類似,不同的就是:索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。它有以下幾種創(chuàng)建方式:
1. 創(chuàng)建索引
CREATE UNIQUE INDEX indexName ON mytable(username(length))2. 修改表結(jié)構(gòu)
ALTER table mytable ADD UNIQUE [indexName] (username(length))3. 創(chuàng)建表的時候直接指定
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, UNIQUE [indexName] (username(length)) );刪除索引的語法
DROP INDEX [indexName] ON mytable;總結(jié)
合理利用索引對于提升數(shù)據(jù)庫的性能、減輕數(shù)據(jù)庫服務(wù)器的負(fù)擔(dān)是最直接有效的手段。
其實(shí),索引的本質(zhì)就是通過縮小范圍、把隨機(jī)事件變成順序事件來篩選出最終結(jié)果,同時可以總是用同一種查找方式來定位數(shù)據(jù),這樣就可以兼顧高效率和穩(wěn)定性。
總結(jié)
以上是生活随笔為你收集整理的mysql 隐藏中间四位_MySQL知识体系——索引的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电商常用同义词库_【福利】不可错过的电商
- 下一篇: mysql数据库中的int类型_MySQ