本文來自微信開發團隊WeMobileDev公衆號的技術分享。php
微信的移動客戶端全文搜索中的多音字問題一直是搜索體驗的痛點之一。微信客戶端全文搜索在上線之後,也常常收到用戶關於多音字問題的反饋。因此,微信全文搜索中的多音字搜索成了一個迫切須要解決的問題。本文重點講述微信安卓客戶端在SQLite FTS5的基礎上,多音字問題的解決方案。html
另外:微信團隊在另外一個文章《微信手機端的本地數據全文檢索優化之路》 中,分享了更爲詳細的全文檢索優化思路,建議有興趣的開發者能夠深刻的看看。程序員
建議:您也能夠在微信客戶端的sqlite數據庫中找到本文中相關技術的真實實現,微信的SQLite樣本庫可在此下載《微信本地數據庫破解版(含iOS、Android),僅供學習研究 [附件下載]》(特別申明:微信的SQLite樣本庫僅供研究和學習以外,嚴禁用於商用業目的,全部權歸微信全部)。算法
學習交流:sql
- 即時通信開發交流羣:320837163[推薦]數據庫
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》緩存
(本文同步發佈於:http://www.52im.net/thread-1545-1-1.html)安全
《微信手機端的本地數據全文檢索優化之路》 微信
《騰訊技術分享:Android版手機QQ的緩存監控與優化實踐》網絡
《微信團隊分享:iOS版微信的高性能通用key-value組件技術實踐》
《微信團隊分享:iOS版微信是如何防止特殊字符致使的炸羣、APP崩潰的?》
《騰訊技術分享:Android手Q的線程死鎖監控系統技術實踐》
《騰訊團隊分享 :一次手Q聊天界面中圖片顯示bug的追蹤過程分享》
《微信團隊披露:微信界面卡死超級bug「15。。。。」的前因後果》
《微信客戶端團隊負責人技術訪談:如何着手客戶端性能監控和優化》
《微信團隊原創分享:微信客戶端SQLite數據庫損壞修復實踐》
《移動端IM實踐:Android版微信如何大幅提高交互性能(一)》
《移動端IM實踐:Android版微信如何大幅提高交互性能(二)》
《信鴿團隊原創:一塊兒走過 iOS10 上消息推送(APNS)的坑》
搜索形式:
拼音前綴搜索,中文和拼音不能混合搜索,輸入拼音必須爲連續漢字的全拼音或者短拼音。
搜索內容:
聯繫人、羣聊以及公衆號的備註和暱稱(最大長度爲16箇中文字符)。
例如。
聯繫人A,暱稱爲「王宏偉」,那麼經過如下幾種方式都須要搜索到聯繫人A的暱稱:
中文全文搜索引擎若是須要支持拼音,就須要把輸入的中文字符,轉化爲拼音字母,若是不考慮多音字的狀況,咱們只須要一張單個漢字的拼音表便可實現轉化,可是在多音字的狀況下,因爲每一個漢字在不一樣的詞語當中的讀音都有可能不同,因此,爲了使得每一箇中文字符可以獲取到準確的拼音,就須要引入一份詞語拼音對應表。
衆所周知,漢語博大精深,經常使用的漢字有20777個,而詞語(包括成語)的漢字個數爲2到16個,同一個漢字在不一樣詞語中讀音有可能不同。
因此漢語詞語轉化爲拼音有以下兩個方案:
1)窮舉詞語表;
2)採用機率模型,經過訓練分類器模型,獲取中文字符拼音。
第一種方案對存儲空間的要求很是高,對資源的消耗過大。
第二種方案,經過在現有的TTS(Text To Speech)模型中,把漢字轉拼音讀音的模型拆出來,初步搭建一個機率模型,在初步的調優後,獲得一個1個G的拼音模型,識別拼音的準確在可接受範圍內。因爲模型大小爲GB級別,初步考慮是將模型放到後臺處理。
處理流程以下圖:
優勢:
客戶端無改動,能夠快速覆蓋全部版本客戶端。
缺點:
用戶修改備註或者暱稱後,須要等待後臺下發拼音後纔能有正確的拼音索引,致使拼音索引創建不夠及時。微信用戶量巨大,用戶修改備註和暱稱的頻次很是高,天天都有幾十億次修改,致使後臺處理這部分的運算量和耗時很是嚴重,須要增長較大成本。
經常使用漢字有20777個,整體大小爲200KB,能夠直接帶到客戶端中,而且查詢的時間複雜度爲O(1),在數據量方面是能夠接受的。
優勢:
用戶修改暱稱或者備註之後,可以快速響應並及時創建索引;
將後臺巨大的計算量均攤到用戶手機上,節省成本;
對於姓名中漢字的讀音,能夠用任意一個讀音搜索出來。
缺點:
在用戶體驗上,詞語中的多音字能夠用任意該漢字的拼音搜索出來;
在綜合考慮用戶體驗和性能問題後,最後微信選擇了字表方案。
六、客戶端索引方案
在肯定字表方案後,須要在客戶端本地使用SQLite FTS5創建索引。由於拼音搜索主要是採用前綴搜索的方式,因此創建索引的內容以及方式須要考慮FTS5前綴搜索的過程。
路徑(1)是在創建索引表時使用Prefix索引,因此用戶在輸入Query時,直接經過Hash方法查找前綴索引表便可找到全部以Query爲前綴的結果。
路徑(2)是在創建索引表時未使用Prefix索引,因此用戶在輸入Query時,FTS5經過臨時搭建一個前綴樹來查找以Query爲Preifx的索引集合。
從時間複雜度上,路徑1具備明顯優點,因此在創建索引時,須要加入Prefix配置:
考慮到用戶輸入時是連續輸入,並不會考慮跨拼音問題。
例如,用戶搜索備註「市委書記」的拼音,會可能採用以下的Query:
shi
shiweishuj
sw
根據以上用戶輸入習慣以及FTS5前綴搜索原理,採用第一個索引方案:
在FTS5匹配以上Query時,用戶一、2兩種輸入都做爲"shiweishuji"的前綴被匹配,而3的輸入會做爲「swsj」的前綴被匹配。
索引方案一僅考慮用戶從拼音的頭部開始搜索,並無考慮從中間開始搜索。
例如如下的Query:
shuji
sj
因此,須要創建索引時,須要把每一個漢字的拼音做爲前綴創建到索引中,以下表:
方案一和方案二是在不考慮多音字的狀況的索引方案,當引入了多音字之後,在組合拼音字符串時,每個拼音均可能存在多種狀況。
如下爲用戶備註「張靚穎」的索引:
當暱稱「張靚穎」創建索引之後,獲得以下索引結構:
TermOffset:表示一個詞語在某一個Document中的偏移;
DocId:Document的惟一ID。
經過一個DocId和一個TermOffset能夠定位一個詞語。而SQLite FTS5正是經過搜索一個詞語來找到對應的DocId,經過TermOffset來定位該詞語在Document中的位置。
方案優勢:
實現較爲簡單;
可覆蓋全部多音字狀況。
方案缺點:
索引數據量過大;
考慮經常使用漢字一共20777個,其中多音字2659個,多音字佔比12.7%,平均每一個多音字有2.14個拼音。
在微信場景中,聯繫人的備註和暱稱最大字符長度爲16個字符,因此咱們假設每一個暱稱的字符爲16個漢字,其中,每一個漢字的拼音長度爲最長度(7個英文字母+1個短拼音英文字母)。
通常場景:
其中20777個漢字當中,出如今暱稱中的機率同樣,因此16個字符中,大約會出現3個多音字,獲得以下公式:
極限場景:
暱稱中每個字都是多音字,每一個多音字都有4個讀音,例如「麼麼麼麼麼麼麼麼麼麼麼麼麼麼麼麼」,獲得以下公式:
從以上兩種場景中能夠看出,方案三在極限場景中會出現佔用超大數據量的狀況,因此方案三不可用。
方案三經過窮舉法來列舉全部拼音組合,核心在於經過空間換區時間,在所須要的空間過於巨大時,能夠採起折中的方案來實現。
在漢語中,一個一樣意義的實體經過兩個不一樣的詞語來表示,稱這兩個不一樣的詞語爲同義詞,在數據上表示爲(詞語A,詞語B)=(意義C),那麼在多音字的狀況來看,一樣能夠表示爲(拼音A,拼音B)=(漢字C)。方案四的核心在於經過同義詞的方式來表示多種拼音的組合。
在SQLite FTS5中,一個詞語能夠經過一個DocId和一個TermOffset來定位,因此當兩個詞語擁有同一個DocId和TermOffset時,就能夠說這兩個詞語爲同義詞了,也就有了以下的索引方案:
方案優勢:
索引數據量小:
1)通常狀況:
2)極限狀況:
創建索引速度快。
方案缺點:
默認分詞器不能適配多音字的拼音數據;
索引中的數據不能直接對應用戶輸入。
爲了解決方案四的兩個問題,咱們引入了多音字分詞器,而且作了用戶輸入預處理。
SQLite FTS5默認的分詞器的分隔符都是固定的,因此,在識別拼音字符時,會當成英文字母來分詞。爲了可以達到須要的索引結構,咱們引入了二級分隔符,使用分號「;」分隔不一樣漢字以及「,」分隔同一個漢字的不一樣拼音。
如下是多音字分詞器的分詞流程:
當用戶的輸入爲連續拼音時,因爲索引中不存在直接對應的Term,因此須要把用戶輸入的Query拆解成爲索引當中可能存在的Term。
假設用戶輸入拼音:zhuang,根據短拼音和全拼音的規則,可獲得以下7中搜索組合:
考慮到最後一個拼音爲前綴搜索,因此,在列舉拼音組合時,前面都須要考慮符合完整的拼音,最後一個能夠只考慮是不是某個拼音的前綴。
實現這個算法能夠經過把全部的拼音做爲輸入,構建一顆前綴樹,可以把整個Query拼音拆解的時間複雜度下降到O(nlgn)。
最後,把全部的拼音組合狀況都寫到SQL中:
對比方案三和方案四,在拼音數據上有較爲明顯的提高,提高的範圍在50%左右。
因爲聯繫人拼音數據的減小,使得單個聯繫人的數據量降低,減小了Insert SQL的執行時間,創建聯繫人索引的時間也有較爲明顯的下降,減小30%左右。
在搜索Query的時間上,多音字方案由於拼音組合的多樣性,增長了查找HashTable的次數,可是因爲搜索HashTable的時間複雜度爲O(1),而拼音組合在有限字符的query下很少(小於20個),因此增長時間很少,可是因爲數據量的減小,ORM的時間縮短,搜索Query的時間有15%左右的提高。
更爲詳細的微信全文檢索優化思路請見《微信手機端的本地數據全文檢索優化之路》。微信的本地SQLite研究樣本可今後下載《微信本地數據庫破解版(含iOS、Android),僅供學習研究 [附件下載]》(特別申明:微信的SQLite樣本庫僅供研究和學習以外,嚴禁用於商用業目的,全部權歸微信全部)。
[1] QQ、微信團隊原創技術文章:
《騰訊技術分享:Android版手機QQ的緩存監控與優化實踐》
《微信團隊分享:iOS版微信的高性能通用key-value組件技術實踐》
《微信團隊分享:iOS版微信是如何防止特殊字符致使的炸羣、APP崩潰的?》
《騰訊技術分享:Android手Q的線程死鎖監控系統技術實踐》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)》
《騰訊團隊分享 :一次手Q聊天界面中圖片顯示bug的追蹤過程分享》
《微信團隊分享:微信Android版小視頻編碼填過的那些坑》
《微信團隊披露:微信界面卡死超級bug「15。。。。」的前因後果》
《月活8.89億的超級IM微信是如何進行Android端兼容測試的》
《微信客戶端團隊負責人技術訪談:如何着手客戶端性能監控和優化》
《微信團隊原創分享:Android版微信的臃腫之困與模塊化實踐之路》
《微信團隊原創分享:微信客戶端SQLite數據庫損壞修復實踐》
《騰訊原創分享(一):如何大幅提高移動網絡下手機QQ的圖片傳輸速度和成功率》
《騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(下篇)》
《騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(上篇)》
《如約而至:微信自用的移動端IM網絡層跨平臺組件庫Mars已正式開源》
《開源libco庫:單機千萬鏈接、支撐微信8億用戶的後臺框架基石 [源碼下載]》
《微信新一代通訊安全解決方案:基於TLS1.3的MMTLS詳解》
《微信團隊原創分享:Android版微信後臺保活實戰分享(進程保活篇)》
《微信團隊原創分享:Android版微信後臺保活實戰分享(網絡保活篇)》
《Android版微信從300KB到30MB的技術演進(PPT講稿) [附件下載]》
《微信團隊原創分享:Android版微信從300KB到30MB的技術演進》
《微信技術總監談架構:微信之道——大道至簡(PPT講稿) [附件下載]》
《微信海量用戶背後的後臺系統存儲架構(視頻+PPT) [附件下載]》
《微信異步化改造實踐:8億月活、單機千萬鏈接背後的後臺解決方案》
《架構之道:3個程序員成就微信朋友圈日均10億發佈量[有視頻]》
《微信團隊原創分享:Android內存泄漏監控和優化技巧總結》
《微信團隊原創Android資源混淆工具:AndResGuard [有源碼]》
《移動端IM實踐:Android版微信如何大幅提高交互性能(一)》
《移動端IM實踐:Android版微信如何大幅提高交互性能(二)》
《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》
《移動端IM實踐:谷歌消息推送服務(GCM)研究(來自微信)》
《信鴿團隊原創:一塊兒走過 iOS10 上消息推送(APNS)的坑》
>> 更多同類文章 ……
[2] 有關QQ、微信的技術故事:
《技術往事:微信估值已超5千億,雷軍曾有機會收編張小龍及其Foxmail》
《2017微信數據報告:日活躍用戶達9億、日發消息380億條》
《技術往事:創業初期的騰訊——16年前的冬天,誰動了馬化騰的代碼》
《技術往事:史上最全QQ圖標變遷過程,追尋IM巨人的演進歷史》
《開發往事:深度講述2010到2015,微信一路風雨的背後》
《開發往事:記錄微信3.0版背後的故事(距微信1.0發佈9個月時)》
>> 更多同類文章 ……
(本文同步發佈於:http://www.52im.net/thread-1545-1-1.html)