mysql字串字首索引

2021-10-23 09:06:09 字數 4631 閱讀 1137

比如,這兩個在 email 欄位上建立索引的語句:

mysql> alter table suser add index index1(email);

或mysql> alter table suser add index index2(email(6));

第乙個語句建立的 index1 索引裡面,包含了每個記錄的整個字串;而第二個語句建立的 index2 索引裡面,對於每個記錄都是只取前 6 個位元組。

那麼,這兩種不同的定義在資料結構和儲存上有什麼區別呢?

​ 圖1: email索引結構

​ 圖2: email(6)索引結構

從圖中你可以看到,由於 email(6) 這個索引結構中每個郵箱欄位都只取前 6 個位元組(即:zhangs),所以占用的空間會更小,這就是使用字首索引的優勢。

但,這同時帶來的損失是,可能會增加額外的記錄掃瞄次數。

接下來,我們再看看下面這個語句,在這兩個索引定義下分別是怎麼執行的。

select id,name,email from suser where email='zhangssxyz@***.com';
如果使用的是 index1(即 email 整個字串的索引結構),執行順序是這樣的:

從 index1 索引樹找到滿足索引值是』zhangssxyz@***.com』的這條記錄,取得 id2 的值;

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

取 index1 索引樹上剛剛查到的位置的下一條記錄,發現已經不滿足 email='zhangssxyz@***.com』的條件了,迴圈結束。

這個過程中,只需要回主鍵索引取一次資料,所以系統認為只掃瞄了一行。

如果使用的是 index2(即 email(6) 索引結構),執行順序是這樣的:

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

到主鍵上查到主鍵值是 id1 的行,判斷出 email 的值不是』zhangssxyz@***.com』,這行記錄丟棄;

取 index2 上剛剛查到的位置的下一條記錄,發現仍然是』zhangs』,取出 id2,再到 id 索引上取整行然後判斷,這次值對了,將這行記錄加入結果集;

重複上一步,直到在 idxe2 上取到的值不是』zhangs』時,迴圈結束。

在這個過程中,要回主鍵索引取 4 次資料,也就是掃瞄了 4 行。

但是,對於這個查詢語句來說,如果你定義的 index2 不是 email(6) 而是 email(7),也就是說取 email 欄位的前 7 個位元組來構建索引的話,即滿足字首』zhangss』的記錄只有乙個,也能夠直接查到 id2,只掃瞄一行就結束了。

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

當要給字串建立字首索引時,有什麼方法能夠確定我應該使用多長的字首呢

實際上,我們在建立索引時關注的是區分度,區分度越高越好。因為區分度越高,意味著重複的鍵值越少。因此,我們可以通過統計索引上有多少個不同的值來判斷要使用多長的字首。

你可以使用下面這個語句,算出這個列上有多少個不同的值:

mysql> select count(distinct email) as l from suser;
然後,依次選取不同長度的字首來看這個值,比如我們要看一下 4~7 個位元組的字首索引,可以用這個語句:

mysql> select 

count(distinct left(email,4))as l4,

count(distinct left(email,5))as l5,

count(distinct left(email,6))as l6,

count(distinct left(email,7))as l7,

from suser;

當然,使用字首索引很可能會損失區分度,所以你需要預先設定乙個可以接受的損失比例,比如 5%。然後,在返回的 l4~l7 中,找出不小於 l * 95% 的值,假設這裡 l6、l7 都滿足,你就可以選擇字首長度為 6。

你先來看看這個 sql 語句:

select id,email from suser where email='zhangssxyz@***.com';
相比,這個語句只要求返回 id 和 email 字段。

所以,如果使用 index1(即 email 整個字串的索引結構)的話,可以利用覆蓋索引,從 index1 查到結果後直接就返回了,不需要回到 id 索引再去查一次。而如果使用 index2(即 email(6) 索引結構)的話,就不得不回到 id 索引再去判斷 email 欄位的值

即使你將 index2 的定義修改為 email(18) 的字首索引,這時候雖然 index2 已經包含了所有的資訊,但 innodb 還是要回到 id 索引再查一下,因為系統並不確定字首索引的定義是否截斷了完整資訊。

對於類似於郵箱這樣的字段來說,使用字首索引的效果可能還不錯。但是,遇到字首的區分度不夠好的情況時,我們要怎麼辦呢?

假設你維護的資料庫是乙個市的公民資訊系統,這時候如果對身份證號做長度為 6 的字首索引的話,這個索引的區分度就非常低了。

按照我們前面說的方法,可能你需要建立長度為 12 以上的字首索引,才能夠滿足區分度要求。

但是,索引選取的越長,占用的磁碟空間就越大,相同的資料頁能放下的索引值就越少,搜尋的效率也就會越低。

第一種方式是使用倒序儲存。如果你儲存身份證號的時候把它倒過來存,每次查詢的時候,你可以這麼寫:

mysql> select field_list from t where id_card = reverse('input_id_card_string')
由於身份證號的最後 6 位沒有位址碼這樣的重複邏輯,所以最後這 6 位很可能就提供了足夠的區分度。當然了,實踐中你不要忘記使用 count(distinct) 方法去做個驗證。

第二種方式是使用 hash 字段。你可以在表上再建立乙個整數字段,來儲存身份證的校驗碼,同時在這個欄位上建立索引。

mysql> alter table t add id_card_crc int unsigned, add index(id_card_crc);
然後每次插入新記錄的時候,都同時用 crc32() 這個函式得到校驗碼填到這個新字段。由於校驗碼可能存在衝突,也就是說兩個不同的身份證號通過 crc32() 函式得到的結果可能是相同的,所以你的查詢語句 where 部分要判斷 id_card 的值是否精確相同。

mysql> select field_list from t where id_card_crc=crc32('input_id_card_string') and id_card='input_id_card_string'
這樣,索引的長度變成了 4 個位元組,比原來小了很多。

接下來,我們再一起看看使用倒序儲存和使用 hash 欄位這兩種方法的異同點

首先,它們的相同點是,都不支援範圍查詢。倒序儲存的字段上建立的索引是按照倒序字串的方式排序的,已經沒有辦法利用索引方式查出身份證號碼在[id_x, id_y]的所有市民了。同樣地,hash 欄位的方式也只能支援等值查詢。

它們的區別,主要體現在以下三個方面:

從占用的額外空間來看,倒序儲存方式在主鍵索引上,不會消耗額外的儲存空間,而 hash 字段方法需要增加乙個字段。當然,倒序儲存方式使用 4 個位元組的字首長度應該是不夠的,如果再長一點,這個消耗跟額外這個 hash 欄位也差不多抵消了。

在 cpu 消耗方面,倒序方式每次寫和讀的時候,都需要額外呼叫一次 reverse 函式,而 hash 欄位的方式需要額外呼叫一次 crc32() 函式。如果只從這兩個函式的計算複雜度來看的話,reverse 函式額外消耗的 cpu 資源會更小些。

從查詢效率上看,使用 hash 字段方式的查詢效能相對更穩定一些。因為 crc32 算出來的值雖然有衝突的概率,但是概率非常小,可以認為每次查詢的平均掃瞄行數接近 1。而倒序儲存方式畢竟還是用的字首索引的方式,也就是說還是會增加掃瞄行數。小結

char 和 varchar 可以設定長度,這個長度是幹什麼的,對於不同字符集又有什麼影響?一直看到不同的解釋,懇請老師專業的解釋一下

char(n)表示「最長存n,但是如果字串小於n,用空格補到n」

varchar(n)表示「最長存n,如果字串小於n,按照實際長度來存」

什麼的,對於不同字符集又有什麼影響?一直看到不同的解釋,懇請老師專業的解釋一下

char(n)表示「最長存n,但是如果字串小於n,用空格補到n」

varchar(n)表示「最長存n,如果字串小於n,按照實際長度來存」

mysql索引 錢綴 mysql字串字首索引

比如,這兩個在 email 欄位上建立索引的語句 mysql alter table suser add index index1 email 或mysql alter table suser add index index2 email 6 第乙個語句建立的 index1 索引裡面,包含了每個記錄...

mysql字串做索引 MySQL

假設要維護乙個支援郵箱登入的系統,使用者表如下定義 1 5mysql create table suser id bigint unsigned primary key,email varchar 64 engine innodb 由於使用郵箱登入,登入的時候一定要根據郵箱去查詢。所以業務 一定會出...

mysql中字串索引

1.只是用字串的最左邊n個字元建立索引,推薦n 10 比如index left address,8 但是需要知道字首索引不能在order by中使用,也不能用在索引覆蓋上。2.對字串使用hash方法將字串轉化為整數,address key hashtoint address 對address key...