創建高性能索引
(一)索引簡介
-
索引的定義
索引,在數據結構的查找那部分知識中有專門的定義。就是把關鍵字和它對應的記錄關聯起來的過程。索引由若干個索引項組成。每個索引項至少包含兩部分內容。關鍵字和關鍵字對應記錄在存儲器位置信息。索引是組織磁盤文件的一種重要的技術。
數據庫的數據量通常比較大,都是存儲在磁盤上。通過存儲引擎對磁盤文件的數據進行管理。而索引是存儲引擎御用快速找到記錄的一種數據結構。
2.索引的優點
(1)大大減少服務器需要掃描的數據量。
(2)索引可以幫助服務器避免排序和臨時表。
(3)索引可以將隨機IO轉換成順序IO。
3.索引三星系統原則
(1)第一星:索引將相關的記錄放在一起。即在一系列必要的列上建立索引,不必為where條件里的所有得列建立索引。
(2)第二星:索引中數據的順序和排序要求的數據的順序一致。通常將選擇性更高的列放在索引列的最前面。
(3)第三星:索引中的列包含查詢所需要的所有列。因為索引中已經包含查詢所需的全部字段,所以不需要在進行行查詢(覆蓋索引的定義)。
(二)索引類型
其中,索引是在存儲引擎層實現的。因此,不同的執行引擎,自己選擇使用實現自己的索引類型。
1.B-Tree 索引
(1)B+樹數據結構
B+樹的定義是在B樹的基礎之上定義的。相比B+樹,區別部分有兩點。
A.非葉子結點只存key,不存儲指向數據的指針(ROWID)。
B.葉子結點都保存一個指向相鄰節點的指針。
區別A.非葉子節點不存儲ROWID,索引項更小。一塊可以存儲更多的索引項。所以,可以存儲更多的非葉子索引項。一個索引項能定位更多的葉子結點。
區別B.葉子節點根據指針鏈接,范圍查詢非常簡單(如果是B樹結構的話,范圍查找需要 葉子節點和內部結點之間不停的往返)。
(2)使用B+樹索引的存儲引擎
InnoDB和MyISAM。
B+樹索引是邏輯結構是B+樹、物理存儲結構是鏈式存儲。
(3)B+樹索引適合的查詢類型
- 鍵值范圍
- 鍵前綴查找(只使用最左前綴查找)
- 只訪問索引的查詢(覆蓋索引)
- 全鍵值
其實是覆蓋索引。即索引中的字段能覆蓋查詢語句中的全部字段(包括分組、排序字段)。
2. 哈希索引
哈希索引是線性索引。是基於哈希表實現。只有精確匹配索引的所有列的查詢才有效。
哈希索引存儲在哈希表中。邏輯結構是線性表,物理存儲結構是順序存儲。
不同的key通過哈希函數計算,可能產生相同的結果。即沖突。哈希索引用的沖突解決算法是鏈地址法(索引相同的記錄指針放在一個鏈表中)
索引表是線性表的順序存儲。存儲在連續的存儲空間中。
(1).哈希索引的索引項包含哈希值和行指針。
哈希值:由索引列按照哈希函數計算,獲得的哈希值。
行指針:當前哈希值對應的行的指針。
(2).哈希索引特點
- 哈希索引只包含哈希值和行指針。不包含其它列信息。因此,無法避免讀取行(無法實現覆蓋索引)。
- 哈希索引的索引項不是按照索引值順序存儲的。所以,無法避免排序。
- 哈希索引不支持索引列的匹配查找。
3. 全文索引
全文索引和哈希索引一樣,也是一種線程索引。本質是倒序索引。
4.B+樹索引和哈希索引的區別
- 哈希索引的特殊性,索引的檢索可以一次定位。而B樹索引的檢索需要工根節點到樹枝結點,最后再到葉子結節點。這樣多次IO訪問。所以,哈希索引的效率遠高於B樹索引。
- 哈希索引無法避免排序(按照哈希碼順序存儲的,不是按照索引列進行順序存儲的)。B樹索引在特殊情況下是可以避免排序操作的(索引列作為索引key)。
- 哈希索引只能使用全部索引鍵來查詢,不能用部分索引鍵來查詢。(哈希函數是一種對應關系,所以,必須要所有的參數才能得到哈希碼,部分索引建是不可以的)
(三)高性能索引策略
1.獨立的列
索引列不能是表達式,也不能是函數。
例如:SELECT actor_id FROM actor WHERE actor_id+1=5 這種寫法,就算在actor_id上建立了索引,也不起效。索引列actor_id需要是獨立的列才可以。
2.多列索引
多列索引也叫符合索引。即同時對多個列建立索引。比如(A,B,C)。
(1). 需要使用多列索引的場景
- 服務器需要對多個索引進行相交運算(通常是AND條件)。
- 服務器需要對多個索引進行聯合操作(通常是OR條件)。
(2).多列索引的生效規則
比如(a,b,c),a,b,c都是排好序的。在任何一段a的下面b都是排好序的。在任何一段b的下面c都是排好序的。多列索引生效原則是從前向后依次生效。如果中間索引列沒有起作用,則該索引列之前的索引列起作用。
例如 (1)select * from mytable where a=3 and b>7 and c=3; --a用到了,b也用到了,c沒有用到,這個地方b是范圍值,也算斷點,只不過自身用到了索引。
3.聚簇索引
聚簇索引:不是一種索引類型,是一種數據存儲方式。
InnoDB的聚簇索引,在同一個結構中保存了B-Tree索引和數據行。
當表有聚簇索引的時候,它的數據實際上是存儲在索引的葉子頁(leaf page)中。數據只有一份,所以,一個表只有一個聚簇索引。聚簇是指鍵值相鄰的數據行緊簇的保存在一起。
聚簇索引的葉子節點也是數據節點。而非聚簇索引的葉子結點仍然是索引節點。只不過,它有指向對應數據塊的指針。
(1).聚簇索引和二級索引
-
聚簇索引的定義
在InnoDB中,聚簇索引就是主鍵索引。如果表中沒有定義主鍵,則InnoDB選擇一個唯一的非空索引作為主鍵。如果沒有這樣的索引,InnoDB會隱式的定義一個主鍵來做聚簇索引。
2.聚簇索引特征
數據存儲和索引放在一起,找到索引,就找到數據。
由於聚簇索引是將數據跟索引結構放到一塊,因此一個表僅有
二級索引定義
非主鍵索引就是二級索引。
二級索引特征
將數據存儲和索引存儲分開的結構,索引結構的葉子節點指向數據的對應行。
在InnoDB存儲引擎中,在聚簇索引之上創建的索引稱之為輔助索引。即二級索引。輔助索引總是需要二次查找的。輔助索引葉子結點存儲的不是行的物理位置,而是主鍵值。(然后根據主鍵值去聚簇索引中查詢數據)
(2).MyISAM和InnoDB的主鍵索引、二級索引對照圖
熟悉兩種引擎的數據和索引分布,就真正理解了MYSQL的存儲和索引查詢。
- MyISAM數據分布
如上圖所示,MyISAM的數據和索引分開儲存的。MyISAM是按照插入的順序,順序的存儲在磁盤上。類似數組那樣順序存儲。
2. MyISAM索引
它對應的不論主鍵索引還是二級索引,都是典型的B+樹索引形式。葉子結點對應的是行指針(行在磁盤中的具體位置)。
3. InnoDB數據分布
如上圖所示,InnoDB支持聚簇索引。其中,聚簇索引就是數據。在聚簇索引的葉子節點上,除了主鍵外,還有事務ID,回滾指針和其余非主鍵字段。所以,通過主鍵索引可以直接找到數據.
4. InnoDB主鍵索引
InnoDB的主鍵索引和數據分布內容一樣。主鍵索引就是其數據分布。
5.InnoDB二級索引
二級索引也是標准的B樹索引。只是葉子結點指的不是行指針,而是主鍵值。所以,整個葉子結點的索引項是[key+主鍵]。
6. 聚簇索引的選擇和重建
聚簇索引默認是按照主鍵建立的主鍵索引。如果沒有定義主鍵,InnoDB會選擇一個唯一的非空索引替代。
InnoDB的數據插入是按照主鍵順序插入行的。
4.覆蓋索引
如果一個索引包含(或者說覆蓋)查詢的所有字段(查詢字段和where條件字段)的值,我們稱之為覆蓋索引。【由此可見,覆蓋索引是索引的一個分類而已】
覆蓋索引效率高的幾個原因
- 覆蓋索引,只需要查找索引,不需要二次查找數據行。少一次操作。
- 覆蓋索引是按照索引字段順序存儲。因而,支持范圍查找。
5.使用索引掃描來排序
核心思路是:因為索引是有序的。如果排序要求的順序和索引的順序一致,就可以直接使用索引的順序。從而減少對排序的操作。
ORDER BY和查找型查詢的限制是一樣的:需要滿足索引的最左前綴原則,否則,MySQL無法使用索引排序。但有一個特殊情況:就是前導列為常量。例如,有一個索引為(A,B,C),那么這樣的SQL語句也會用索引排序。
select id from table where A=2 order by B,C;
第一個索引列A為常量2,2后面對應的B,C也是有序的。所以,這個查出的數據是有序的。
select id from table where A>2 order by B,C; 這個不可以。
(四)維護索引和表
索引如此重要,所以需要對索引和表進行實時維護。確保索引正常工作。
1.找到並修復損壞的表
檢查:通過CHECK TABLE來檢查引擎是否發生表損壞。
維修:使用 ALTER TABLE innoDB_tbl ENGINE = INNODB;
2.更新索引統計信息
ANALYZE TABLE
3.減少索引和數據碎片
數據存儲的碎片有
(1).行碎片
數據行被存在多個地方的多個片段中。
(2).行間碎片
邏輯上順序的也,或者行在磁盤上不是順序存儲的。行間碎片對全表掃描和聚簇索引掃描影響較大。
(3).剩余空間碎片
數據頁中有大量的剩余空間。從而導致服務器讀取大量不需要的數據。
修復: OPTIMIZE TABLE ;或者 ALTER TABLE innoDB_tbl ENGINE= <engine>;
(五)為什么 絕大多數索引選擇B+樹?
MYSQL查詢的本質是在一個數據集合中的查找數據。查找常見方式以及場景如下
1.順序查找,場景:無序,數據量較小。
2.折半(二分)查找,場景:順序線性表存儲。數據量較小。
3.二叉樹查找(AVL):場景:二叉樹或者平衡二叉樹存儲(有序鏈式存儲),數據量中等或者較小。
4.多路查找樹(B樹或B+樹):場景:B+樹存儲(有序存儲),可以處理數據量很大的數據。
因為B樹存儲可以讓樹的階(深度)控制在較小的分為內。階(深度)每少一層,查詢就會少一次索引的獲取。因而,B樹這個類型的存儲是MYSQL選擇索引數據結構的一個很好的方案。
為什么選擇B+樹而不是B樹?
- B+樹非葉子結點不存數據,所以,可以存儲更多的結點,因而,樹的階就越小。從樹形上來看越矮胖。可以較少IO操作。
- 葉子結點都添加一個指向相鄰葉子的指針。范圍掃描更容易。