現在主流**都支援手機號登入,如何在手機號這樣的字串字段建立合適的索引呢?
假設,你現在維護乙個支援郵箱登入的系統,使用者表是這麼定義的:
create
table suser(
id bigint
unsigned
primary
key,
email varchar(64
),..
.)engine
=innodb
;
要使用郵箱登入,會有語句:
select f1, f2 from suser where email=
'***'
;
若email欄位無索引,該語句只能全表掃瞄。
mysql支援字首索引,可定義字串的一部分作為索引。
若建立索引的語句不指定字首長度,那麼索引預設包含整個字串。
比如,這倆在email欄位建立索引的語句:
alter
table suser add
index index1(email)
;alter
table suser add
index index2(email(6)
);
可見,email(6)索引結構中每個郵箱欄位都只取前6位元組(zhangs),占用空間更小,這就是字首索引優勢。
這同時帶來損失:可能會增加額外的記錄掃瞄次數。
看看下面這語句,在這倆索引定義分別怎麼執行。
select id,name,email from suser where email=
'zhangssxyz@***.com'
;
如果使用index1,執行順序如下:
從index1索引樹找到滿足索引值 'zhangssxyz@***.com』的記錄,取得id2的值
到主鍵上查到主鍵值是id2的行,判斷email值是正確的,將改行記錄加入結果集
取index1索引樹上剛剛查到位置的下條記錄,發現已不滿足email='zhangssxyz@***.com』條件,結束迴圈
該過程,只需回主鍵索引取一次資料,所以系統認為只掃瞄一行。
如果使用是index2,執行順序如下:
從index2索引樹找到滿足索引值是』zhangs』的記錄,找到的第乙個是id1
到主鍵上查到主鍵值是id1的行,判斷出email的值不是』zhangssxyz@***.com』,該行記錄丟棄
取index2上剛剛查到的位置的下條記錄,仍是』zhangs』,取出id2,再到id索引取整行判斷,這次值對,將該行記錄加入結果集
重複上一步,直到在idxe2上取值不是』zhangs』,結束迴圈結束
該過程,要回主鍵索引取4次資料,即掃瞄4行。
對比發現,使用字首索引,可能導致查詢語句讀資料的次數變多。
但對該查詢語句,如果定義index2不是email(6)而是email(7),即取email欄位前7位元組構建索引,即滿足字首』zhangss』記錄只有乙個,也能直接查到id2,只掃瞄一行結束。
即使用字首索引,定義好長度,就可做到既節省空間,又不用增加額外太多的查詢成本。
要給字串建立字首索引
在建立索引時我們關注的是區分度,區分度越高越好。區分度越高,重複的鍵值越少。因此可通過統計索引上有多少不同值判斷要使用多長字首。
可使用如下語句,計算該列上有多少不同值
select
count
(distinct email)
as l from suser;
依次選取不同長度字首來測該值,比如看4~7個位元組字首索引:
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'
;
與前例sql語句:
select id,name,email from suser where email=
'zhangssxyz@***.com'
;
相比,該語句只要求返回id和email。
若使用index1,可利用覆蓋索引,從index1查到結果後直接返回,不需回到id索引再查一次。
而若使用index2(email(6)),得回id索引再判斷email字段值。
即使將index2定義改為email(18),雖然index2已包含所有資訊,但innodb還是要回id索引再查,因為系統並不確定字首索引的定義是否截斷了完整資訊。
即字首索引根本用不上覆蓋索引對查詢的優化,這也是選擇是否使用字首索引時需要考慮的因素。
對類似郵箱這樣字段,字首索引可能還行。但遇到字首區分度不好的,怎麼辦?
比如身份證號18位,前6位是位址碼,所以同縣人身份證號前6位一般相同。
假設維護資料庫是個市公民資訊系統,若對身份證號做長度6字首索引,區分度非常低。
可能需建立長度12以上字首索引,才能夠滿足區分度要求。
但索引選取越長,佔磁碟空間越大,相同資料頁能放下索引值越少,查詢效率就越低。
第一種方式使用
如果儲存身份證號時把它倒過來存,每次查詢這麼寫:
select field_list from t where id_card = reverse(
'input_id_card_string'
);
由於身份證號最後6位沒有位址碼這樣重複邏輯,所以最後6位可能提供足夠的區分度。
實踐中也別忘記使用count(distinct)驗證區分度哦!
第二種方式是使用
可在表再建立整數字段,儲存身份證的校驗碼,同時在該字段建立索引。
alter
table t add id_card_crc int
unsigned
,add
index
(id_card_crc)
;
每次插新記錄時,同時用crc32()函式得到校驗碼填到該新字段。
由於校驗碼可能存在衝突,即兩不同身份證號crc32()所得結果可能相同(雜湊衝突),所以查詢語句where部分要判斷id_card值是否精確相同。
select field_list from t where
id_card_crc=crc32(
'input_id_card_string'
)and id_card=
'input_id_card_string'
這索引長度變4位元組,比原來小很多。
都不支援範圍查詢。
占用的額外空間
cpu消耗
若只從這倆函式計算複雜度看,reverse函式額外消耗cpu資源較少。
查詢效率
字串字段建立索引的場景,可使用的方式如下:
直接建立完整索引,這樣可能比較占用空間
建立字首索引,節省空間,但增加查詢掃瞄次數,且不能使用覆蓋索引
倒序儲存,再建立字首索引,用於繞過字串本身字首的區分度不足缺陷
建立hash欄位索引,查詢效能穩定,有額外儲存和計算消耗,跟第三種方式一樣不支援範圍掃瞄
實際應用中,根據業務欄位的特點擊擇使用哪種方式。
維護學生資訊資料庫,學生登入名的統一格式是」學號@gmail.com"
學號的則是:十五位的數字,其中前三位是所在城市編號、第四到第六位是學校編號、第七位到第十位是入學年份、最後五位是順序編號。
系統登入時輸入登入名和密碼,驗證正確後才能繼續使用系統。
只考慮登入驗證,怎麼設計這個登入名的索引呢?
上期我留給你的問題是,給乙個學號字段建立索引,有哪些方法。
由於學號規則,無論正向反向字首索引,重複度都較高。
因為維護的只是乙個學校的,因此前面6位(其中,前三位是所在城市編號、第四到第六位是學校編號)固定,郵箱字尾都是@gamil.com,因此可只存入學年份加順序編號,長度9位。
在此基礎,可用數字型存這9位數字。比如201100001,只需佔4位元組。其實這就是種hash,只是用最簡單轉換規則:字串轉數字,而剛好我們設定背景,可保證轉換結果唯一。
當然了,乙個學校的總人數這種資料量,50年才100萬學生,這錶肯定是小表。為了業務簡單,直接存原來的字串。「優化成本和收益」的思想。
阿里做題 百度做題
阿里大概題目兩個單元 1 單選。大部分數學邏輯題 小部分程式設計基礎題 選擇題目 strlen和sizeof的用法 幾乎逢考必見 資料結構樹的遍歷,和equal的區別 排序的複雜度分析 程式設計題目 1 考察hadoop檔案讀寫過程 2 編寫乙個轉賬介面,注意條件 餘額 轉賬金額 轉賬次數 1000...
阿里雲 騰訊雲 百度雲 華為雲 評測對比
所有伺服器測試使用的軟體版本均為 vps bench v0.3.2 版本 源 在 github 您可以在這裡檢視原始資料以及 您可以在這裡看每次單獨的測試結果 從結果上看,這四家的cpu計算穩定性都不差,畢竟都是有雄厚技術支援的廠家。具體結果如下圖所示 註解 國內的網路這四家看來差不多 實際上網路還...
百度的搜尋工具如何使用
在使用搜尋引擎搜尋時大家都有注意到,搜尋欄下面有個 搜尋工具 這是為了方便進行更加詳細搜尋!那麼,搜尋工具該如何使用呢?點開搜尋工具我們能看到有一欄時間選項。你可以在這裡選擇你需要的檔案發布的具體時間。第二欄是檔案型別,你可以根據需要選擇型別,如word。第三欄是選擇輸入 進行站內搜尋!下面以 河南...