一個線上全文索引BUG的排查:關於類阿拉件數位的分詞與檢索

2023-05-18 15:00:33

說到全文檢索的分詞,多半講到的是中(日韓)文分詞,少有英文等拉丁文系語言,因為英語單詞天然就是分詞的。
但更少講到阿拉伯數位。比如金額,手機號碼,座機號碼等等。

以下不是傳統的從0開始針對mysql全文索引前世今生講起。
我更喜歡從一個小問題入手,見縫插針的將相關的知識點,以非時間線性順序零散穿插起來。

從一個線上的BUG說起

我們有一張人口表,裡面的資料有多種資料來源合併而來,因此每個使用者的手機號可能有多個。
這也很好理解,有的人就是有多個手機號,有的人就是經常換手機號,對吧。
現在有個功能需要通過手機號去關聯使用者。

因為手機號有多個,所以要麼使用like進行模糊匹配。使用者表有上千萬條記錄,這樣的效率肯定是不能接受的。

select * from t_user where phone like '%13112345678%'

要麼使用另一個折中的方案,將手機號單獨成表,使用者表對手機號表一對多關聯。
這種方式效率上能接受,但需要改變現有資料結構,故放棄。

select u.id,u.username,u.phone from t_user u LEFT JOIN t_user_phone p on u.id = p.user_id where p.phone = '13112345678'

最終選用全文索引。(mysql 5.7.6+)

先在使用者錶針對手機號建立一個全文索引。
使用內建分詞引擎ngram

CREATE FULLTEXT INDEX idx_full_text_phone ON t_user (phone) WITH PARSER ngram;

當使用手機模糊查詢關聯使用者時可使用以下語句。

  1. 布林模式模糊檢索
select * from t_user where match(phone) AGAINST('13996459860' in boolean mode)
  1. 自然語言模式。mysql預設為此模式,所以第2條sql沒有顯式指定時,仍然為自然語言模式。
select * from t_user where match(phone) AGAINST('13996459860' in NATURAL LANGUAGE mode)
或
select * from t_user where match(phone) AGAINST('13996459860')

根據我們的需求,查詢手機號需要全匹配才算命中。所以選擇布林模式。
自然語言模式做不到。
關於布林模式和自然語言模式的區別,後面做介紹。


以上算是簡單的背景介紹。

但是
萬惡的但是,雖遲但到

有一天產品過來告訴我,某個手機號關聯出來上百個人。
他問,這種情況是正常的嗎?

他如果直接說你這裡有個bug,我可能直接就懟回去了(bushi