一文搞懂MySQL字首索引

2021-10-06 05:04:10 字數 3629 閱讀 6155

通常在開發中我們需要定義字串型別的字段,例如使用者名稱或者使用者郵箱等。

假設我們在維護乙個使用者登入系統,使用者表的定義:

create

table

user

(id bigint

unsigned

primary

key,

email varchar(64

))engine

=innodb

;

如果使用郵箱登入的話,查詢語句可能這樣寫:

select id  from

user

where email=

'***'

;

如果email欄位沒有加索引,那麼這個語句只能做全表掃瞄。

mysql是支援字首索引的,也就是說,你可以定義字串的一部分作為索引。如果不指定字首索引,那麼索引就是整個字串。

例子:

alter

table

user

addindex index1(email)

;alter

table

user

addindex index2(email(6)

);

第一句sql建立的索引就是將email整個字串作為索引;第二個sql語句建立的索引,只取email字串的前6個位元組作為索引。

儲存過程中的具體區別如下圖所示

顯然可以從圖中知道,email(6)這個索引結構中每個郵箱欄位只取前6個位元組,所以占用的空間更少,這就是使用字首索引的優勢。

缺點:可能會額外的增加記錄掃瞄的次數。

這個該怎麼理解呢?

select id,name,email from

user

where email =

' [email protected]'

;

使用的是將整個字串作為索引結構。過程如下:

從index1索引樹上找到索引值是"[email protected]"的這條記錄,去的id2的值

到主鍵中查id2的這一行,判斷email的值是否是正確的,將這行記錄裝入結果集中;

再回到index1這個索引樹上,繼續判斷下一條記錄,發現不滿足where條件,結束迴圈。

這個過程中只需要從主鍵索引樹上查詢一次資料,系統自認為掃瞄了一行。

使用字首索引的執行過程

從index2的索引樹上,找到滿足索引值是「zhangs」的記錄,找到第乙個是id1;

到主鍵索引樹上查到id1這一行,判斷email的值滿不滿足where後的條件,不滿足這一行丟棄。

繼續回到index2這個索引樹上查下一條記錄,發現如果還是"zhangs",取出id2,再回到id2索引樹上進行判斷,如果值正確,將結果返回結果集中。

重複執行以上流程,直到從index2索引樹上取出的資料不是「zhangs」,迴圈結束。

通過以上執行流程的分析你就可以知道,字首索引會導致掃瞄的行數變多,這和你所指定字首的長度有關。或許email(7)中的區分度就比email(6)高,就不會掃瞄那麼多行。

也即是說使用字首索引,定義好長度,就可以節省空間又不用額外增加太多的查詢成本

那怎樣定義字首索引長度比較好呢?

實際上,建立索引時關注的是區分度,區分度越高,越能體現索引的價值和他的優勢。因此我們可以通過統計索引上有不同的值來判斷要使用多長的字首。

select

count

(distinct email)

as l from

user

;

字首索引對覆蓋索引的影響

前面我們說了使用字首索引可能會增加掃瞄行數,這會影響效能。其實字首索引的影響不止如此:

select id ,email from

user

where email=

'[email protected]'

;select id , name, email from

user

where email=

'[email protected]'

;

第一句sql相比於第二條sql,只返回了id和email。如果使用email整個字串作為索引的話,可以利用覆蓋索引,從index1查到結果直接返回,不需要回表。但是如果使用字首索引的話,是需要回表進行判斷的。

在選取索引的時候,我們需要明白:索引選取的越長,占用的磁碟空間就越大,相同的資料頁能放下的索引值就越小,搜尋的效率也就會越低。

如果我們在區分度不是很高的場景下,字首索引的效果就不明顯了,我們該如何才去措施提高查詢效率。

採用倒序儲存方式

select filed_list from t where id_card = reverse(

'input_id_card'

)

因為字串正序的區分度不夠明顯所以可以看看如果採用倒序的話情況如何,如果倒序的區分度更高,可以採用這種方式。

採用hash欄位

alter

table t add id_card_crc int

unsigned

,add

index

(id_card_crc)

;

這裡在表t中多加入了乙個字段 id_card_crc並把它作為索引。

然後每次插入新紀錄的時候,都用crc32函式得到校驗碼填充到這個新字段中。由於產生的校驗碼也有可能衝突(相同)所以查詢條件部分需要判斷id_card的值是否相同。

select field_list from t

where id_card_crc=crc32(

'input_id_card_string'

)and

id_card=

'input_id_card_string'

兩者的對比

相同點都不支援範圍查詢,只能等值查詢。

不同點從查詢效率上看,使用的hash欄位方式的查詢效能相對穩定一點,因為crc_32算出的值雖然有可能衝突,但是概率還是很小的。而倒序方式其實還是用的字首索引的方式還會增加掃瞄行數。

從儲存空間上看,倒序儲存不會在主鍵上消耗額外的空間,但hash欄位需要增加乙個新字段。

從cpu消耗來講,倒序每次寫和讀的時候都需要呼叫reverse函式;hash欄位的方式需要嗲用crc32函式。從函式的複雜度講,reverse效率更高一些。

在向字串型別的字段加索引的時候,需要考慮字首索引是否合適,實在不行再加全欄位索引。

全欄位索引相比於字首索引占用的空間多些。

建立字首索引節省空間,但是會增加查詢的掃瞄行數,並且加了之後不能使用覆蓋索引。

倒序儲存是基於字首索引的改良版,用於字串本身區分度不高的情況下。

建立hash欄位索引,查詢穩定但需增加乙個額外的字段。

一文搞懂MySQL的Join

經常聽到2種觀點 其實對於上面的觀點一定程度上是正確的,但不是完全正確。但之所以流傳這麼廣,主要還是沒有搞清楚實際狀態,而根據實際使用中總結出來的一些模糊規律。只有了解的mysql的join實際執行方式,就會知道上面2種觀點是一種模糊的規律,這種規律並不能指導我們實際開發。下面就說說mysql的實際...

一文搞懂MySQL預編譯

1 預編譯的好處 大家平時都使用過jdbc中的preparedstatement介面,它有預編譯功能。什麼是預編譯功能呢?它有什麼好處呢?當客戶傳送一條sql語句給伺服器後,伺服器總是需要校驗sql語句的語法格式是否正確,然後把sql語句編譯成可執行的函式,最後才是執行sql語句。其中校驗語法,和編...

一文搞懂transform skew

目錄 如何理解斜切 skew,先看乙個 demo。在下面的 demo 中,有 4 個正方形,分別是 紅色 不做 skew 變換,綠色 x 方向變換,藍色 y 方向變換,黑色 兩個方向都變換,拖動下面的滑塊可以檢視改變 skew 角度後的效果。切換 selector 可以設定 transform or...