首頁 > 軟體

MySQL的索引你瞭解嗎

2022-03-10 19:15:05

一、索引介紹

索引(index)是幫助MySQL高效獲取資料的資料結構(有序)。在資料之外,資料庫系統還維護著滿足特定查詢演演算法的資料結構,這些資料結構以某種方式參照(指向)資料,這樣就可以在這些資料結構上實現高階查詢演演算法,這種資料結構就是索引。

二、索引優缺點

優點:

提高資料檢索的效率,降低資料庫的io成本通過索引列對資料進行排序,降低資料排序的成本,降低CPU的消耗。

缺點:

索引列也是要佔用空間的。索引大大提高了查詢效率,同時卻也降低更新表的速度,如對錶進行INSERT、UPDATE、DELETE時,效率降低。

三、索引結構

通常我們所說的索引,沒有特別指明,都是指B+樹結構組織的索引

B+Tree索引:最常見的索引型別,大部分引擎都支援B+樹索引

Hash索引:底層資料結構是用雜湊表實現的,只有精確匹配索引列的查詢才有效,不支援範圍查詢

R-tree(空間索引):空間索引是MyISAM引擎的一一個特殊索引型別,主要用於地理空間資料型別,通常使用較少

Full-text(全文索引):是一種通過建立倒排索引,快速匹配檔案的方式。類似於Lucene,Solr,ES 

1. 經典B+樹

看結構和B樹比較像,B+樹與B樹的區別在於:

1.所有的元素都會出現在葉子節點,非葉子節點主要起到索引的作用,而葉子節點是用來存放資料的

2.B+樹的資料結構中,葉子節點形成了一個單向連結串列,每一個節點都會通過指標指向下一個元素

2. MySQL中B+樹索引

MySQL索引資料結構對經典的B+Tree進行了優化。在原B+Tree的基礎上,增加一個指向相鄰葉子節點的連結串列指標,就形成了帶有順序指標的B+Tree,提高區間存取的效能,葉子節點雙向連結串列+首尾相連,便於範圍搜尋和排序。

3. Hash索引

雜湊索引就是採用一定的hash演演算法,將鍵值換算成新的hash值,對映到對應的槽位上,然後儲存在hash表中。

如果兩個(或多個)鍵值,對映到一個相同的槽位上,他們就產生了hash衝突(也稱為hash碰撞),可以通過連結串列來解決。

特點:

1. Hash索引只能用於對等比較(=,in), 不支援範圍查詢(between, >,<, ...)

2. 無法利用索引完成排序操作

3. 查詢效率高,通常只需要一次檢索就可以了,效率通常要高於B+tree索引

儲存引擎支援:

在MySQL中,支援hash索引的是Memory引擎,而InnoDB中具有自適應hash功能,hash索引是儲存引擎根據B+Tree索引在指定條件下自動構建的。

4. 為什麼InnoDB選擇B+樹索引?   

相對於二元樹,層級更少,搜尋效率高;

對於B-tree,無論是葉子節點還是非葉子節點,都會儲存資料,這樣導致一頁中儲存的鍵值減少,指標跟著減少,要同樣儲存大量資料,只能增加樹的高度,導致效能降低;

相對Hash索引,Hash索引只支援等值匹配,B+tree支援範圍匹配及排序操作。

四、索引分類

在InnoDB儲存引擎中,根據索引的儲存形式,又可以分為以下兩種:

聚簇索引Clustering Index):將資料儲存與索引放到了一塊,索引結構的葉子節點儲存了行資料;必須有而且只有一個。

二級索引Secondary Index):將資料與索引分開儲存,索引結構的葉子節點關聯的是對應的主鍵;可以存在多個。

聚簇索引選取規則:

如果存在主鍵,主鍵索引就是聚簇索引。

如果不存在主鍵,將使用第一個唯一(UNIQUE) 索引作為聚簇索引。

如果表沒有主鍵,或沒有合適的唯一索引,則InnoDB會自動生成一個rowid作為隱藏的聚簇索引。

如果是(非主鍵)條件查詢,則採用回表查詢,即先通過二級索引查詢主鍵(聚簇索引),得到主鍵再通過聚簇索引查詢這一行資料。

InnoDB主鍵索引的B+tree高度為多高呢?

假設:

一行資料大小為1k,一頁中可以儲存16行這樣的資料。InnoDB的指標占用6個位元組的空間,主鍵即使為bigint,佔用位元組數為8。

高度為2:

n*8+(n+ 1)*6= 16*1024 , 算出n約為1170

1171*16= 18736

高度為3:

1171 * 1171 * 16 = 21939856

五、索引語法

建立索引

CREATE [ UNIQUE | FULLTEXT ] INDEX index_ name ON table_ name ( index_ _col_ name,.. ) ;

檢視索引

SHOW INDEX FROM table_ name ;

刪除索引

DROP INDEX index_ name ON table_ name ;

六、SQL效能分析

1. SQL執行頻率

MySQL使用者端連線成功後,通過show [session|global] status命令可以提供伺服器狀態資訊。通過如下指令,可以檢視當前資料庫的INSERTUPDATEDELETESELECT的存取頻次:

show global status like 'Com_______';

2. 慢查詢紀錄檔

慢查詢紀錄檔記錄了所有執行時間超過指定引數(long_ query_ _time, 單位:秒,預設10秒)的所有SQL語句的紀錄檔。

MySQL的慢查詢紀錄檔預設沒有開啟,需要在MySQL的組態檔(/etc/my.cnf) 中設定如下資訊:

#開啟MySQL慢紀錄檔查詢開關
slow_query_log=1

#設定慢紀錄檔的時間為2秒,SQL 語句執行時間超過2秒,就會視為慢查詢,記錄慢查詢紀錄檔
long query time=2

設定完畢之後,通過以下指令重新啟動MySQL伺服器進行測試,檢視慢紀錄檔檔案中記錄的資訊/var/lib/mysql/localhost-slow.log

當某一操作時間多於2s則會被記錄在慢查詢紀錄檔中。

3. profile詳情

show profiles能夠在做SQL優化時幫助我們瞭解時間都耗費到哪裡去了。通過have_ profiling引數, 能夠看到當前MySQL是否支援profile操作:

#檢視當前資料庫是否支援profile操作
select @@have_profiling

預設profiling是關閉的,可以通過set語句在session/ global級別開啟profiling:

#開啟profiling
set profiling = 1;
#檢視每一條SQL 的耗時基本情況
show profiles;

#檢視指定query_ id的SQL語句各個階段的耗時情況
show profile for query query_ id;

#檢視指定query_ id的SQL語句CPU的使用情況
show profile cpu for query query_id;

4. explain執行計劃

EXPLAIN或者DESC命令獲取MySQL如何執行SELECT語句的資訊,包括在SELECT語句執行過程中表如何連線和連線的順序。語法:

#直接在select語句之前加,上關鍵字explain / desc
EXPLAIN SELECT 欄位列表FROM 表名WHERE 條件;

EXPLAIN執行計劃各欄位含義:

Id: 

select查詢的序列號,表示查詢中執行select子句或者是操作表的順序(id相同,執行順序從上到下; id不同,值越大,越先執行)。

select_ type:

表示SELECT的型別,常見的取值有SIMPLE (簡單表,即不使用表連線或者子查詢)、PRIMARY (主查詢,即外層的查詢)、UNION (UNION 中的第二個或者後面的查詢語句)、SUBQUERY (SELECT/WHERE之後包含了子查詢)等

type:

表示連線型別,效能由好到差的連線型別為NULL、system、 const、 eq_ref、ref、range、index、all 。

possible_ key:

顯示可能應用在這張表上的索引,一個或多個。

Key:

實際使用的索引,如果為NULL,則沒有使用索引。

Key_ len:

表示索引中使用的位元組數,該值為索引欄位最大可能長度,並非實際使用長度,在不損失精確性的前提下,長度越短越好。

rows:

MySQL認為必須要執行查詢的行數,在innodb引擎的表中,是-一個估計值,可能並不總是準確的。

filtered:

表示返回結果的行數佔需讀取行數的百分比,filtered 的值越大越好。

七、索引使用

1. 索引效率

當資料量特別大時,在未建立索引之前,執行SQL,查詢無索引欄位SQL的耗時非常大。

針對欄位建立索引後。

再次執行相同的SQL語句,SQL的耗時將大大減小。

2. 聯合索引

最左字首法則

如果索引了多列(聯合索引) , 要遵守最左字首法則。最左字首法則指的是查詢從索引的最左列開始,查詢必須包含最左邊的列(否則全部失敗),並且不跳過索引中的列。

如果跳躍某一列,索引將部分失效(後面的欄位索引失效)。

範圍查詢

聯合索引中,出現範圍查詢(>,<),範圍查詢右側的列索引失效,一般使用>=或者<=可以有效規避這種情況

3. 索引失效

索引列運算

不要在索引列上進行運算操作,索引將失效。

字串不加引號

字串型別欄位使用時,不加引號,索引將失效。

模糊查詢

如果僅僅是尾部模糊匹配,索引不會失效。如果是頭部模糊匹配,索引失效。 

or連線的條件

用or分割開的條件,如果or前的條件 中的列有索引,而後面的列中沒有索引,那麼涉及的索引都不會被用到。只有兩側都使用索引時索引才會生效。

資料分佈影響 

如果MySQL評估使用索引比全表掃描更慢,則不使用索引、索引失效。

4. SQL提示

SQL提示,是優化資料庫的一個重要手段,簡單來說,就是在SQL語句中加入一些人為的提示來達到優化操作的目的。

#  use index:
explain select * from tb_name use index(索引名) where profession= 'xxxx';

#  ignore index:
explain select * from tb_name ignore index(索引名) where profession='xxxx';

#  force index:
explain select * from tb_name force index(索引名) where profession='xxxx';

5. 覆蓋索引

儘量使用覆蓋索引(查詢使用了索引,並且需要返回的列,在該索引中已經全部能夠找到),減少 select * 。

在Extra欄位中出現的資料分析:

using index condition:查詢使用了索引,但是需要回表查詢資料

using where; using index:查詢使用了索引,但是需要的資料都在索引列中能找到,所以不需要回表查詢資料

6. 字首索引

當欄位型別為字串(varchar, text等 ),時,有時候需要索引很長的字串,這會讓索引變得很大,查詢時,浪費大量的磁碟IO,影響查詢效率。此時可以只將字串的一部分字首建立索引,這樣可以大大節約索引空間,從而提高索引效率。

#語法
create index idx_xxx on table_ name(column(n)) ;
#字首長度
可以根據索引的選擇性來決定,而選擇性是指不重複的索引值(基數)和資料表的記錄總數的比值,索引選擇性越高則查詢效率越高,
唯一索引的選擇性是1,這是最好的索引選擇性,效能也是最好的。

# 求取選擇性
 select count(distinct email)/ count(*) from tb_name ;
 select count(distinct substring(email,1 ,5)) / count(*) from tb_name ;

7. 單列索引與聯合索引

單列索引:即一個索引只包含單個列。

聯合索引:即一個索引包含了多個列。

在業務場景中,如果存在多個查詢條件,考慮針對於查詢欄位建立索引時,建議建立聯合索引(效率較高、有效規避一些回表查詢),而非單列索引。

多條件聯合查詢時,MySQL優化器會評估哪個欄位的索引效率更高,會選擇該索引完成本次查詢。當建立了聯合索引時會有單列索引干擾,我們可以指定聯合索引查詢。

聯合索引情況:

八、索引設計原則

1. 針對於資料量較大,且查詢比較頻繁的表建立索引。

2.針對於常作為查詢條件(where) 、排序(order by)、分組(group by)操作的欄位建立索引。

3.儘量選擇區分度高的列作為索引,儘量建立唯一索引,區分度越高,使用索引的效率越高。

4.如果是字串型別的欄位, 欄位的長度較長,可以針對於欄位的特點,建立字首索引。

5.儘量使用聯合索引, 減少單列索引,查詢時,聯合索引很多時候可以覆蓋索引,節省儲存空間,避免回表,提高查詢效率。

6.要控制索引的數量, 索引並不是多多益善,索引越多,維護索引結構的代價也就越大,會影響增刪改的效率。

7.如果索引列不能儲存NULL值,請在建立表時使用NOT NULL約束它。當優化器知道每列是否包含NULL值時,它可以更好地確定哪個索引最有效地用於查詢。

總結

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!  


IT145.com E-mail:sddin#qq.com