1.從全文檢索說起
首先介紹一下結(jié)構化與非結(jié)構化數(shù)據(jù):
- 結(jié)構化數(shù)據(jù)將數(shù)據(jù)具有的特征事先以結(jié)構化的形式定義好,數(shù)據(jù)有固定的格式或有限的長度。典型的結(jié)構化數(shù)據(jù)就是傳統(tǒng)關系型數(shù)據(jù)庫的表結(jié)構,數(shù)據(jù)特征直接體現(xiàn)在表結(jié)構的字段上,所以根據(jù)某一特征做數(shù)據(jù)檢索很直接,速度也比較快
- 非結(jié)構化數(shù)據(jù)沒有預先定義好的結(jié)構化特征,也沒有固定格式和固定長度。典型的非結(jié)構化數(shù)據(jù)包括文章、圖片、視頻、網(wǎng)頁、郵件等,其中像HTML網(wǎng)頁這種具有一定格式的文檔也稱為半結(jié)構化數(shù)據(jù)
對于非結(jié)構化的數(shù)據(jù)檢索,被稱為全文檢索。
假設現(xiàn)在MySQL中有一張User表,含有三個階段:姓名name、年齡age和愛好favor:
對于User表來說,整體上是結(jié)構化的,比如name、age都可以直接建立索引來快速地檢索。
而其中的favor字段是一個text類型,存儲的是非結(jié)構化的文本數(shù)據(jù):
籃球、足球、愛運動的我;本人熱愛學習,游戲偶爾也玩?。。。?/p>
與結(jié)構化查詢相比,全文檢索面臨的最大問題就是性能問題。全文檢索最一般的應用場景是根據(jù)一些關鍵字查找包含這些關鍵字的文檔,比如互聯(lián)網(wǎng)搜索引擎要實現(xiàn)的功能就是根據(jù)一些關鍵字查找網(wǎng)頁。顯然,如果沒有對文檔做特別處理,查找的辦法似乎只能是逐條比對。
假設現(xiàn)在需要找到favor中含有“足球”這個關鍵字的User,那么只能使用like模糊查詢:select * from user where favor like '%足球%'
like語句是無法建立索引的,查詢時會進行全表掃描,并且在每個favor字段中進行遍歷匹配,以找到含有“足球”這個關鍵字的記錄,整體復雜度特別高,所以全文檢索也是MySQL這類結(jié)構關系式數(shù)據(jù)庫無法很好實現(xiàn)的需求。
全文檢索一般是查詢包含某一或某些關鍵字記錄,所以通過文檔整體值建立的索引對提高查詢速度是沒有任何幫助的。為了解決這個問題,人們創(chuàng)建了一種新索引方法,這種索引方法就是倒排索引。
2.倒排索引的原理
倒排索引是為了解決上述非結(jié)構化數(shù)據(jù)的檢索問題而產(chǎn)生的。
首先明確一下,在ES中存儲記錄的單位是JSON“文檔”,而JSON“文檔”中的“字段”也就是組成JSON的一個個KV對。
普通索引也被稱為正排索引,也就是通過對主鍵和結(jié)構化字段建立索引,通過這些結(jié)構化索引找到文檔。
倒排索引則是先將文檔中包含的關鍵字全部提取出來,然后再將關鍵字與文檔的對應關系保存起來,最后再對關鍵字本身做索引排序。用戶在檢索某一關鍵字時,可以先對關鍵字的索引進行查找,再通過關鍵字與文檔的對應關系找到所在文檔。
假設上述的User表通過ES存儲,其中兩個User文檔為:
{
"_id: 1,
"name":"pbr1",
"age":22,
"favor":"籃球、足球、愛運動的我;本人熱愛學習,游戲偶爾也玩!?。。?
}
{
"_id: 2,
"name":"pbr2",
"age":22,
"favor":"籃球、足球、愛運動的我"
}
其中favor定義為text類型,假設分詞器進行以下分詞:
- 文檔1的favor分詞:“籃球”、“足球”、“愛運動的我”、“本人熱愛學習”、“游戲偶爾也玩”這5個token
- 文檔2的favor分詞:“籃球”、“足球”、“愛運動的我”這3個token
那么對分詞token建立索引,并建立對原始文檔的映射,就得到一個以favor進行分詞的倒排索引:
可以看到,倒排索引實際上就是對全文數(shù)據(jù)結(jié)構化的過程。對于存儲在關系型數(shù)據(jù)庫中的數(shù)據(jù)來說,它們依賴于人的預先分析將數(shù)據(jù)拆解為不同字段,所以在數(shù)據(jù)插入時就已經(jīng)是結(jié)構化的;而在全文數(shù)據(jù)庫中,文檔在插入時還不是結(jié)構化的,需要應用程序根據(jù)規(guī)則自動提取關鍵字,并形成關鍵字與文檔之間的結(jié)構化對應關系。
比如現(xiàn)在需要查詢愛好為“籃球”和“足球”的用戶,那么可以直接通過倒排索引拿到對應的文檔1和文檔2,也就查詢到了這兩個用戶。
3.ES索引構建過程
全文檢索中提取關鍵字是非常重要的一步。這些預先提取出來的關鍵字,在Elasticsearch及全文檢索的相關文獻中一般稱為詞項(Term),文檔的詞項提取在Elasticsearch中稱為文檔分析(Analysis),是整個全文檢索中較為核心的過程。這個過程必須要區(qū)分哪些是詞項,哪些不是。對于英文來說,它還必須要知道apple和apples指的同一個東西,而run和running指的是同一動作。對于中文來說就更麻煩了,因為中文詞語不以空格分隔,所以面臨的第一難題是如何將詞語分辨出來。
ES底層使用了Lucene來構建索引,一個基本的過程是先對text類型的字段進行分詞,分詞使用的分詞器以配置mapping時指定的為準,默認使用standard分詞器,對于中文分詞來說,一般建議使用ik_smart或ik_max_word分詞器:
關于Lucene如何存儲這些分詞解析結(jié)果可以學習這篇文章:https://www.shenyanchao.cn/blog/2018/12/04/lucene-index-files/
由于文檔存儲前的分析和索引過程比較耗資源,所以為了提升性能,文檔在添加到ES中時并不會立即被編入索引。
默認情況下,ES會每隔1s統(tǒng)一處理一次新加入的文檔,可以通過index.refresh_interval參數(shù)修改。
為了提升性能,在ES 7中還添加了index.search.idle.after參數(shù),它的默認值是30s:如果索引在一段時間內(nèi)沒有收到檢索數(shù)據(jù)的請求,那么它至少要等30s后才會刷新索引數(shù)據(jù)。文章來源:http://www.zghlxwxcb.cn/news/detail-629925.html
所以可以看出ES的寫入操作實際上是準實時的,新添加到索引中的文檔可能在一段時間內(nèi)不能被檢索到,如果的確需要立即檢索到文檔可以使用強制刷新到索引的方式,包括使用_refresh接口和在操作文檔時使用refresh參數(shù)等進行強制刷新緩沖區(qū)中的索引到磁盤中。文章來源地址http://www.zghlxwxcb.cn/news/detail-629925.html
到了這里,關于ElasticSearch:全文檢索及倒排索引原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!