mysql(一)瞭解索引以及所以類型

一、瞭解索引

當我們使用漢語字典查找某個字時,我們會先通過拼音目錄查到那個字所在的頁碼,然後直接翻到字典的那一頁,找到我們要查的字,通過拼音目錄查找比我們拿起字典從頭一頁一頁翻找要快的多,數據庫索引也一樣,索引就像書的目錄,通過索引能極大提高數據查詢的效率。


索引的實現方式

在數據庫中,常見的索引實現方式有哈希表、有序數組、搜索樹

  • 哈希表
    哈希表是通過鍵值對(key-value)存儲數據的索引實現方式,可以將哈希表想象成是一個數組,將索引通過哈希函數計算得到該行數據在數組中的位置,然後將數據存到數組中,容易發現一個問題,如果兩個索引通過哈希函數計算後得到的數組位置相同要怎麼辦?在這裏,數組的每個value都是一個鏈表,鏈表上的每個元素都是一個數據,新數據直接添加到鏈表尾部。

    哈希表.png


    所以數據庫查詢過程爲:索引通過哈希函數計算數據所在位置--> 遍歷指定位置的鏈表,找到滿足條件的數據。
    要注意的是,鏈表上的數據元素不是有序的,每次有新數據加入時,新數據時直接添加到鏈表尾部,這樣做的好處是添加數據時很方便。
           哈希表不擅長進行區間查詢,一般都用於等值查詢
              1、兩個相鄰索引通過hash函數後計算得到的數組位置不一定還保持相鄰
              2、鏈表上的數據是無序的

     

  • 有序數組
    顧名思義,有序數組是按索引大小將數據保存在一個數組上,因爲該數組是有序的,可以通過二分法很容易查到位置,找到第一個位置後,通過向左/向右遍歷很容易得到所求區間的數據。因此,無論是等值查詢還是區間查詢,效率都極高。
    但缺陷也是顯而易見的,當向數組中間n位置插入一條數據時,需將n後面的數據全部往後移動,所以,這種索引一般用於靜態存儲引擎。

  • 搜索樹

二叉搜索樹:一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; 二叉搜索樹的左、右子樹也分別爲二叉搜索樹。
平衡二叉樹:平衡二叉樹是在二叉搜索樹的基礎上引入的,指的是結點的左子樹和右子樹的深度差不超過1.
多叉樹:每個結點可以有多個子結點,子節點的大小從左到右依次遞增。

當使用平衡二叉實現索引時,結構如下圖

 

圖片來自課程文章

 

從圖中可發現,每次查詢最多需要訪問4個節點必能得到所要數據。例如查詢user2時,查詢過程爲:userA-->userC-->userF-->user2。
所以查詢速度很高,同時,因爲搜索樹的特性(左子樹小於右子樹),區間查詢也很方便。

如果搜索樹存於內存中,與多叉樹相比,二叉樹的搜索速率是最高的,但實際上數據庫使用的是n叉樹而不是二叉樹。

1、索引不僅存於內存,還是寫到磁盤上
2、搜索樹上的每個結點在磁盤上表現爲一個數據塊
3、多叉樹每個結點下可以有多個子節點,所以存儲相同數據量時多叉樹的樹高比二叉樹小,查詢一個數據需要訪問的結點數更少,即查詢過程訪問更少的數據塊。查詢速度較高。


innodb的索引模型

innodb使用B+樹作爲索引結構。
在B+樹中,我們將節點分爲葉子結點和非葉子結點,非葉子結點上保存的是索引,而且一個節點可以保存多個索引;數據全部存於葉子結點上,根據葉子結點的內容不同,innodb索引分爲主鍵索引和非主鍵索引。非主鍵索引也稱爲二級索引。
主鍵索引的葉子結點中保存的數據爲整行數據,而非主鍵索引葉子節點保存的是主鍵的值。

 

主鍵索引圖

 

非主鍵索引圖

 

通過主鍵索引查詢數據時,我們只需查找主鍵索引樹便可以獲取數據;通過非主鍵索引查詢數據時,我們先通過非主鍵索引樹查找到主鍵值,然後再在主鍵索引樹搜索一次,這個過程稱爲回表,也就是說非主鍵索引查詢會比主鍵查詢多搜索一棵樹。所以我們應儘可能使用主鍵查詢。

索引維護

添加新行時,將會在索引表上添加一條記錄,如果是索引遞增插入時,數據都是追加在當前最大索引之後,不會對樹中其他數據造成影響;如果新加入的數據的索引值位於節點的中間,需要挪動部分節點的位置,從而保持索引樹的有序性。
而且,相鄰多個節點是存儲在同一個數據頁上的,此時,如果是在已經存儲滿狀態的數據頁中插入節點,會申請新的數據頁,將部分數據挪動到新的數據頁,這個過程稱爲頁分裂,頁分裂除了會影響性能,還會降低磁盤空間利用率。不規則數據插入時,會造成頻繁的頁分裂。

當相鄰兩個頁由於刪除了數據,利用率很低之後,會將數據頁做合併

所以,一般情況下會採用遞增主鍵,使新數據遞增插入。

使用業務邏輯字段做主鍵有什麼優缺點?

1、業務邏輯字段不容易保證索引樹結點有序插入,這樣寫入成本較高。
2、innodb默認使用整數類型作爲主鍵,主鍵長度較小,二級索引的葉子結點中保存的是主鍵值,主鍵長度越小,二級索引的葉子結點佔用空間也就越小。
3、當然,使用業務邏輯字段做主鍵也有好處,可以避免回表,每次只需掃描一次主鍵索引樹即可
綜上,從性能和存儲空間方面考量,自增主鍵往往是更合理的選擇,當業務場景有且只有一個索引,而且該索引爲唯一索引時,此時更適合使用業務邏輯字段作爲主鍵。

因爲數據修改/刪除、頁分裂等原因,會導致數據頁空間利用率降低,此時,可以考慮重建索引,將數據按順序插入,提高磁盤空間利用率。但重建主鍵索引和普通索引會有不同影響,重建普通索引,可以達到提高空間利用率的目的,且不會對其他索引造成影響,但如果重建主鍵索引就不合理了,會影響所有普通索引,性能影響較大,而且無論是新建/刪除主鍵,都會重建整張表。這時我們可以使用alter table T engine=InnoDB這個語句代替。

以上轉載自:鏈接

二、索引採用哪種數據結構?

Hash索引和B+ Tree索引,mysql默認的是InnoDB引擎,默認的是B+樹。

優缺點:B+ Tree索引和Hash索引區別 哈希索引適合等值查詢,但是不無法進行範圍查詢 哈希索引沒辦法利用索引完成排序 哈希索引不支持多列聯合索引的最左匹配規則 如果有大量重複鍵值得情況下,哈希索引的效率會很低,因爲存在哈希碰撞問題。

三、索引類型

索引包括:

1.普通索引(NORMAL)
2.唯一索引(UNIQUE)
3.主鍵索引(PRIMARY)
4.組合索引(多個字段的普通索引)
5.組合唯一索引(多個字段的唯一索引)
5.全文索引(FULLTEXT)

1、聚簇索引(聚集索引 clustered index)、非聚簇索引(普通索引 secondary index)

InnoDB的B+ Tree可能存儲的是整行數據,也有可能是主鍵的值。索引B+ Tree的葉子節點存儲了整行數據的是主鍵索引,也被稱之爲聚簇索引;而索引B+ Tree的葉子節點存儲了主鍵的值的是非主鍵索引,也被稱之爲非聚簇索引。

區別:聚簇索引會更快,涉及到回表查詢

InnoDB聚集索引的葉子節點存儲行記錄,因此, InnoDB必須要有,且只有一個聚集索引:

(1)如果表定義了PK,則PK就是聚集索引;

(2)如果表沒有定義PK,則第一個not NULL unique列是聚集索引;

(3)否則,InnoDB會創建一個隱藏的row-id作爲聚集索引;

畫外音:所以PK查詢非常快,直接定位行記錄。

InnoDB普通索引的葉子節點存儲主鍵值。

畫外音:注意,不是存儲行記錄頭指針,MyISAM的索引葉子節點存儲記錄指針。

舉個栗子,不妨設有表:

t(id PK, name KEY, sex, flag);

畫外音:id是聚集索引,name是普通索引。

表中有四條記錄:

1, shenjian, m, A

3, zhangsan, m, A

5, lisi, m, A

9, wangwu, f, B

image

兩個B+樹索引分別如上圖:

(1)id爲PK,聚集索引,葉子節點存儲行記錄;

(2)name爲KEY,普通索引,葉子節點存儲PK值,即id;

既然從普通索引無法直接定位行記錄,那普通索引的查詢過程是怎麼樣的呢?

通常情況下,需要掃碼兩遍索引樹。

例如:

select * from t where name='lisi';

是如何執行的呢?

image

粉紅色路徑,需要掃碼兩遍索引樹:

(1)先通過普通索引定位到主鍵值id=5;

(2)在通過聚集索引定位到行記錄;

這就是所謂的回表查詢,先定位主鍵值,再定位行記錄,它的性能較掃一遍索引樹更低。

2、什麼是索引覆蓋****(Covering index)****?

額,樓主並沒有在MySQL的官網找到這個概念。

畫外音:治學嚴謹吧?

借用一下SQL-Server官網的說法。

image

MySQL官網,類似的說法出現在explain查詢計劃優化章節,即explain的輸出結果Extra字段爲Using index時,能夠觸發索引覆蓋。

image

不管是SQL-Server官網,還是MySQL官網,都表達了:只需要在一棵索引樹上就能獲取SQL所需的所有列數據,無需回表,速度更快。

3、如何實現索引覆蓋?

常見的方法是:將被查詢的字段,建立到聯合索引裏去。

仍是《迅猛定位低效SQL?》中的例子:

create table user (

id int primary key,

name varchar(20),

sex varchar(5),

index(name)

)engine=innodb;

第一個SQL語句:

image

select id,name from user where name='shenjian';

能夠命中name索引,索引葉子節點存儲了主鍵id,通過name的索引樹即可獲取id和name,無需回表,符合索引覆蓋,效率較高。

畫外音,Extra:Using index

第二個SQL語句:

 

image

 

select id,name,sex* from user where name='shenjian';*

能夠命中name索引,索引葉子節點存儲了主鍵id,但sex字段必須回表查詢才能獲取到,不符合索引覆蓋,需要再次通過id值掃碼聚集索引獲取sex字段,效率會降低。

畫外音,Extra:Using index condition

如果把(name)單列索引升級爲聯合索引(name, sex)就不同了。

create table user (

id int primary key,

name varchar(20),

sex varchar(5),

index(name, sex)

)engine=innodb;

image

可以看到:

select id,name ... where name='shenjian';

select id,name,sex* ... where name='shenjian';*

都能夠命中索引覆蓋,無需回表。

畫外音,Extra:Using index

4、哪些場景可以利用索引覆蓋來優化SQL?

場景1:全表count查詢優化

image

原表爲:

user(PK id, name, sex);

直接:

select count(name) from user;

不能利用索引覆蓋。

添加索引:

alter table user add key(name);

就能夠利用索引覆蓋提效。

場景2:列查詢回表優化

select id,name,sex ... where name='shenjian';

這個例子不再贅述,將單列索引(name)升級爲聯合索引(name, sex),即可避免回表。

場景3:分頁查詢

select id,name,sex ... order by name limit 500,100;

將單列索引(name)升級爲聯合索引(name, sex),也可以避免回表。

InnoDB聚集索引普通索引回表索引覆蓋,希望這1分鐘大家有收穫。

以上轉載自:鏈接

5、索引下推

對於user_table表,我們現在有(username,age)聯合索引
如果現在有一個需求,查出名稱中以“張”開頭且年齡小於等於10的用戶信息,語句C如下:"select * from user_table where username like '張%' and age > 10".
語句C有兩種執行可能:
1)、根據(username,age)聯合索引查詢所有滿足名稱以“張”開頭的索引,然後回表查詢出相應的全行數據,然後再篩選出滿足年齡小於等於10的用戶數據。過程如下圖。

 

2)、根據(username,age)聯合索引查詢所有滿足名稱以“張”開頭的索引,然後直接再篩選出年齡小於等於10的索引,之後再回表查詢全行數據。過程如下圖。

 

明顯的,第二種方式需要回表查詢的全行數據比較少,這就是mysql的索引下推。mysql默認啓用索引下推,我們也可以通過修改系統變量optimizer_switch的index_condition_pushdown標誌來控制

SET optimizer_switch = 'index_condition_pushdown=off';
  • 注意點:
    1、innodb引擎的表,索引下推只能用於二級索引。

    就像之前提到的,innodb的主鍵索引樹葉子結點上保存的是全行數據,所以這個時候索引下推並不會起到減少查詢全行數據的效果。

    2、索引下推一般可用於所求查詢字段(select列)不是/不全是聯合索引的字段,查詢條件爲多條件查詢且查詢條件子句(where/order by)字段全是聯合索引。

    假設表t有聯合索引(a,b),下面語句可以使用索引下推提高效率
    select * from t where a > 2 and b > 10;

參考:https://www.jianshu.com/p/bdc9e57ccf8bhttps://www.jianshu.com/p/8991cbca3854

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章