-
1 建表語句
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `id_card` varchar(18) NOT NULL, `email` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB INSERT INTO `test`.`user`(`id`, `id_card`, `email`) VALUES (1, '321023199303164012', '[email protected]'); INSERT INTO `test`.`user`(`id`, `id_card`, `email`) VALUES (2, '321023199003184012', '[email protected]'); INSERT INTO `test`.`user`(`id`, `id_card`, `email`) VALUES (3, '321023199003251234', '[email protected]'); INSERT INTO `test`.`user`(`id`, `id_card`, `email`) VALUES (4, '021236199503251235', '[email protected]');
user表使用郵箱作爲登錄賬號。
2 郵箱字段建立索引
這裏我們用登錄賬號,也就是email字段作爲索引。
2.1 直接給email加索引
select * from user where email = '[email protected]'
2.1.1 執行流程
因爲葉子節點存放的就是email的完整值,在非聚集索引里根據email查到了主鍵id=1,然後進行回表,這裏發生一次回表.
2.1.2 存在問題
因爲郵箱較長,因爲b+tree節點是按頁存取的,默認16k,如果鍵值過長,導致問題就是每頁存放的鍵值數量就較少,會增加樹高,增加IO次數。
2.2 前綴索引email(4)
select * from user where email = '[email protected]'
2.2.1 執行流程
因爲葉子節點只會存放email前四個字節的值,所以在非聚集索引裏查到四條記錄,還要進行四次回表操作,比對email=‘[email protected]’記錄進行篩選。
2.2.2 存在問題
因爲email長度截取了,那麼b+tree每個節點存儲鍵值數量多了,樹高就低了,那麼帶來的問題就是,增加了回表的次數。
2.3 前綴索引email(6)
select * from user where email = '[email protected]'
2.3.1 執行流程
因爲葉子節點只會存放email前六個個字節的值,所以在非聚集索引裏查到一條記錄,只要進行一次回表操作,比對email=‘[email protected]’,得到結果。
2.4 階段小結
通過上面三個案例可以得到一個結論:
使用前綴索引,定義好合適的長度,可以在空間和查詢效率取得一個平衡。
那麼這個前綴索引長度如何選取呢?
建立索引的原則就是選取離散度大的字段,那麼我們可以計算使用多少長度離散度大:select count(distinct left(filed,length)) from tableName;
我們通過如下sql:
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, count(distinct left(email,8)) as L8 from user;
結果如下:
可以看到當長度爲6時,區分度最大,可以email(6)。2.5 前綴索引帶來其他性能問題
比如:
select id, email from user where email = '[email protected]';
上面這個sql,當我們沒有使用前綴索引,利用了覆蓋索引,無需回表,而如果使用了前綴索引,因爲葉子節點沒有保存完整的email信息,那麼會進行回表。
當我們使用前綴索引,就是利用不到覆蓋索引,需要回表,所以得根據業務場景來選擇是否使用前綴索引。
3 身份證字段建立索引
身份證長度爲18,不適合作爲索引。身份證號碼特點:前6位代表地址,中間8位爲年月日。
3.1 前綴索引
前面談到前綴索引,就不說了。這裏存在問題就是,當維護系統是一個市政系統,因爲前6位區分度就不高了,所以需要截取長度要更長,還是浪費空間。
3.2 倒序存儲
因爲身份證後六位區分度高,那麼我們可以將身份證倒序存儲,然後索引爲id_card(6)
select * from user where id_card = reverse('輸入的正序身份證號碼');
倒序存儲只適用等值查詢。
3.3 哈希
可以新增一個字段存儲身份證號碼的哈希值,加上索引,存入身份證時候,對身份證進行crc3()計算,得到的值存入id_card_crc,索引長度爲4,因爲hash可能會發生碰撞,所以查詢時候加上身份證作爲篩選條件:
select * from user where id_card_crc = crc32("輸入的身份證號碼") and id_card = '輸入身份證號';
哈希存儲只適用等值查詢。
3.4 身份證號碼拆分存儲
可以將區分度高的,比如後六位單獨存儲。
4 總結
對於長度較長的字符串,我們可以這麼建立索引:
- 前綴索引
- 倒序存儲
- 哈希存儲
- 字段拆分
如何給特殊字符串加索引:如身份證、郵箱等
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.