第五章 創建高性能的索引(上)

索引是存儲引擎用于快速找到記錄的一種數據結構, 它能提高查詢性能.

索引基礎

索引是在存儲引擎層實現的, MySQL支持以下幾種索引類型:

索引的類型

B-Tree索引

InnoDB使用了B+Tree, 特點是:

  • 非葉子節點只存儲鍵值信息。
  • 所有葉子節點之間都有一個鏈指針。
  • 數據記錄都存放在葉子節點中。
截屏2020-02-24上午6.37.31.png

B+Tree的高度一般都在2~4層。mysql的InnoDB存儲引擎在設計時是將根節點常駐內存的,也就是說查找某一鍵值的行記錄時最多只需要1~3次磁盤I/O操作.

數據庫中的B+Tree索引可以分為聚集索引(clustered index)和輔助索引(secondary index)。上面的B+Tree示例圖在數據庫中的實現即為聚集索引,聚集索引的B+Tree中的葉子節點存放的是整張表的行記錄數據。輔助索引與聚集索引的區別在于輔助索引的葉子節點并不包含行記錄的全部數據,而是存儲相應行數據的聚集索引鍵,即主鍵。當通過輔助索引來查詢數據時,InnoDB存儲引擎會遍歷輔助索引找到主鍵,然后再通過主鍵在聚集索引中找到完整的行記錄數據.

InnoDB存儲引擎中有頁(Page)的概念,頁是其磁盤管理的最小單位, InnoDB在把磁盤數據讀入到磁盤時會以頁為基本單位, 通過如下命令查看頁的大小:

mysql> show variables like 'innodb_page_size';
image.png

上表的索引對以下類型的查詢有效:

  • 全值匹配全值匹配: 指的是和索引中的所有列進行匹配,例如前面提到的索引可用于查找姓名為Cuba Allen、出生于1960-01-01的人。
  • 匹配最左前綴前面: 提到的索引可用于查找所有姓為Allen的人,即只使用索引的第一列。
  • 匹配列前綴: 也可以只匹配某一列的值的開頭部分。例如前面提到的索引可用于查找所有以J開頭的姓的人。
  • 匹配范圍值: 例如前面提到的索引可用于查找姓在Allen和Barrymore之間的人。這里也只使用了索引的第一列。
  • 精確匹配某一列并范圍匹配另外一列: 前面提到的索引也可用于查找所有姓為Allen,并且名字是字母K開頭(比如Kim、Karl等)的人。
  • 只訪問索引的查詢: B-Tree通常可以支持“只訪問索引的查詢”,即查詢只需要訪問索引,而無須訪問數據行。后面我們將單獨討論這種“覆蓋索引”的優化。

B+Tree索引適用于全鍵值, 鍵值范圍或鍵前綴查找. 其中鍵前綴查找只適用于根據最左前綴的查找. 且因為索引樹中的節點是有序的, 索引除了按值查找外, 索引還可以用于查詢中的ORDER BY操作. 即滿足上面索引查詢的時候, 也可以滿足對應的排序要求(不需再花費額外的內存和時間).

哈希索引

哈希索引基于哈希表實現,只有精確匹配索引的所有列,索引才會生效。

哈希索引的限制:

  1. 哈希索引只存儲哈希值和行指針,不存儲字段數據值,因此無法從索引中查詢數據
  2. 哈希索引無法順序存儲,無法進行排序
  3. 哈希索引不支持部分匹配,必須精確匹配所有索引列
  4. 哈希索引不支持范圍查找
  5. 哈希沖突會影響性能

可以創建自定義哈希索引(見書上的例子), 此時要避免沖突問題, 必須在where條件中帶入hash值和對應列值, 只需要根據Hash值做快速的整數比較找到索引條目, 然后一一比較返回對應的行.

空間數據索引(R-Tree)

用作地理數據存儲, MySQL支持的不完善, 比較好的是PostgresSQL的PostGIS.

全文索引

它查找的是文本中的關鍵詞, 而非直接比較索引中的值. 可以在相同的列上同時創建全文索引和基于值的B-Tree索引不會有沖突, 全文索引適用于MATCH AGAINST操作, 而非普通的WHERE條件操作. 第七章會討論更多全文索引的細節.

索引的優點

  1. 大大減少服務器需要掃描的數據量
  2. 可以幫助服務器避免排序和臨時表
  3. 可以將隨機I/O變為順序I//O

一星系統:
索引將相關的記錄放到一起得一星.
二星系統:
索引中的數據順序和查找中的排列順序一致得二星.
三星系統:
索引中的列包含了查詢中需要的所有列得三星.

當然,索引也并不總是最好的工具。只有當索引幫助存儲引擎快速查找到記錄帶來的好處大于其帶來的額外工作時,索引才是有效的。非常小的表,大部分直接全表掃描更有效;中到大型的表,索引非常有效。特大型的表,則建立和使用索引的代價將隨之增長,需要一種技術可以區分出查詢需要的一組數據,如分區技術

高性能的索引策略

獨立的列

列不要進行參與計算:
where id+1=5應該改成where id=4

前綴索引和索引選擇性

一種選擇索引的技巧是: 選擇足夠長的前綴保證較高的選擇性, 同時又不能太長(以便節約空間).

索引的選擇性是指: 不重復的索引值(也稱為基數, cardinality)和數據表的總記錄數(#T)的比值, 范圍從1/#T到1之間. 索引的選擇性越高則查詢效率越高.

balabala, 若找到了合適的前綴長度, 則可用以下語句創建前綴索引:

mysql> ALTER TABLE sakila.city_demo ADD KEY (city(7));

查詢的時候where city='xxxxxxxxxx'自動會用到前綴索引
前綴索引優點是: 使索引更小.
缺點是: MySQL無法使用前綴索引做ORDER BY和GROUP BY, 也無法使用前綴索引做覆蓋掃描.

多列索引

這里指多個單列索引, 不建議.

選擇合適的索引列順序

在一個多列B-Tree索引中, 索引列的順序意味著索引先按照最左邊進行排序, 其次是第二列, 等等.

經驗法則: 將選擇性最高的列放在索引最前列. 但特殊情況有其他的標準.

聚簇索引

InnoDB的聚簇索引實際在同一個結構中保存了B-Tree索引和數據行.
數據行存儲在葉子頁中, "聚簇"表示數據行和相鄰的鍵值緊湊地存儲在一起. 一個表只能有一個聚簇索引.

假設建表語句為:

mysql> create table T(
id int primary key, 
k int not null, 
name varchar(16),
index (k))engine=InnoDB;
image.png

主鍵索引的葉子節點存的是整行數據。在 InnoDB 里,主鍵索引也被稱為聚簇索引(clustered index)。

非主鍵索引的葉子節點內容是主鍵的值。在 InnoDB 里,非主鍵索引也被稱為二級索引(secondary index)。

如果語句是 select * from T where ID=500,即主鍵查詢方式,則只需要搜索 ID 這棵 B+ 樹;如果語句是 select * from T where k=5,即普通索引查詢方式,則需要先搜索 k 索引樹,得到 ID 的值為 500,再到 ID 索引樹搜索一次。這個過程稱為回表

索引維護

B+ 樹為了維護索引有序性,在插入新值的時候需要做必要的維護。
以上面這個圖為例,如果插入新的行 ID 值為 700,則只需要在 R5 的記錄后面插入一個新記錄。如果新插入的 ID 值為 400,就相對麻煩了,需要邏輯上挪動后面的數據,空出位置。而更糟的情況是,如果 R5 所在的數據頁已經滿了,根據 B+ 樹的算法,這時候需要申請一個新的數據頁,然后挪動部分數據過去。這個過程稱為頁分裂
在這種情況下,性能自然會受影響。除了性能外,頁分裂操作還影響數據頁的利用率。原本放在一個頁的數據,現在分到兩個頁中,整體空間利用率降低大約 50%。當然有分裂就有合并。當相鄰兩個頁由于刪除了數據,利用率很低之后,會將數據頁做合并。合并的過程,可以認為是分裂過程的逆過程。

MyISAM的數據存儲

MyISAM的行數據按照數據插入的順序存儲在磁盤上, 索引也是B-Tree, 不過B-Tree的葉子節點存儲的是行號(行指針). MyISAM的主鍵和其他索引在結構上沒有不同. 如下圖所示:


截屏2020-02-24下午11.41.53.png

要注意的是: InnoDB的二級索引的葉子節點存儲的不是行指針, 而是主鍵值, 這樣的好處是當行移動或數據頁分裂時, 無須更新二級索引中的主鍵值.

在InnoDB表中按主鍵順序插入行

盡量使用AUTO_INCREMENT自增列, 保證數據行是按順序寫入. 如果非順序插入, 會有以下幾個缺點:

  1. 寫入的目標頁可能已經刷到磁盤上并從緩存中移除, 不得不先找到并從磁盤讀取目標頁到內存中.
  2. 因為寫入是亂序, InnoDB不得不頻繁的做頁分裂操作, 這時候需要申請一個新的數據頁,然后挪動部分數據過去, 性能受影響.
  3. 頻繁頁分裂, 頁會變得稀疏和碎片.
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容