1. 程式人生 > 其它 >mysql索引&索引資料結構

mysql索引&索引資料結構

mysql索引

Mysql資料庫中的常見索引結構有多種,常用Hash,B-樹,B+樹等資料結構來進行資料儲存。樹的深度加深一層,意味著多一次查詢,對於資料庫磁碟而言,就是多一次IO操作,導致查詢效率低下。

什麼是索引

索引是對資料庫表中一列或多列的值進行排序的一種結構。MySQL索引的建立對於MySQL的高效執行是很重要的,索引可以大大提高MySQL的檢索速度。索引只是提高效率的一個因素,如果你的MySQL有大資料量的表,就需要花時間研究建立最優秀的索引,或優化查詢語句。

簡單類比一下,資料庫如同書籍,索引如同書籍目錄,假如我們需要從書籍查詢與 xx 相關的內容,我們可以直接從目錄中查詢,定位到 xx 內容所在頁面,如果目錄中沒有 xx 相關字元或者沒有設定目錄(索引),那隻能逐字逐頁閱讀文字查詢,效率可想而知。

為什麼使用索引

  • 快速查詢匹配where子句中的行
  • 如果可以在多個索引中選擇,mysql通常會使用找到最少行的索引
  • 如果表具有多列索引,則優化器可以使用索引的任何最左字首來查詢行
  • 當有表連線的時候,從其他表檢索行資料
  • 查詢特定索引列的min和max的值
  • 如果排序或者分組時可用索引的最左字首完成的,則對錶進行排序和分組
  • 在某些情況下,可以優化查詢以檢索資料值而無需查詢資料行

總結: 使用最主要的原因是使用提高效率

索引的優缺點

優點:

  • 索引大大減小了伺服器需要掃描的資料量,從而大大加快資料的檢索速度,這也是建立索引的最主要的原因。
  • 索引可以幫助伺服器避免排序和建立臨時表【B+樹已經排序過的】
  • 索引可以將隨機IO變成順序IO,減少IO次數
  • 索引對於InnoDB(對索引支援行級鎖)非常重要,因為它可以讓查詢鎖更少的元組,提高了表訪問併發性
  • 關於InnoDB、索引和鎖:InnoDB在二級索引上使用共享鎖(讀鎖),但訪問主鍵索引需要排他鎖(寫鎖)
  • 通過建立唯一性索引,可以保證資料庫表中每一行資料的唯一性。
  • 可以加速表和表之間的連線,特別是在實現資料的參考完整性方面特別有意義。
  • 在使用分組和排序子句進行資料檢索時,同樣可以顯著減少查詢中分組和排序的時間。
  • 通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的效能

缺點:

  • 建立索引和維護索引要耗費時間,這種時間隨著資料量的增加而增加
  • 索引需要佔物理空間,除了資料表佔用資料空間之外,每一個索引還要佔用一定的物理空間,如果需要建立聚簇索引,那麼需要佔用的空間會更大
  • 對錶中的資料進行增、刪、改的時候,索引也要動態的維護,這就降低了整數的維護速度
  • 如果某個資料列包含許多重複的內容,為它建立索引就沒有太大的實際效果。
  • 對於非常小的表,大部分情況下簡單的全表掃描更高效;

建立索引準則

  • 應該建立索引的列:
    • 經常需要搜尋的列上,可以加快搜索的速度
    • 作為主鍵的列上,強制該列的唯一性和組織表中資料的排列結構
    • 經常用在連線(JOIN)的列上,這些列主要是一外來鍵,可以加快連線的速度[少用]
    • 經常需要根據範圍(<,<=,=,>,>=,BETWEEN,IN)進行搜尋的列上建立索引,因為索引已經排序,其指定的範圍是連續的
    • 在經常需要排序(order by)的列上建立索引,因為索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間;
    • 經常使用在WHERE子句中的列上面建立索引,加快條件的判斷速度。
  • 不該建立索引的列:
    • 對於那些在查詢中很少使用或者參考的列不應該建立索引。若列很少使用到,因此有索引或者無索引,並不能提高查詢速度。相反,由於增加了索引,反而降低了系統的維護速度和增大了空間需求。
    • 對於那些只有很少資料值或者重複值多的列也不應該增加索引。【資料去重後的資料比趨於1,則索引效果越好
      這些列的取值很少,例如人事表的性別列,在查詢的結果中,結果集的資料行佔了表中資料行的很大比例,即需要在表中搜索的資料行的比例很大。增加索引,並不能明顯加快檢索速度。
    • 對於那些定義為text, image和bit資料型別的列不應該增加索引。這些列的資料量要麼相當大,要麼取值很少。
    • 當該列修改效能要求遠遠高於檢索效能時,不應該建立索引。(修改效能和檢索效能是互相矛盾的)

索引的分類

  • 主鍵索引(唯一且非空)【資料庫預設建立的索引是給唯一鍵建立的】
  • 唯一索引(唯一可為空)
  • 普通索引(普通欄位的索引)
  • 全文索引(一般是varchar,char,text型別建立的,但很少用)
  • 組合索引(多個字的建立的索引)

MySQL 的索引有兩種分類方式:邏輯分類和物理分類。

邏輯分類

有多種邏輯劃分的方式,比如按功能劃分,按組成索引的列數劃分等

  1. 按功能劃分
  • 主鍵索引:一張表只能有一個主鍵索引,不允許重複、不允許為 NULL;
 ALTER TABLE TableName ADD PRIMARY KEY(column_list); 
  • 唯一索引:資料列不允許重複,允許為 NULL 值,一張表可有多個唯一索引,索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。
CREATE UNIQUE INDEX IndexName ON `TableName`(`欄位名`(length));
# 或者
ALTER TABLE TableName ADD UNIQUE (column_list); 
  • 普通索引:一張表可以建立多個普通索引,一個普通索引可以包含多個欄位,允許資料重複,允許 NULL 值插入;
CREATE INDEX IndexName ON `TableName`(`欄位名`(length));
# 或者
ALTER TABLE TableName ADD INDEX IndexName(`欄位名`(length));
  • 全文索引:它查詢的是文字中的關鍵詞,主要用於全文檢索。(篇幅較長,下文有獨立主題說明)
  1. 按列數劃分
  • 單例索引:一個索引只包含一個列,一個表可以有多個單例索引。
  • 組合索引:一個組合索引包含兩個或兩個以上的列。查詢的時候遵循 mysql 組合索引的 “最左字首”原則,即使用 where 時條件要按照建立索引的時候欄位的排列方式放置索引才會生效。

物理分類

分為聚簇索引和非聚簇索引(有時也稱輔助索引或二級索引)

聚簇是為了提高某個屬性(或屬性組)的查詢速度,把這個或這些屬性(稱為聚簇碼)上具有相同值的元組集中存放在連續的物理塊。

聚簇索引(clustered index)不是單獨的一種索引型別,而是一種資料儲存方式。這種儲存方式是依靠B+樹來實現的,根據表的主鍵構造一棵B+樹且B+樹葉子節點存放的都是表的行記錄資料時,方可稱該主鍵索引為聚簇索引。聚簇索引也可理解為將資料儲存與索引放到了一塊,找到索引也就找到了資料。

非聚簇索引:資料和索引是分開的,B+樹葉子節點存放的不是資料表的行記錄

雖然InnoDB和MyISAM儲存引擎都預設使用B+樹結構儲存索引,但是隻有InnoDB的主鍵索引才是聚簇索引,InnoDB中的輔助索引以及MyISAM使用的都是非聚簇索引。每張表最多隻能擁有一個聚簇索引。

聚簇索引優點:

  • 聚簇索引對於主鍵的排序查詢和範圍查詢速度非常快
  • 資料訪問更快,因為聚簇索引將索引和資料儲存在同一個B+樹中,因此從聚簇索引中獲取資料比非聚簇索引更快

聚簇索引缺點:

  • 插入速度嚴重依賴於插入順序,按照主鍵的順序插入是最快的方式,否則將會出現頁分裂,嚴重影響效能。因此,對於InnoDB表,我們一般都會定義一個自增的ID列為主鍵(主鍵列不要選沒有意義的自增列,選經常查詢的條件列才好,不然無法體現其主鍵索引效能

  • 更新主鍵的代價很高,因為將會導致被更新的行移動。因此,對於InnoDB表,我們一般定義主鍵為不可更新。

  • 二級索引訪問需要兩次索引查詢,第一次找到主鍵值,第二次根據主鍵值找到行資料。

Mysql中key 、primary key 、unique key 與index區別

key 與 index 含義

  • key具有兩層含義:1.約束(約束和規範資料庫的結構完整性)2.索引
  • index:索引

key 種類

  • key:等價普通索引 key 鍵名 (列)

  • primary key: 【1. 約束作用(constraint),主鍵約束(unique,not null,一表一主鍵,唯一標識記錄),規範儲存主鍵和強調唯一性; 2.為這個key建立主鍵索引 】

  • unique key:【1. 約束作用(constraint),unique約束(保證列或列集合提供了唯一性;2.為這個key建立一個唯一索引】

  • foreign key:【1.約束作用(constraint),外來鍵約束,規範資料的引用完整性 ;2.為這個key建立一個普通索引】

練習

建立個user表:

mysql> create table user(
    -> id int auto_increment,
    -> username varchar(100) not null,
    -> user_id int(8) primary key,
    -> depart_no int not null,
    -> corp varchar(100),
    -> phone char(11),
    -> key auto_id (id),
    -> unique key phone (phone),
    -> index username_depart_corp (username,depart_no,corp),
    -> constraint fk_user_depart foreign key(depart_no) references depart(id);
    -> )engine=innodb charset=utf8;

auto_increment修飾的欄位需要是一個候選鍵,需要用key指定,否則報錯。我們看下錶的結構:

查看錶索引:

可見key也會生成索引,【key型別:PRI 主鍵約束;UNI 唯一約束;MUL 可以重複】

如果一個Key有多個約束,將顯示約束優先順序最高的, PRI>UNI>MUL

索引資料結構

MySQL中常用的索引結構(索引底層的資料結構)有:B-TREE ,B+TREE ,HASH 等,而樹是資料結構中的重中之重,有一般二叉樹、完全二叉樹、滿二叉樹、線索二叉樹、霍夫曼樹、二叉排序樹、平衡二叉樹、紅黑樹、B樹。

二叉樹

定義:二叉排序樹(Binary Sort Tree),又稱二叉查詢樹(Binary Search Tree),也稱二叉搜尋樹。二叉排序樹或者是一棵空樹

二叉樹圖

特性:

  • 所有非葉子結點至多擁有兩個子節點(Left和Right);

  • 所有結點儲存一個關鍵字;

  • 非葉子結點的左指標指向小於其關鍵字的子樹,右指標指向大於其關鍵字的子樹;

二叉搜尋樹的搜尋,從根結點開始,如果查詢的關鍵字與結點關鍵字相等,則該結點為查詢的結點,如果查詢關鍵字比結點關鍵字小,則進入左子樹,反之則進入右子樹;如果左子樹為空或者右子樹為空,則返回查詢不到響應的關鍵字;
如果二叉搜尋樹的所有葉子結點的左右子樹的樹木保持一個平衡即左右子樹個數大致相等的話,其搜尋則更接近與二分查詢;但是它相比連續記憶體空的二分查詢的優點是:改變二叉搜尋樹的結構(新增或者刪除)不需要大段的移動資料,甚至通常都是常數開銷;

產生問題:

當一個二叉樹經歷多次刪除操作後,就會出現樹不平衡的狀態,下圖

右邊也是一個搜尋二叉樹,只不過不在平衡了,他的搜尋功能也變成了線性的,同樣的關鍵字可能導致不同的樹結構索引,所以,在使用搜索二叉樹時,還要考慮儘可能讓B樹保持左圖的結構,避免和右圖類似,這也有事所謂的平衡問題

實際使用的二叉搜尋樹都是在原二叉搜尋樹的基礎上加上平衡演算法,即平衡二叉樹;如何保持B樹節點分佈均勻的平衡演算法就是平衡二叉樹的關鍵所在,平衡演算法是一種在二叉搜尋樹的插入和刪除結點時的一種策略。即:在插入或刪除的同時保持二叉搜尋樹的平衡。

B-樹

B-樹,這裡的 B 表示 balance( 平衡的意思),B-樹是一種多路自平衡的搜尋樹(B樹是一顆多路平衡查詢樹)它類似普通的平衡二叉樹,不同的一點是B-樹允許每個節點有更多的子節點

三階B樹圖

磁碟讀取資料是以盤塊(block)為基本單位的。

B-樹就是B樹,多路搜尋樹,樹高一層意味著多一次的磁碟I/O

B樹是一種多路搜尋樹,一棵m階的B樹滿足下列條件:

  • 樹中每個結點至少有M個孩子
  • 根結點的子節點數為[2,M)
  • 除根結點以外的非葉子結點的兒子數為[M/2, M];
  • 每個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字)
  • 非葉子結點的關鍵字個數 = 指向子節點的指標個數-1;
  • 非葉子結點的關鍵字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
  • 非葉子結點的指標:P[1], P[2], …, P[M];其中P[1]指向關鍵字小於K[1]的子樹,P[M]指向關鍵字大於K[M-1]的子樹,其它P[i]指向關鍵字屬於(K[i-1], K[i])的子樹;
  • 所有葉子結點位於同一層;

B樹的特徵:

  • 關鍵字集合分佈在整顆樹中;

  • 任何一個關鍵字出現且只出現在一個結點中;

  • 搜尋有可能在非葉子結點結束;

  • 其搜尋效能等價於在關鍵字全集內做一次二分查詢;

  • 自動層次控制;

    B樹的搜尋,從根結點開始,對結點內的關鍵字(有序)序列進行二分查詢,如果命中則結束,否則進入查詢關鍵字所屬範圍的子結點;重複,直到所對應的子指標為空,或已經是葉子結點;

B+ 樹

B+樹是B-樹的變體,也是一種多路搜尋樹,特性和B-類似,不同在於:

  1. 所有關鍵字儲存在葉子節點出現,內部節點(非葉子節點並不儲存真正的 data)
  2. 為所有葉子結點增加了一個鏈指標

B+簡圖:

因為內節點並不儲存 data,所以一般B+樹的葉節點和內節點大小不同,而B-樹的每個節點大小一般是相同的,為一頁

為了增加 區間訪問性,一般會對B+樹做一些優化。如下圖帶順序訪問的B+樹。

B-樹和B+樹的區別

  1. B+樹內節點不儲存資料,所有 data 儲存在葉節點導致查詢時間複雜度固定為 log n。而B-樹查詢時間複雜度不固定,與 key 在樹中的位置有關,最好為O(1)。

B-樹:

從上圖可以看出,key 為 50 的節點就在第一層,B-樹只需要一次磁碟 IO 即可完成查詢。所以說B-樹的查詢最好時間複雜度是 O(1)

B+樹:

由於B+樹所有的 data 域都在根節點,所以查詢 key 為 50的節點必須從根節點索引到葉節點,時間複雜度固定為 O(log n)。

小結:B樹的由於每個節點都有key和data,所以查詢的時候可能不需要O(logn)的複雜度,甚至最好的情況是O(1)就可以找到資料,而B+樹由於只有葉子節點儲存了data,所以必須經歷O(logn)複雜度才能找到資料

  1. B+樹葉節點兩兩相連可大大增加區間訪問性,可使用在範圍查詢等,而B-樹每個節點 key 和 data 在一起,則無法區間查詢。

空間區域性性原理:如果一個儲存器的某個位置被訪問,那麼將它附近的位置也會被訪問。利用磁碟預讀原理提前將這些資料讀入記憶體,減少了磁碟 IO 的次數。由於B+樹的葉子節點的資料都是使用連結串列連線起來的,而且他們在磁盤裡是順序儲存的,所以當讀到某個值的時候,磁碟預讀原理就會提前把這些資料都讀進記憶體,使得範圍查詢和排序都很快。

  1. B+樹更適合外部儲存。由於內節點無 data 域,每個節點能索引的範圍更大更精確

由於B-樹節點內部每個 key 都帶著 data 域,而B+樹節點只儲存 key 的副本,真實的 key 和 data 域都在葉子節點儲存。前面說過磁碟是分 block 的,一次磁碟 IO 會讀取若干個 block,具體和作業系統有關,那麼由於磁碟 IO 資料大小是固定的,在一次 IO 中,單個元素越小,量就越大。這就意味著B+樹單次磁碟 IO 的資訊量大於B-樹,從這點來看B+樹相對B-樹磁碟 IO 次數少。

由於B樹的節點都存了key和data,而B+樹只有葉子節點存data,非葉子節點都只是索引值,沒有實際的資料,這就時B+樹在一次IO裡面,能讀出的索引值更多。從而減少查詢時候需要的IO次數!

B-樹僅有 2 個 key,而B+樹有 3 個 key

總結:

  1. B+ 樹的磁碟讀寫代價更低:B+ 樹的資料都集中在葉子節點,分支節點 只負責指標(索引);B 樹的分支節點既有指標也有資料 。這將導致B+ 樹的層高會小於B 樹的層高,也就是說B+ 樹平均的Io次數會小於B 樹。
  2. B+ 樹的查詢效率更加穩定:B+ 樹的資料都存放在葉子節點,故任何關鍵字的查詢必須走一條從根節點到葉子節點的路徑。所有關鍵字的查詢路徑相同,每個資料查詢效率相當。
  3. B+樹更便於遍歷:由於B+樹的資料都儲存在葉子結點中,分支結點均為索引,遍歷只需要掃描一遍葉子節點即可;B樹因為其分支結點同樣儲存著資料,要找到具體的資料,需要進行一次中序遍歷按序來搜尋。
  4. B+樹更擅長範圍查詢:B+樹葉子節點存放資料,資料是按順序放置的雙向連結串列。B樹範圍查詢只能中序遍歷。
  5. B+ 樹佔用記憶體空間小:B+ 樹索引節點沒有資料,比較小。在記憶體有限的情況下,相比於B樹索引可以載入更多B+ 樹索引。

B樹由來

B-樹是專門為外部儲存器設計的,如磁碟,它對於讀取和寫入大塊資料有良好的效能,所以一般被用在檔案系統及資料庫中。

首先看看為什麼會出現B-樹這類資料結構:

傳統用來搜尋的平衡二叉樹有很多,如 AVL 樹,紅黑樹等。這些樹在一般情況下查詢效能非常好,但當資料非常大的時候它們就無能為力了。原因當資料量非常大時,記憶體不夠用,大部分資料只能存放在磁碟上,只有需要的資料才載入到記憶體中。一般而言記憶體訪問的時間約為 50 ns,而磁碟在 10 ms 左右。速度相差了近 5 個數量級,磁碟讀取時間遠遠超過了資料在記憶體中比較的時間。這說明程式大部分時間會阻塞在磁碟 IO 上。那麼我們如何提高程式效能?減少磁碟 IO 次數,像 AVL 樹,紅黑樹這類平衡二叉樹從設計上無法“迎合”磁碟。

上圖是一顆簡單的平衡二叉樹,平衡二叉樹是通過旋轉來保持平衡的,而旋轉是對整棵樹的操作,若部分載入到記憶體中則無法完成旋轉操作。其次平衡二叉樹的高度相對較大為 log n(底數為2),這樣邏輯上很近的節點實際物理上可能非常遠,無法很好的利用磁碟預讀(區域性性原理),所以這類平衡二叉樹在資料庫和檔案系統上的選擇就被 pass 了。【空間區域性性原理:如果一個儲存器的某個位置被訪問,那麼將它附近的位置也會被訪問

然後從減少磁碟IO的角度來看看B-樹的設計:

索引的效率依賴與磁碟 IO 的次數,快速索引需要有效的減少磁碟 IO 次數,如何快速索引呢?索引的原理其實是不斷的縮小查詢範圍,快速定位,就如我們平時用字典查單詞一樣,先找首字母縮小範圍,再第二個字母等等。平衡二叉樹是每次將範圍分割為兩個區間。為了更快,B-樹每次將範圍分割為多個區間,區間越多,定位資料越快越精確。那麼如果節點為區間範圍,每個節點就較大了。所以新建節點時,直接申請頁大小的空間(磁碟儲存單位是按 block 分的,一般為 512 Byte。磁碟 IO 一次讀取若干個 block,我們稱為一頁,具體大小和作業系統有關,一般為 4 k,8 k或 16 k),計算機記憶體分配是按頁對齊的,這樣就實現了一個節點只需要一次 IO

上圖是一棵簡化的B-樹,多叉的好處非常明顯,有效的降低了B-樹的高度,為底數很大的 log n,底數大小與節點的子節點數目有關,一般一棵B-樹的高度在 3 層左右。層數低,每個節點區確定的範圍更精確,範圍縮小的速度越快(比二叉樹深層次的搜尋肯定快很多)。一個節點需要進行一次 IO,那麼總 IO 的次數就縮減為了 log n 次。B-樹的每個節點是 n 個有序的序列(a1,a2,a3…an),並將該節點的子節點分割成 n+1 個區間來進行索引(X1< a1, a2 < X2 < a3, … , an+1 < Xn < anXn+1 > an)。

小結:

B樹的每個節點,都是存多個值的,不像二叉樹那樣,一個節點就一個值,B樹把每個節點都給了一點的範圍區間,區間更多的情況下,搜尋也就更快了,比如:有1-100個數,二叉樹一次只能分兩個範圍,0-50和51-100,而B樹,分成4個範圍 1-25, 25-50,51-75,76-100一次就能篩選走四分之三的資料。所以作為多叉樹的B樹是更快的

Hash

雜湊索引就是採用一定的雜湊演算法,把鍵值換算成新的雜湊值,檢索時不需要類似B+樹那樣從根節點到葉子節點逐級查詢,只需一次雜湊演算法即可立刻定位到相應的位置,速度非常快。Memory儲存引擎使用Hash

範圍查詢

Hash索引僅僅能滿足"=",“IN"和”<=>"查詢,不能使用範圍查詢。也不支援任何範圍查詢。

由於Hash索引比較的是進行Hash運算之後的Hash值,所以它只能用於等值的過濾,不能用於基於範圍的過濾,儲存是雜湊計算之後的順序,因為經過相應的Hash演算法處理之後的Hash值的大小關係,並不能保證和Hash運算前完全一樣。

B+樹和雜湊結構區別

  • 如果是等值查詢,那麼雜湊索引明顯有絕對優勢,因為只需要經過一次演算法即可找到相應的鍵值;這有個前提,鍵值都是唯一的。如果鍵值不是唯一的,就需要先找到該鍵所在位置,然後再根據連結串列往後掃描,直到找到相應的資料;

  • 如果是範圍查詢檢索,這時候雜湊索引就毫無用武之地了,因為原先是有序的鍵值,經過雜湊演算法後,有可能變成不連續的了,就沒辦法再利用索引完成範圍查詢檢索;

  • 雜湊索引也沒辦法利用索引完成排序,以及like ‘xxx%’ 這樣的部分模糊查詢(這種部分模糊查詢,其實本質上也是範圍查詢);

  • 雜湊索引也不支援多列聯合索引的最左匹配規則;

  • B+樹索引的關鍵字檢索效率比較平均,不像B樹那樣波動幅度大,在有大量重複鍵值情況下,雜湊索引的效率也是極低的,因為存在所謂的雜湊碰撞問題。

InnoDB B+Tree結構來儲存索引

InnoDB使用B+Tree資料結構儲存索引,根據索引物理結構可將索引劃分為聚簇索引和非聚簇索引(也可稱輔助索引或二級索引)。一個表中只能存在一個聚簇索引(主鍵索引),但可以存在多個非聚簇索引。

B+樹 葉子節點包含資料表中行記錄就是聚簇索引(索引和資料是一塊的):

B+樹 葉子節點沒包含資料表中行記錄就是非聚簇索引(索引和資料是分開的),下圖:

資料表中的資料都是儲存在頁中的,所以一個頁中能儲存多少行資料呢?假設一行資料的大小是1k,那麼一個頁可以存放16行這樣的資料。

如果資料庫只按這樣的方式儲存,那麼如何查詢資料就成為一個問題?
因為我們不知道要查詢的資料存在哪個頁中,也不可能把所有的頁遍歷一遍,那樣太慢了。

於是人們想到了用B+ 樹的方式組織這些資料,下圖以InnoDB為例:

  • pointer往往是6個位元組,指明對應key值的頁面位置資訊。

  • key一般為索引主鍵,如果為單欄位 bigint 型別,則為8位元組。如此可計算一個頁大概可以存放16 * 1024/(6+8)=1170行資料。

  • 假設一行資料1k,那麼2層B+ 樹(第一層索引,第二層葉子節點 存資料)就可以儲存1170 * 16 = 18 720行;三層則可以儲存1170 * 1170 * 16=21902400行。

MyISAM B+Tree結構來儲存索引

MyISAM也使用B+Tree資料結構儲存索引,但都是非聚簇索引。

以下是MyISAM主鍵索引儲存圖:

可見,索引和資料是分開的 索引的data部分只是索引的地址值。其實上文也提到過,.MYI就是MyISAM表的索引檔案,MYD是MyISAM表的資料檔案。

索引規則

回表

name欄位是普通索引,從name列的B+樹找到主鍵,再從主鍵的B+樹找到最終的資料,這就是回表。(主鍵索引的葉子節點儲存的是列的所有資料,但是普通所有的葉子結點儲存的是對應的主鍵ID)

總結:通過普通索引B+樹確定主鍵值,再到主鍵索引樹,查詢到具體資料,即為回表

例如

select * from use where name='sun' # name已經建立有普通索引

首先會通過name這個非主鍵索引找到sun對應的主鍵Id=2,然後通過id=2在主鍵索引中找到整個行資料,並返回,這個就是回表。

覆蓋索引

在非主鍵索引上可以查詢到所需要的欄位,不需要回表再次查詢就叫覆蓋索引。

例如:

select id,name from user where name ="1"

只查詢id和name值,id的值在第一步非主鍵索引就已經有了,就不需要根據ID到主鍵索引中查詢行資料了,沒有回表操作。

最左匹配原則

組合索引中 先匹配左邊,再繼續向後匹配;比如user表中有name+age組成的聯合索引,select * from user where name="紀先生" and age = 18 就符合最左匹配,可以用的索引。而select * from user where age = 18就不符合,用不到這個索引。

總結:組合索引顧頭不顧未,想要使用索引,必須從組合索引的第一個欄位開始。

例如:

select * from user where name="是非" and age = 18;

select * from user where age = 18;
#  由於最左匹配原則:只需要建立一個組合索引age+name即可
#  第一個sql在執行過程中,mysql 底層優化器會把age和name調換執行位置,從而提高查詢效率
select * from user where name="紀先生" and age = 18;

select * from user where name= "紀先生";

# 建立name+age和age索引,或者建立age+name和name索引,看著兩個都可以。
# 其實name+age和age更好,因為索引也是需要持久化儲存的,佔用磁碟空間,讀取的時候也是佔用記憶體的,
# name+age和age+name這兩個佔用是一樣的,但是name和age單獨比較,
# 肯定age佔用空間更少,name更長(索引越大,IO次數可能更多)

索引下推

組合索引中儘量利用索引資訊,來儘可能的減少回表的次數

name+age的組合索引如果沒有索引下推的查詢是 在組合索引中通過name查詢所有匹配的資料,然後回表根據ID查詢對於的資料行,之後在篩選出符合age條件的資料。索引下推就是組合索引中通過name查詢匹配再根據age找到符合的資料ID,然後回表根據ID查詢對應行資料,明顯會減少資料的條數


大佬部落格:MySQL體系構架、儲存引擎和索引結構

一文搞懂MySQL索引(清晰明瞭)


參考:

深入學習二叉樹(四) 二叉排序樹[https://www.jianshu.com/p/bbe133625c73]
B樹和B+樹的區別[https://blog.csdn.net/a519640026/article/details/106940115]
深入瞭解MySQL中的索引(用處、分類、匹配方式)[https://www.php.cn/mysql-tutorials-482605.html]