MySQL索引原理及其優化

MySQL索引原理及其優化

之前有寫過一篇關於索引的文章,但是感覺寫的不好,因此重新總結一下,當做複習,爲明年的春招做準備,歡迎互關呀,共同學習,進步!

一,索引原理

我們之前可能都使用過索引,但是我們有沒有了解過索引的底層原理呢?

1.底層數據結構

我們知道,索引幫助MySQL高效獲取數據的排好序的數據結構

既然,索引是一種數據結構,我們瞭解原理,就有必要喫透這種數據結構

在這裏我先拋出結論:

MySQL中的索引是在存儲引擎實現的,不同存儲引擎對索引的實現可能有所不同,常用的MyISAM和innodb都使用B+樹實現索引,但是這兩者又有些不同。

1.爲什麼使用B+樹實現索引?

索引絕大多數情況都是用在查詢操作當中,那麼我們列舉下我們知道的查找算法

順序查找:

在這裏插入圖片描述

對於這樣一張表,如果我要做這樣的查詢

select * from tb_stu where stu_name = 'walker'

如果是順序查找,那麼我就得先從第一行數據,一行一行的比對,運氣好的,第一行就找到了符合條件的數據行,運氣不好,就得遍歷到最後一行才找到,所以這樣的時間複雜度爲O(N),N是數據表的行數,當數據表的數據非常多時,效率就會原來越差,IO次數隨着增多

BST二叉搜索樹

有人說,二叉搜索樹的時間複雜度可達O(logN),但是二叉搜索樹有可能會退化爲左斜樹或者右斜樹

在這裏插入圖片描述

這樣時間複雜度就會有O(logN)變爲O(N)

AVL平衡樹

有人會說,使用AVL平衡樹可以避免搜索樹退化爲左斜樹或者右斜樹,但是這樣又會帶來一個問題,當數據量比較大時,樹的層次會越來越大,這樣即使時間複雜度可以達到O(logN),但是IO次數是隨着樹的高度增加而增加的。

B樹

B樹能很好的解決查詢效率和IO次數多的問題,但是當我做範圍查詢:

select * from table where class_id > 5

img

B樹這時就不好處理了,因爲B樹要多次遍歷,比如,上邊的sql,B樹在找到第一個大於5的數據6後,還需要重新從根節點在遍歷找第二個大於5的數據,所以這樣IO次數也是比較不穩定的。

B+樹:

針對上邊B樹的情況,使用變種的B樹----B+樹可以解決,B樹的所有數據都儲存在葉子節點,所有葉子節點組成是一條鏈表

在這裏插入圖片描述

當我們進行範圍查詢時,查到葉節點後可以遍歷鏈表,找到我們想要的數據

img

所以,B+樹要比B樹具有更穩定的查詢效率,因爲B+樹樹的高度相對穩定,3層的B+樹就可以存儲百萬條數據,所以查詢次數也相對穩定O(N),N是樹的高度,在樹的高度不是很高的情況下,且加上葉子層的鏈表,IO次數相對B樹也大大減少。

所以,大多數數據庫採用B+樹實現索引,但也有用B樹實現索引的,例如MongoDB

2.索引在MyISAM和innodb中的實現

以這個表爲例,col是主鍵

在這裏插入圖片描述

索引在MyISAM的實現

MyIsam引擎使用B+樹來實現索引,在B+樹的葉子節點數據域中存放的mysql表數據記錄的地址

在这里插å¥å›¾ç‰‡æè¿°

在col2上建立一個普通索引,也是一樣的存放

在这里插å¥å›¾ç‰‡æè¿°

所以特點就是:數據和索引是分開存放的, 在進行sql查詢時,會根據查詢語句中的索引條件,在b+樹中找到相應的葉子節點,取出相應的數據地址,然後根據地址獲取相關表記錄

索引在innodb中的實現

雖然innerDB也使用b+樹作爲索引結構,但是實現方式和myisam不同,在主鍵索引中,b+樹葉子節點數據域中存儲的是數據庫表相關列的完整記錄

在这里插å¥å›¾ç‰‡æè¿°

在其他索引中,比如col3建立了一個普通索引,存放的是該列對應的主鍵值

在这里插å¥å›¾ç‰‡æè¿°

這樣我們在利用col3查數據時,就會先找出對應的主鍵值,然後在從主鍵索引中找出對應的行數據,這種也叫作回表查詢, 先定位主鍵值,再定位行記錄,它的性能較掃一遍索引樹更低。

所以說,innodb實現的主鍵索引葉節點保存了完整數據,非主鍵索引葉節點保存主鍵值

在innodb中實現索引爲還是那麼必須有主鍵?

  • 因爲innodb中是通過主鍵索引去組織數據,如果沒有主鍵索引,MySQL會自動找出能唯一標識行數據的一列作爲主鍵列,如果沒有則會生成一個默認的隱藏列作爲主鍵,去維護葉數據

爲什麼儘量使用整形自增主鍵?

  • 比如,我現在不使用整形自增主鍵,我使用uuid作爲主鍵,也能起到主鍵唯一標識的作用,但是uuid是字符串,字符串在比較上比整形要麻煩,所以還是推薦使用整形主鍵
  • 對於爲什麼推薦自增,因爲,自增的主鍵每次都比原來B+樹中的主鍵大,所以,在新增數據時都會將節點添加到最後一個葉子節點,而減少了B+樹維護平衡的消耗

聚集索引和非聚集索引

上邊提到的

  • 在innodb上實現的索引也被稱爲聚集索引
  • 在MyISAM中實現的索引被稱爲非聚集索引

對比:

  • 聚集索引在進行主索引查詢時效率很高,直接找到相關葉子節點就可以找到相關數據記錄,非聚集索引還需要根據數據地址去尋找數據記錄
  • 但是聚集索引在輔助索引查詢時效率低非聚集索引低,因爲需要檢索兩次b+樹,第一次找到主鍵值,第二次根據主鍵值找到相關記錄、

3.哈希索引

只有memory(內存)存儲引擎支持哈希索引,哈希索引用索引列的值計算該值的hashCode,然後在hashCode相應的位置存執該值所在行數據的物理位置,因爲使用散列算法,因此訪問速度非常快,但是一個值只能對應一個hashCode,而且是散列的分佈方式,因此哈希索引不支持範圍查找和排序的功能,但是hash適用於 = 或者 in的等值查詢

4.聯合索引的B+樹如何組織?

如果是聯合索引的話,一個聯合索引還是維護一棵B+樹,但是這個B+樹每一個節點是多個列的,意思就是說,原來獨立索引,一個節點一個列嘛,現在聯合索引節點是多列的,列的順序按照聯合索引順序排,比如聯合索引key(a,b),會先檢索a,然後在檢索b

二,索引優化

1.儘量不要出現重複索引

第一步

儘量不要出現重複索引,何爲重複索引?重複索引是指相同的列以相同的順序建立的同類的索引,比如主鍵和唯一索引

create table test(
	id int not null primay key,
    name varchar(10) not null,
	title varchar(50) not null,
    unique(id)
)engine = innodb

就像這裏邊的id既是唯一索引也是主鍵,就是重複索引.

2.減少冗餘索引

第二:

減少冗餘索引,什麼是冗餘索引就是指多個索引的前綴列相同,或是在聯合索引中包含了主鍵索引

create table test(
	id int not null primay key,
    name varchar(10) not null,
	title varchar(50) not null,
    key(name,id)
)engine = innodb

上邊就是在聯合索引中包含了主鍵索引,導致索引冗餘,因爲innodb的特性,innodb會在沒個索引的後邊加上主鍵索引,

還有前綴列相同就是下邊說到的聯合索引的情況,

建立了聯合索引(a,b,c)就沒必要建立一個獨立索引a

3.where子句後邊的列索引只能用上1個

第三:

注意where子句後邊的列不能都加上索引

select * from table where age = 1 and price > 100

where子句後邊的age和price如果兩個都是獨立的索引,那麼同時只能用上1個,此時應該建立age和price的聯合索引

4.多列索引必須遵循最左匹配原則

第四:

多列索引必須遵循最左匹配原則,索引順序應該遵循離散度大的列放的越前

先說最左匹配原則

所謂最左匹配原則,就是假如我創建了聯合索引index(a,b,c)

如果,我在查詢中:

where a = 1 and b = 1 and c = 1	#索引中abc三列都走
where a = 1 and b = 1 #索引中ab兩列走
where a = 1 and c = 1 #索引中ac兩列走

所以從上面可以看出,最左匹配中的一個要求:索引中排第一的列必須出現,索引纔會生效,比如上邊是索引生效的組合可以有:

abc
ab
ac
a

所以不生效的如:

bc
b
c

上面都是等值查詢,下邊涉及到範圍查詢

1. where a = 1 and b = 1 and c > 1
2. where a = 1 and b > 1 and c = 1

上邊的where子句中只有第一句會走索引,走索引中的abc列,第二句不走列c,這涉及到多列索引中第二個要求:當在查詢中出現範圍查詢時, 存儲引擎不能使用索引中範圍條件右邊的列

還有,建立了聯合索引(a,b,c)就沒必要建立獨立索引(a)

如果有order by或者group by的情景,也要注意索引的有序性

比如:

where a = ? and b = ? order by c

這樣可以建立key(a,b,c)的聯合索引,order by 最後的字段是組合索引的一部分且放在組合索引最後,避免出現文件排序fileSort

5.儘量使用覆蓋索引

第五:

**儘量使用覆蓋索引(只訪問索引的查詢(索引列包含的查詢列)減少select ***

比如在登錄驗證中:

select user_time from user where user_name = ? and password = ?

可以建立key(user_name,password,user_time)的聯合索引

6.前導模糊查詢不能使用索引

第六:

前導模糊查詢不能使用索引

select * from table where title like  '%ax'

該語句屬於前導模糊查詢,即使title是索引,也不能使用到

非前導模糊查詢可以使用索引

select * table from title like 'abc%'

7.union,in,or都能命中索引推薦使用in

第七:

union,in,or都能命中索引,但是cpu耗費上,union < in < or

所以一般推薦使用in

比如:

select * from table where a = 1
union
select * from table where a = 2

直接告訴mysql怎麼做,cpu耗費最少,但是一般推薦使用in

select * from table where a in (1,2)

or的話,cpu耗費最大,不建議

select * from table where a = 1 or a = 2

8.負向條件查詢不能使用索引,可以優化爲in查詢

第八:

負向條件查詢不能使用索引,可以優化爲in查詢,負向條件有:!=,< >,not in , not exists , not like

9.建立索引的列,不能爲null,聯合索引不存全爲null的值

第九:

建立索引的列,不能爲null,聯合索引不存全爲null的值

10.使用索引查詢時,避免強制類型轉換

第十:

在使用索引查詢時,避免強制類型轉換,強制類型轉換會導致全表掃描,例如,phone本來是varchar類型

select * from table where phone = 123456789

這樣就不能命中索引.

應改爲:

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