【MySQL(2)| MySQL索引機制】

什麼是索引?

索引是存儲引擎用於快速找到記錄數據行的一種分散存儲的數據結構。sql

索引對於良好的性能很是關鍵,尤爲是當表中的數據量愈來愈大時,索引對性能的影響愈發重要。數據庫

在數據量較小且負載較低時,不恰當的索引對性能的影響可能還不明顯,可是當數據量逐漸增大時,性能則會急劇降低。編程

因此 正確的建立合適的索引是提高數據庫查詢性能的基礎bash

爲何要使用索引?

  • 索引能夠把隨機IO編程順序IO
  • 索引能極大的減小存儲殷勤須要掃描的數據量
  • 索引能夠幫助咱們在進行分組、排序等操做時,避免使用臨時表

索引有哪些類型?

索引有不少類型,能夠爲不一樣的場景提供更好的性能。微信

MySQL中,索引是在存儲引擎層面實現的,因此,並無統一的索引標準,通常來講,不一樣存儲引擎的工做方式是不同的,也不是全部的存儲引擎都支持全部類型的索引數據結構

哈希索引

哈希索引基於哈希表實現,只有精確匹配索引全部列的查詢纔有效。性能

對於每個數據行,存儲引擎都會對全部的索引列根據必定的計算規則計算出一個哈希碼,而後哈希索引將全部的哈希碼存儲在索引中,同時在哈希表中會保存一個指向對應數據行的指針大數據

MySQL中,Memory引擎是顯式支持哈希索引的,他也是該引擎默認的索引類型,值得注意的一點是:Memory引擎是支持非惟一哈希索引的,也就是說若是多個列的哈希值相同,索引會以鏈表的方式存放多個記錄指針到同一個哈希表中。優化

哈希索引的應用場景

根據本人的理解,這種直接經過哈希索引的存儲引擎,由於索引自身只須要存儲對應的哈希值,因此索引的結構十分緊湊,這會讓哈希索引查找的速度很是快ui

哈希索引的一些限制
  • 哈希索引只包含哈希值和行指針,而不存儲字段值,因此不能使用索引中的值來避免讀取行(即不能使用哈希索引來作覆蓋索引掃描),不過,訪問內存中的行的速度很快(由於memory引擎的數據都保存在內存裏),因此大部分狀況下這一點對性能的影響並不明顯。
  • 哈希索引數據並非按照索引列的值順序存儲的,因此也就沒法用於排序
  • 哈希索引也不支持部分索引列匹配查找,由於哈希索引始終是使用索引的所有列值內容來計算哈希值的。如:數據列(a,b)上創建哈希索引,若是隻查詢數據列a,則沒法使用該索引。
  • 哈希索引只支持等值比較查詢,如:=,in(),<=>(注意,<>和<=>是不一樣的操做),不支持任何範圍查詢(必須給定具體的where條件值來計算hash值,因此不支持範圍查詢)。
  • 訪問哈希索引的數據很是快,除非有不少哈希衝突,當出現哈希衝突的時候,存儲引擎必須遍歷鏈表中全部的行指針,逐行進行比較,直到找到全部符合條件的行。
  • 若是哈希衝突不少的話,一些索引維護操做的代價也很高,如:若是在某個選擇性很低的列上創建哈希索引(即不少重複值的列),那麼當從表中刪除一行時,存儲引擎須要遍歷對應哈希值的鏈表中的每一行,找到並刪除對應的引用,衝突越多,代價越大。

B-Tree索引

B-Tree索引使用B-Tree樹數據結構存儲數據,大多數MySQL引擎都支持這種索引(Archive引擎是個例外)

在這裏插入圖片描述

詳細的B-Tree和B+Tree能夠參考這篇文章

B樹被做爲實現索引的數據結構被創造出來,是由於它可以完美的利用「局部性原理」。

什麼是局部性原理與磁盤預讀?

因爲存儲介質的特性,磁盤自己存取就比主存慢不少,再加上機械運動耗費,磁盤的存取速度每每是主存的幾百分分之一,所以爲了提升效率,要儘可能減小磁盤I/O。爲了達到這個目的,磁盤每每不是嚴格按需讀取,而是每次都會預讀,即便只須要一個字節,磁盤也會從這個位置開始,順序向後讀取必定長度的數據放入內存。這樣作的理論依據是計算機科學中著名的局部性原理當一個數據被用到時,其附近的數據也一般會立刻被使用。程序運行期間所須要的數據一般比較集中。

因爲磁盤順序讀取的效率很高(不須要尋道時間,只需不多的旋轉時間),所以對於具備局部性的程序來講,預讀能夠提升I/O效率。

預讀的長度通常爲**頁(page)**的整倍數。頁是計算機管理存儲器的邏輯塊,硬件及操做系統每每將主存和磁盤存儲區分割爲連續的大小相等的塊,每一個存儲塊稱爲一頁(在許多操做系統中,頁得大小一般爲4k),主存和磁盤以頁爲單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置並向後連續讀取一頁或幾頁載入內存中,而後異常返回,程序繼續運行。

B樹爲什麼適合作索引?

(1)因爲是m分叉的,高度可以大大下降;

(2)每一個節點能夠存儲j個記錄,若是將節點大小設置爲頁大小,例如4K,可以充分的利用預讀的特性,極大減小磁盤IO;

注意:高度下降的緣由在於:

  • 在利用了局部性原理前提下,咱們把一個節點的大小設爲一頁,一頁4K,假設一個KEY有8byte,一個節點能夠存儲500個KEY,即j=500

  • m叉樹,大概m/2<= j <=m,便可以差很少是1000叉樹

  • 一層樹:1個節點,1*500個KEY,大小4K

​ 二層樹:1000個節點,1000500=50W個KEY,大小10004K=4M

​ 三層樹:10001000個節點,10001000500=5億個KEY,大小10001000*4K=4G

因此:《高性能Mysql第三版》這本書也說了,通常的B+樹都不會超過三層,也就意味着絕大數數據經過三次IO就能夠找到

B樹,它的特色是:

(1)再也不是二叉搜索,而是m叉搜索;

(2)葉子節點,非葉子節點,都存儲數據;

(3)中序遍歷,能夠得到全部節點;

B+樹的特色是:

B+樹,是在B樹的基礎上,作了一些改進

  • 非葉子節點再也不存儲數據,數據只存儲在同一層的葉子節點上;

  • 葉子之間,增長了鏈表,獲取全部節點,再也不須要中序遍歷;

以上改進讓B+樹比B樹有更優的特性:

  • 範圍查找,定位min與max以後,中間葉子節點,就是結果集,不用中序回溯(範圍查詢在SQL中用得不少,這是B+樹比B樹最大的優點);

  • 葉子節點存儲實際記錄行,記錄行相對比較緊密的存儲,適合大數據量磁盤存儲;非葉子節點存儲記錄的PK,用於查詢加速,適合內存存儲;

  • 非葉子節點,不存儲實際記錄,而只存儲記錄的KEY的話,那麼在相同內存的狀況下,B+樹可以存儲更多索引;

索引體現形式

Myisam引擎

使用Myisam引擎的表在數據庫中會存在三個文件

以user表爲例:

一個是表定義文件 user.frm

一個是索引存儲文件 user.MYI

還有一個是數據存儲文件 user.MYD

由於Myisam引擎的索引和數據是分開存儲的,叫作非彙集索引UnClustered Index)。而且在B+tree樹的葉子節點存儲的是數據行的地址,在檢索數據時,以此從根節點開始檢索,直到找到對應的關鍵字,而後到數據區獲取數據行地址,最後根據這個數據行地址返回檢索的數據行

Innodb引擎

Innodb和Myisam最大的不一樣是他是以主鍵爲索引來組織數據的存儲,叫作聚簇索引(Clustered Index),也叫做彙集索引

可能你會說,若是個人表沒有主鍵怎麼辦?你也儘可放心:

  • 若是你沒有主鍵,innodb會選擇一個惟一的非空索引代替;

  • 若是沒有這樣的惟一索引可用,Innodb會本身建立一個隱式的row-id索引用於組織存儲數據,

使用Innodb引擎的表在數據庫中會存在三個文件

以teacher表爲例:

一個是表定義文件 teacher.frm

一個是數據和索引存儲文件 teacher.IBD

此處引入一個聚簇索引(也叫彙集索引):數據庫錶行中數據的物理順序與鍵值得邏輯順序(也就是索引)相同,彙集索引並非一種單獨的索引類型,而是一種數據存儲技術。

當有聚簇索引時,它的全部數據行實際上存放在索引的葉子頁中,此處應該注意的是,由於沒法同時把數據行存放在兩個不一樣的地方,因此一個表只能有一個聚簇索引。

聚簇索引的優勢
  • 能夠把相關數據保存在一塊兒
  • 數據訪問更快。聚簇索引將索引和數據保存在同一個b-tree中,所以從聚簇索引中查詢數據一般比在非聚簇索引中查找要快
  • 使用覆蓋索引(後文會有介紹)掃描的查詢能夠直接使用頁節點中主鍵值
聚簇索引的缺點
  • 插入速度嚴重依賴於插入順序。按照主鍵的順序插入是加載數據到Innodb表中速度最快的方式。
  • 更新聚簇索引列的代價很高,由於Innodb會強制將每一個被更新的行移動到新的位置
  • 聚簇索引可能致使全表掃描變慢,尤爲是行比較稀疏,或者因爲頁分裂(當行的主鍵值要求必須將這一行插入到某個已滿的頁時,存儲引擎會將該頁分裂成兩個頁面來存儲該行,這就是一頁分裂操做,頁分裂會佔用更多的磁盤空間)致使數據存儲不連續的時候
  • 二級索引訪問須要兩次索引查找

關於最後一點,是由於,二級索引葉子節點保存的並非指向行的物理位置的指針,而是保存的是主鍵值,這意味着經過二級索引查找行的時候,存儲引擎首先須要找到二級索引所對應的主鍵值,而後經過主鍵值再去聚簇索引找到對應的行。

小結

MyISAM和InnoDB都使用B+樹來實現索引:

  • MyISAM的索引與數據分開存儲
  • MyISAM的索引葉子存儲指針,主鍵索引與普通索引無太大區別
  • InnoDB的彙集索引和數據行統一存儲
  • InnoDB的彙集索引存儲數據行自己,普通索引存儲主鍵
  • InnoDB必定有且只有一個彙集索引
  • InnoDB建議使用趨勢遞增整數做爲PK,而不宜使用較長的列做爲PK

其餘索引策略

覆蓋索引

若是一個索引包含全部須要查詢的字段的值,咱們就稱之爲「覆蓋索引」

換言之,若是查詢列能夠經過索引節點中的關鍵字直接返回,則該索引稱之爲覆蓋索引

覆蓋索引的優勢
  • 索引條目一般遠小於數據行大小,由於只須要讀取索引,天然極大減小了數據訪問量,減小數據庫IO
  • 將隨機IO變成順序IO:由於索引是按照列值順序存儲的。

聯合索引

單列索引能夠理解成是一種特殊的聯合索引

不少人對多列索引的理解都不夠,一個常見錯誤哪就是 爲每一個列建立獨立的索引,或者按照錯誤的順序建立多列索引

記得以前看過一個博客說 建議把 where條件裏邊的列都加上索引,實際上這個建議是很是錯誤的。

在多個列上創建獨立的單列索引大部分狀況下並不能提升MySQL的查詢性能

聯合索引有幾個選擇原則:

  • 常常用的列優先【最左匹配原則】
  • 選擇性(離散度)高的列優先【離散度越高 選擇性越好】
  • 寬度小的列優先【最少空間原則】

哪麼如何選擇合適的索引列順序?

選擇合適的索引列順序

正確的順序依賴於使用該索引的查詢,而且同時須要考慮如何更好的知足排序和分組的須要

在B-Tree索引中,索引列的順序意味着索引首先按照最左列進行排序,其次是第二列,也就是最左匹配原則,因此多列索引的列順序相當重要。

有一個經驗法則:

將選擇性最高的列放到索引最前列

在不須要考慮排序和分組時,這個法則是很好的。由於此時索引的做用只是用於優化where條件的查找。這種狀況下這樣設計的索引可以最快的過濾出須要的行。可是查詢性能還和查詢條件的具體值以及值得分佈有關

判斷哪一個列的選擇性更高

select * from paymen where staff_id = 2 and customer_id = 584;

select sum(staff_id = 2),sum(customer_id = 584) from payment \G

select count(distinct staff_id)/count(*) as staff_id_selectivity,
count(distinct customer_id)/count(*) as customer_id_selectivity,
count(*)
from payment \G

值越大 選擇性更高
複製代碼

比你優秀的人比你還努力,你有什麼資格不去奮鬥!

選用B+Tree的緣由

  • B+樹是B-樹的變種(PLUS版)多路絕對平衡查找樹,他擁有B-樹的優點
  • B+樹掃庫、表能力更強
  • B+樹的磁盤讀寫能力更強
  • B+樹的排序能力更強
  • B+樹的查詢效率更加穩定

總結

最後在網上看到一個順口溜,以下:

全值匹配我最愛,最左前綴要遵照;

帶頭大哥不能死,中間兄弟不能斷;

索引列上少計算,範圍以後全失效;

LIKE百分寫最右,覆蓋索引不寫星;

不等空值還有or,索引失效要少用。

可謂是很精闢

歡迎關注微信公衆號 程序猿雜貨鋪 ID zhoudl_l

微信公衆號

在這裏插入圖片描述
相關文章
相關標籤/搜索