索引是存儲引擎用於快速找到記錄數據行的一種分散存儲的數據結構。sql
索引對於良好的性能很是關鍵,尤爲是當表中的數據量愈來愈大時,索引對性能的影響愈發重要。數據庫
在數據量較小且負載較低時,不恰當的索引對性能的影響可能還不明顯,可是當數據量逐漸增大時,性能則會急劇降低。編程
因此 正確的建立合適的索引是提高數據庫查詢性能的基礎。bash
索引有不少類型,能夠爲不一樣的場景提供更好的性能。微信
MySQL中,索引是在存儲引擎層面實現的,因此,並無統一的索引標準,通常來講,不一樣存儲引擎的工做方式是不同的,也不是全部的存儲引擎都支持全部類型的索引數據結構
哈希索引基於哈希表實現,只有精確匹配索引全部列的查詢纔有效。性能
對於每個數據行,存儲引擎都會對全部的索引列根據必定的計算規則計算出一個哈希碼,而後哈希索引將全部的哈希碼存儲在索引中,同時在哈希表中會保存一個指向對應數據行的指針。大數據
MySQL中,Memory引擎是顯式支持哈希索引的,他也是該引擎默認的索引類型,值得注意的一點是:Memory引擎是支持非惟一哈希索引的,也就是說若是多個列的哈希值相同,索引會以鏈表的方式存放多個記錄指針到同一個哈希表中。優化
根據本人的理解,這種直接經過哈希索引的存儲引擎,由於索引自身只須要存儲對應的哈希值,因此索引的結構十分緊湊,這會讓哈希索引查找的速度很是快ui
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就能夠找到
(1)再也不是二叉搜索,而是m叉搜索;
(2)葉子節點,非葉子節點,都存儲數據;
(3)中序遍歷,能夠得到全部節點;
B+樹,是在B樹的基礎上,作了一些改進:
非葉子節點再也不存儲數據,數據只存儲在同一層的葉子節點上;
葉子之間,增長了鏈表,獲取全部節點,再也不須要中序遍歷;
以上改進讓B+樹比B樹有更優的特性:
範圍查找,定位min與max以後,中間葉子節點,就是結果集,不用中序回溯(範圍查詢在SQL中用得不少,這是B+樹比B樹最大的優點);
葉子節點存儲實際記錄行,記錄行相對比較緊密的存儲,適合大數據量磁盤存儲;非葉子節點存儲記錄的PK,用於查詢加速,適合內存存儲;
非葉子節點,不存儲實際記錄,而只存儲記錄的KEY的話,那麼在相同內存的狀況下,B+樹可以存儲更多索引;
使用Myisam引擎的表在數據庫中會存在三個文件
以user表爲例:
一個是表定義文件 user.frm
一個是索引存儲文件 user.MYI
還有一個是數據存儲文件 user.MYD
由於Myisam引擎的索引和數據是分開存儲的,叫作非彙集索引(UnClustered Index
)。而且在B+tree樹的葉子節點存儲的是數據行的地址,在檢索數據時,以此從根節點開始檢索,直到找到對應的關鍵字,而後到數據區獲取數據行地址,最後根據這個數據行地址返回檢索的數據行
Innodb和Myisam最大的不一樣是他是以主鍵爲索引來組織數據的存儲,叫作聚簇索引(Clustered Index
),也叫做彙集索引。
可能你會說,若是個人表沒有主鍵怎麼辦?你也儘可放心:
若是你沒有主鍵,innodb會選擇一個惟一的非空索引代替;
若是沒有這樣的惟一索引可用,Innodb會本身建立一個隱式的row-id索引用於組織存儲數據,
使用Innodb引擎的表在數據庫中會存在三個文件
以teacher表爲例:
一個是表定義文件 teacher.frm
一個是數據和索引存儲文件 teacher.IBD
此處引入一個聚簇索引(也叫彙集索引):數據庫錶行中數據的物理順序與鍵值得邏輯順序(也就是索引)相同,彙集索引並非一種單獨的索引類型,而是一種數據存儲技術。
當有聚簇索引時,它的全部數據行實際上存放在索引的葉子頁中,此處應該注意的是,由於沒法同時把數據行存放在兩個不一樣的地方,因此一個表只能有一個聚簇索引。
關於最後一點,是由於,二級索引葉子節點保存的並非指向行的物理位置的指針,而是保存的是主鍵值,這意味着經過二級索引查找行的時候,存儲引擎首先須要找到二級索引所對應的主鍵值,而後經過主鍵值再去聚簇索引找到對應的行。
MyISAM和InnoDB都使用B+樹來實現索引:
若是一個索引包含全部須要查詢的字段的值,咱們就稱之爲「覆蓋索引」
換言之,若是查詢列能夠經過索引節點中的關鍵字直接返回,則該索引稱之爲覆蓋索引
單列索引能夠理解成是一種特殊的聯合索引
不少人對多列索引的理解都不夠,一個常見錯誤哪就是 爲每一個列建立獨立的索引,或者按照錯誤的順序建立多列索引
記得以前看過一個博客說 建議把 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
值越大 選擇性更高
複製代碼
最後在網上看到一個順口溜,以下:
全值匹配我最愛,最左前綴要遵照;
帶頭大哥不能死,中間兄弟不能斷;
索引列上少計算,範圍以後全失效;
LIKE百分寫最右,覆蓋索引不寫星;
不等空值還有or,索引失效要少用。
可謂是很精闢
歡迎關注微信公衆號 程序猿雜貨鋪 ID zhoudl_l
微信公衆號
![]()