溫故知新-Mysql索引結構&頁&聚集索引&非聚集索



摘要

第一部分主要從數據結構及算法理論層面討論MySQL數據庫索引的數理基礎。
第二部分結合MySQL數據庫中MyISAM和InnoDB數據存儲引擎中索引的架構實現討論聚集索引、非聚集索引及覆蓋索引等話題。

索引

索引概述

MySQL官方對索引的定義爲:索引(index)是幫助MySQL高效獲取數據的數據結構(有序)。在數據之外,數據庫系統還維護者滿足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據, 這樣就可以在這些數據結構上實現高級查找算法,這種數據結構就是索引。

索引優勢劣勢

  • 優勢
    • 類似於書籍的目錄索引,提高數據檢索的效率,降低數據庫的IO成本。
    • 通過索引列對數據進行排序,降低數據排序的成本,降低CPU的消耗。
  • 劣勢
    • 實際上索引也是一張表,該表中保存了主鍵與索引字段,並指向實體類的記錄,所以索引列也是要佔用空間的。
    • 雖然索引大大提高了查詢效率,同時卻也降低更新表的速度,如對錶進行INSERT、UPDATE、DELETE。因爲更新表時,MySQL 不僅要保存數據,還要保存一下索引文件每次更新添加了索引列的字段,都會調整因爲更新所帶來的鍵值變化後的索引信息

索引結構

索引是在MySQL的存儲引擎層中實現的,而不是在服務器層實現的。所以每種存儲引擎的索引都不一定完全相同,也不是所有的存儲引擎都支持所有的索引類型的。MySQL目前提供了以下4種索引:

  • BTREE 索引 : 最常見的索引類型,大部分索引都支持 B 樹索引。
  • HASH 索引:只有Memory引擎支持 , 使用場景簡單 。
  • R-tree 索引(空間索引):空間索引是MyISAM引擎的一個特殊索引類型,主要用於地理空間數據類型,通常使用較少,不做特別介紹。
  • Full-text (全文索引) :全文索引也是MyISAM的一個特殊索引類型,主要用於全文索引,InnoDB從
    Mysql5.6版本開始支持全文索引
    索引對比
  • 平常所說的索引,如果沒有特別指明,都是指B+樹(多路搜索樹,並不一定是二叉的)結構組織的索引。其中聚集索引、複合索引、前綴索引、唯一索引默認都是使用 B+tree 索引,統稱爲 索引;

聚集索引 複合索引區別:
聚簇索引:將數據存儲與索引放到了一塊,索引結構的葉子節點保存了行數據
非聚簇索引:將數據與索引分開存儲,索引結構的葉子節點指向了數據對應的位置

BTREE 結構

BTree又叫多路平衡搜索樹,一顆m叉的BTree特性如下:

  • 樹中每個節點最多包含m個孩子。
  • 除根節點與葉子節點外,每個節點至少有[ceil(m/2)]個孩子。
  • 若根節點不是葉子節點,則至少有兩個孩子。
  • 所有的葉子節點都在同一層。
  • 每個非葉子節點由n個key與n+1個指針組成,其中[ceil(m/2)-1] <= n <= m-1

以5叉BTree爲例,key的數量:公式推導[ceil(m/2)-1] <= n <= m-1。所以 2 <= n <=4 。當n>4時,中間節點分裂到
父節點,兩邊節點分裂。
插入 C N G A H E K Q M F W L T Z D P R X Y S 數據爲例。
動畫演示
演變過程如下,可以通過上面的網站走一下這個過程:
1). 插入前4個字母 C N G A
2). 插入H,n>4,中間元素G字母向上分裂到新的節點
3). 插入E,K,Q不需要分裂
4). 插入M,中間元素M字母向上分裂到父節點G
5). 插入F,W,L,T不需要分裂
6). 插入Z,中間元素T向上分裂到父節點中
7). 插入D,中間元素D向上分裂到父節點中。然後插入P,R,X,Y不需要分裂
8). 最後插入S,NPQR節點n>5,中間節點Q向上分裂,但分裂後父節點DGMT的n>5,中間節點M向上分裂
在這裏插入圖片描述

  • BTREE樹 和 二叉樹相比,查詢數據的效率更高, 因爲對於相同的數據量來說,BTREE的層級結構比二叉樹小,因此搜索速度快。

B+TREE 結構

B+Tree爲BTree的變種,B+Tree與BTree的區別爲:
1). n叉B+Tree最多含有n個key,而BTree最多含有n-1個key。
2). B+Tree的葉子節點保存所有的key信息,依key大小順序排列。
3). 所有的非葉子節點都可以看作是key的索引部分
通過下面的圖,可以非常直觀的看出來二者的差異:
B樹
B+樹
MySQL中的B+Tree

存儲的層級關係

image-20200611161724023

表空間 Tablespace idb索引文件
segment 1.索引段:中間節點(非葉子節點)2.數據段:葉子節點
Extent 大小1MB 可存儲64個Page頁
Page 大小16KB 包含N個Row
Row 包含N個字段
字段 Field

概念解釋:

  • 一個B+樹節點就是一個頁(16KB)
  • B+樹葉子節點前後形成雙向鏈表

image-20200611161805358

它是InnoDB管理存儲空間的基本單位,一個頁的大小一般是16KB。InnoDB爲了不同的目的而設計了許多種不同類型的頁,比如存放表空間頭部信息的頁,存放Insert Buffer信息的頁,存放INODE信息的頁,存放undo日誌信息的頁等等等等。
頁結構
頁
插入數據過程

  • 一個B+樹節點就是一個頁(16KB)
  • B+樹葉子節點前後形成雙向鏈表
  • 頁與頁之間是雙向鏈表頁裏面的數據是單向鏈表 如圖所示:
    在這裏插入圖片描述

索引分類

  1. 單值索引 :即一個索引只包含單個列,一個表可以有多個單列索引
  2. 唯一索引 :索引列的值必須唯一,但允許有空值
  3. 複合索引 :即一個索引包含多個列

索引語法

  • 創建索引

CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
[USING index_type]
ON tbl_name(index_col_name,…)
示例 :
CREATE INDEX index_name ON table_name (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)

  • 追加索引

ALTER TABLE用來創建普通索引、UNIQUE索引或PRIMARY KEY索引。
ALTER TABLE table_name ADD INDEX index_name (column_list)
ALTER TABLE table_name ADD UNIQUE (column_list)
ALTER TABLE table_name ADD PRIMARY KEY (column_list)

  • 查看索引

show index from table_name;

  • 刪除索引

DROP INDEX index_name ON tbl_name;

索引設計原則

索引的設計可以遵循一些已有的原則,創建索引的時候請儘量考慮符合這些原則,便於提升索引的使用效率,更高
效的使用索引。

  1. 對查詢頻次較高,且數據量比較大的表建立索引
  2. 索引字段的選擇,最佳候選列應當從where子句的條件中提取,如果where子句中的組合比較多,那麼應當挑選最常用、過濾效果最好的列的組合
  3. 使用唯一索引,區分度越高,使用索引的效率越高。
  4. 索引可以有效的提升查詢數據的效率,但索引數量不是多多益善,索引越多,維護索引的代價自然也就水漲船高。對於插入、更新、刪除等DML操作比較頻繁的表來說,索引過多,會引入相當高的維護代價,降低DML操作的效率,增加相應操作的時間消耗。另外索引過多的話MySQL也會犯選擇困難病,雖然最終仍然會找到一個可用的索引,但無疑提高了選擇的代價。
  5. 使用短索引,索引創建之後也是使用硬盤來存儲的,因此提升索引訪問的I/O效率,也可以提升總體的訪問效率。假如構成索引的字段總長度比較短,那麼在給定大小的存儲塊內可以存儲更多的索引值,相應的可以有效的提升MySQL訪問索引的I/O效率。
  6. 利用最左前綴,N個列組合而成的組合索引,那麼相當於是創建了N個索引,如果查詢時where子句中使用了組成該索引的前幾個字段,那麼這條查詢SQL可以利用組合索引來提升查詢效率。

聚觸索引 & 非聚觸索引

聚集索引與非聚集索引的區別是:葉節點是否存放一整行記錄
InnoDB 主鍵使用的是聚簇索引和輔助索引,MyISAM 不管是主鍵索引,還是二級索引使用的都是非聚簇索引。
下圖形象說明了聚簇索引表(InnoDB)和非聚簇索引(MyISAM)的區別:
索引區別

  • 對於聚簇索引表來說(左圖),表數據是和主鍵一起存儲的,主鍵索引的葉結點存儲行數據(包含了主鍵值),二級索引的葉結點存儲行的主鍵值。使用的是B+樹作爲索引的存儲結構,非葉子節點都是索引關鍵字,但非葉子節點中的關鍵字中不存儲對應記錄的具體內容或內容地址。葉子節點上的數據是主鍵與具體記錄(數據內容)。

  • 對於非聚簇索引表來說(右圖),表數據和索引是分成兩部分存儲的,主鍵索引和二級索引存儲上沒有任何區別。使用的是B+樹作爲索引的存儲結構,所有的節點都是索引,葉子節點存儲的是索引+索引對應的記錄的數據。

  • 聚簇索引的優點:

    • 需要取出一定範圍內的數據時,用聚簇索引也比用非聚簇索引好。
    • 當通過聚簇索引查找目標數據時理論上比非聚簇索引要快,因爲非聚簇索引定位到對應主鍵時還要多一次目標記錄尋址,即多一次I/O。
    • 使用覆蓋索引掃描的查詢可以直接使用頁節點中的主鍵值。
  • 聚簇索引的缺點

    • 插入速度嚴重依賴於插入順序,按照主鍵的順序插入是最快的方式,否則將會出現頁分裂,嚴重影響性能。因此,對於InnoDB表,我們一般都會定義一個自增的ID列爲主鍵。
    • 更新主鍵的代價很高,因爲將會導致被更新的行移動。因此,對於InnoDB表,我們一般定義主鍵爲不可更新。
    • 二級索引(輔助索引)訪問需要兩次索引查找,第一次找到主鍵值,第二次根據主鍵值找到行數據。二級索引的葉節點存儲的是主鍵值,而不是行指針(非聚簇索引存儲的是指針或者說是地址),這是爲了減少當出現行移動或數據頁分裂時二級索引的維護工作,但會讓二級索引佔用更多的空間。
    • 採用聚簇索引插入新值比採用非聚簇索引插入新值的速度要慢很多,因爲插入要保證主鍵不能重複,判斷主鍵不能重複,採用的方式在不同的索引下面會有很大的性能差距,聚簇索引遍歷所有的葉子節點,非聚簇索引也判斷所有的葉子節點,但是聚簇索引的葉子節點除了帶有主鍵還有記錄值,記錄的大小往往比主鍵要大的多。這樣就會導致聚簇索引在判定新記錄攜帶的主鍵是否重複時進行昂貴的I/O代價。

參考:


你的鼓勵也是我創作的動力

打賞地址

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