如何給特殊字符串加索引:如身份證、郵箱等

  • 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 總結

    對於長度較長的字符串,我們可以這麼建立索引:

    • 前綴索引
    • 倒序存儲
    • 哈希存儲
    • 字段拆分
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章