mysql學習與提高3:mysql索引
目錄
索引
索引是什麼呢?簡而言之,索引就是一種高效找到資料的一種資料結構。
比如我們查字典,如果我們查每個字都從第一頁開始翻,直到找到我們的目標為止的話,顯然效率是十分低下的。所以便有了我們的拼音索引表,根據這個索引表,我們找每個字的時間就大大縮短了。
B Tree 原理
B-Tree
定義一條資料記錄為一個二元組 [key, data],B-Tree 是滿足下列條件的資料結構:
- 所有葉節點具有相同的深度,也就是說 B-Tree 是平衡的;
- 一個節點中的 key 從左到右非遞減排列;
- 如果某個指標的左右相鄰 key 分別是 keyi 和 keyi+1,且不為 null,則該指標指向節點的(所有 key ≥ keyi) 且(key ≤ keyi+1)。
查詢演算法:首先在根節點進行二分查詢,如果找到則返回對應節點的 data,否則在相應區間的指標指向的節點遞迴進行查詢。
由於插入刪除新的資料記錄會破壞 B-Tree 的性質,因此在插入刪除時,需要對樹進行一個分裂、合併、旋轉等操作以保持 B-Tree 性質。
B+Tree
與 B-Tree 相比,B+Tree 有以下不同點:
- 每個節點的指標上限為 2d 而不是 2d+1(d 為節點的出度);
- 內節點不儲存 data,只儲存 key;
- 葉子節點不儲存指標。
順序訪問指標
一般在資料庫系統或檔案系統中使用的 B+Tree 結構都在經典 B+Tree 基礎上進行了優化,在葉子節點增加了順序訪問指標,做這個優化的目的是為了提高區間訪問的效能。
優勢
紅黑樹等平衡樹也可以用來實現索引,但是檔案系統及資料庫系統普遍採用 B Tree 作為索引結構,主要有以下兩個原因:
(一)更少的檢索次數
平衡樹檢索資料的時間複雜度等於樹高 h,而樹高大致為 O(h)=O(logdN),其中 d 為每個節點的出度。
紅黑樹的出度為 2,而 B Tree 的出度一般都非常大。紅黑樹的樹高 h 很明顯比 B Tree 大非常多,因此檢索的次數也就更多。
B+Tree 相比於 B-Tree 更適合外存索引,因為 B+Tree 內節點去掉了 data 域,因此可以擁有更大的出度,檢索效率會更高。
(二)利用計算機預讀特性
為了減少磁碟 I/O,磁碟往往不是嚴格按需讀取,而是每次都會預讀。這樣做的理論依據是電腦科學中著名的區域性性原理:當一個數據被用到時,其附近的資料也通常會馬上被使用。預讀過程中,磁碟進行順序讀取,順序讀取不需要進行磁碟尋道,並且只需要很短的旋轉時間,因此速度會非常快。
作業系統一般將記憶體和磁碟分割成固態大小的塊,每一塊稱為一頁,記憶體與磁碟以頁為單位交換資料。資料庫系統將索引的一個節點的大小設定為頁的大小,使得一次 I/O 就能完全載入一個節點,並且可以利用預讀特性,相鄰的節點也能夠被預先載入。
更多內容請參考:MySQL 索引背後的資料結構及演算法原理
索引分類
特性 | 說明 | InnoDB | MyISAM | MEMORY |
---|---|---|---|---|
B樹索引 (B-tree indexes) | 自增ID物理連續性更高, 二叉樹,紅黑樹高度不可控 |
√ | √ | √ |
R樹索引 (R-tree indexes) | 空間索引 | √ | ||
雜湊索引 (Hash indexes) | 無法做範圍查詢 | √ | √ | |
全文索引 (Full-text indexes) | √ | √ |
B+Tree 索引
B+Tree 索引是大多數 MySQL 儲存引擎的預設索引型別。
因為不再需要進行全表掃描,只需要對樹進行搜尋即可,因此查詢速度快很多。除了用於查詢,還可以用於排序和分組。
可以指定多個列作為索引列,多個索引列共同組成鍵。
B+Tree 索引適用於全鍵值、鍵值範圍和鍵字首查詢,其中鍵字首查詢只適用於最左字首查詢。
如果不是按照索引列的順序進行查詢,則無法使用索引。
InnoDB 的 B+Tree 索引分為主索引和輔助索引。
主索引的葉子節點 data 域記錄著完整的資料記錄,這種索引方式被稱為聚簇索引。因為無法把資料行存放在兩個不同的地方,所以一個表只能有一個聚簇索引。
輔助索引的葉子節點的 data 域記錄著主鍵的值,因此在使用輔助索引進行查詢時,需要先查詢到主鍵值,然後再到主索引中進行查詢。
雜湊索引
InnoDB 引擎有一個特殊的功能叫 “自適應雜湊索引”,當某個索引值被使用的非常頻繁時,會在 B+Tree 索引之上再建立一個雜湊索引,這樣就讓 B+Tree 索引具有雜湊索引的一些優點,比如快速的雜湊查詢。
雜湊索引能以 O(1) 時間進行查詢,但是失去了有序性,它具有以下限制:
- 無法用於排序與分組;
- 只支援精確查詢,無法用於部分查詢和範圍查詢;
全文索引
MyISAM 儲存引擎支援全文索引,用於查詢文字中的關鍵詞,而不是直接比較是否相等。查詢條件使用 MATCH AGAINST,而不是普通的 WHERE。
全文索引一般使用倒排索引實現,它記錄著關鍵詞到其所在文件的對映。
InnoDB 儲存引擎在 MySQL 5.6.4 版本中也開始支援全文索引。
空間資料索引(R-Tree)
MyISAM 儲存引擎支援空間資料索引,可以用於地理資料儲存。空間資料索引會從所有維度來索引資料,可以有效地使用任意維度來進行組合查詢。
必須使用 GIS 相關的函式來維護資料。
索引的特點
- 可以加快資料庫的檢索速度
- 降低資料庫插入、修改、刪除等維護的速度
- 只能建立在表上,不能建立到檢視上
- 既可以直接建立又可以間接建立
- 可以在優化隱藏中使用索引
- 使用查詢處理器執行SQL語句,在一個表上,一次只能使用一個索引
索引的優點
- 建立唯一性索引,保證資料庫表中每一行資料的唯一性
- 大大加快資料的檢索速度,這是建立索引的最主要的原因
- 加速資料庫表之間的連線,特別是在實現資料的參考完整性方面特別有意義
- 在使用分組和排序子句進行資料檢索時,同樣可以顯著減少查詢中分組和排序的時間
- 通過使用索引,可以在查詢中使用優化隱藏器,提高系統的效能
索引的缺點
- 建立索引和維護索引要耗費時間,這種時間隨著資料量的增加而增加
- 索引需要佔用物理空間,除了資料表佔用資料空間之外,每一個索引還要佔一定的物理空間,如果建立聚簇索引,那麼需要的空間就會更大
- 當對錶中的資料進行增加、刪除和修改的時候,索引也需要維護,降低資料維護的速度
索引失效
-
如果MySQL估計使用全表掃秒比使用索引快,則不適用索引。
例如,如果列key均勻分佈在1和100之間,下面的查詢使用索引就不是很好:select * from table_name where key>1 and key<90;
-
如果條件中有or,即使其中有條件帶索引也不會使用
例如:select * from table_name where key1='a' or key2='b';如果在key1上有索引而在key2上沒有索引,則該查詢也不會走索引
-
複合索引,如果索引列不是複合索引的第一部分,則不使用索引(即不符合最左字首)
例如,複合索引為(key1,key2),則查詢select * from table_name where key2='b';將不會使用索引
-
如果like是以 % 開始的,則該列上的索引不會被使用。
例如select * from table_name where key1 like '%a';該查詢即使key1上存在索引,也不會被使用如果列型別是字串,那一定要在條件中使用引號引起來,否則不會使用索引
-
如果列為字串,則where條件中必須將字元常量值加引號,否則即使該列上存在索引,也不會被使用。
例如,select * from table_name where key1=1;如果key1列儲存的是字串,即使key1上有索引,也不會被使用。
-
如果使用MEMORY/HEAP表,並且where條件中不使用“=”進行索引列,那麼不會用到索引,head表只有在“=”的條件下才會使用索引
在什麼情況下適合建立索引
- 為經常出現在關鍵字order by、group by、distinct後面的欄位,建立索引。
- 在union等集合操作的結果集欄位上,建立索引。其建立索引的目的同上。
- 為經常用作查詢選擇 where 後的欄位,建立索引。
- 在經常用作表連線 join 的屬性上,建立索引。
- 考慮使用索引覆蓋。對資料很少被更新的表,如果使用者經常只查詢其中的幾個欄位,可以考慮在這幾個欄位上建立索引,從而將表的掃描改變為索引的掃描。
更多資料:MySQL索引背後的資料結構及演算法原理
為什麼用B+樹做索引而不用B-樹或紅黑樹
B+ 樹只有葉節點存放資料,其餘節點用來索引,而 B- 樹是每個索引節點都會有 Data 域。所以從 InooDB 的角度來看,B+ 樹是用來充當索引的,一般來說索引非常大,尤其是關係性資料庫這種資料量大的索引能達到億級別,所以為了減少記憶體的佔用,索引也會被儲存在磁碟上。
-
那麼 MySQL如何衡量查詢效率呢?答:磁碟 IO 次數
- B- 樹 / B+ 樹 的特點就是每層節點數目非常多,層數很少,目的就是為了就少磁碟 IO 次數,但是 B- 樹的每個節點都有 data 域(指標),這無疑增大了節點大小,說白了增加了磁碟 IO 次數(磁碟 IO 一次讀出的資料量大小是固定的,單個數據變大,每次讀出的就少,IO 次數增多,一次 IO 多耗時),而 B+ 樹除了葉子節點其它節點並不儲存資料,節點小,磁碟 IO 次數就少。
- B+ 樹所有的 Data 域在葉子節點,一般來說都會進行一個優化,就是將所有的葉子節點用指標串起來。這樣遍歷葉子節點就能獲得全部資料,這樣就能進行區間訪問啦。在資料庫中基於範圍的查詢是非常頻繁的,而 B 樹不支援這樣的遍歷操作。
-
B 樹相對於紅黑樹的區別
- AVL 樹和紅黑樹基本都是儲存在記憶體中才會使用的資料結構。在大規模資料儲存的時候,紅黑樹往往出現由於樹的深度過大而造成磁碟 IO 讀寫過於頻繁,進而導致效率低下的情況。為什麼會出現這樣的情況,我們知道要獲取磁碟上資料,必須先通過磁碟移動臂移動到資料所在的柱面,然後找到指定盤面,接著旋轉盤面找到資料所在的磁軌,最後對資料進行讀寫。磁碟IO代價主要花費在查詢所需的柱面上,樹的深度過大會造成磁碟IO頻繁讀寫。根據磁碟查詢存取的次數往往由樹的高度所決定,所以,只要我們通過某種較好的樹結構減少樹的結構儘量減少樹的高度,B樹可以有多個子女,從幾十到上千,可以降低樹的高度。
- 資料庫系統的設計者巧妙利用了磁碟預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次 I/O 就可以完全載入。為了達到這個目的,在實際實現 B-Tree 還需要使用如下技巧:每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也儲存在一個頁裡,加之計算機儲存分配都是按頁對齊的,就實現了一個 node 只需一次 I/O。
聯合索引
什麼是聯合索引
兩個或更多個列上的索引被稱作聯合索引,聯合索引又叫複合索引。對於複合索引:Mysql 從左到右的使用索引中的欄位,一個查詢可以只使用索引中的一部份,但只能是最左側部分。
例如索引是key index (a,b,c),可以支援[a]、[a,b]、[a,b,c] 3種組合進行查詢,但不支 [b,c] 進行查詢。當最左側欄位是常量引用時,索引就十分有效。
命名規則
- 需要加索引的欄位,要在 where 條件中
- 資料量少的欄位不需要加索引
- 如果 where 條件中是OR關係,加索引不起作用
- 符合最左原則
建立索引
在執行 CREATE TABLE 語句時可以建立索引,也可以單獨用 CREATE INDEX 或 ALTER TABLE 來為表增加索引。
ALTER TABLE
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)
其中 table_name 是要增加索引的表名,column_list 指出對哪些列進行索引,多列時各列之間用逗號分隔。索引名 index_name 可選,預設時,MySQL將根據第一個索引列賦一個名稱。另外,ALTER TABLE 允許在單個語句中更改多個表,因此可以在同時建立多個索引。
CREATE INDEX
CREATE INDEX 可對錶增加普通索引或 UNIQUE 索引。
例如:
CREATE INDEX index_name ON table_name (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
table_name、index_name 和 column_list 具有與 ALTER TABLE 語句中相同的含義,索引名不可選。另外,不能用 CREATE INDEX 語句建立 PRIMARY KEY 索引。
索引型別
在建立索引時,可以規定索引能否包含重複值。如果不包含,則索引應該建立為 PRIMARY KEY 或 UNIQUE 索引。對於單列惟一性索引,這保證單列不包含重複的值。對於多列惟一性索引,保證多個值的組合不重複。 PRIMARY KEY 索引和 UNIQUE 索引非常類似。
事實上,PRIMARY KEY 索引僅是一個具有名稱 PRIMARY 的 UNIQUE 索引。這表示一個表只能包含一個 PRIMARY KEY,因為一個表中不可能具有兩個同名的索引。 下面的SQL語句對 students 表在 sid 上新增 PRIMARY KEY 索引。 ALTER TABLE students ADD PRIMARY KEY (sid)
刪除索引
可利用 ALTER TABLE 或 DROP INDEX 語句來刪除索引。類似於 CREATE INDEX 語句,DROP INDEX 可以在 ALTER TABLE 內部作為一條語句處理,語法如下。
DROP INDEX index_name ON talbe_name
ALTER TABLE table_name DROP INDEX index_name
ALTER TABLE table_name DROP PRIMARY KEY
其中,前兩條語句是等價的,刪除掉 table_name 中的索引 index_name。
第3條語句只在刪除 PRIMARY KEY 索引時使用,因為一個表只可能有一個 PRIMARY KEY 索引,因此不需要指定索引名。如果沒有建立 PRIMARY KEY 索引,但表具有一個或多個 UNIQUE 索引,則 MySQL 將刪除第一個 UNIQUE 索引。
如果從表中刪除了某列,則索引會受到影響。對於多列組合的索引,如果刪除其中的某列,則該列也會從索引中刪除。如果刪除組成索引的所有列,則整個索引將被刪除。
什麼情況下使用索引
- 為了快速查詢匹配WHERE條件的行。
- 為了從考慮的條件中消除行。
- 如果表有一個multiple-column索引,任何一個索引的最左字首可以通過使用優化器來查詢行。
- 查詢中與其它表關聯的字,欄位常常建立了外來鍵關係
- 查詢中統計或分組統計的欄位
- select max(hbs_bh) from zl_yhjbqk
- select qc_bh,count(*) from zl_yhjbqk group by qc_bh