【漫畫】ES原理 必知必會的倒排索引和分詞
阿新 • • 發佈:2020-05-23
![es1](https://yqfile.alicdn.com/cf7303615996607dad8068cfc67065cfb1d7ed3d.jpeg)
# 倒排索引的初衷
![es2_1](https://yqfile.alicdn.com/1c23ad58c7183fce376abf400424fad70303598b.jpeg)
**倒排索引,它也是索引。索引,初衷都是為了快速檢索到你要的資料。**
我相信你一定知道mysql的索引,如果對某一個欄位加了索引,一般來說查詢該欄位速度是可以有顯著的提升。
每種資料庫都有自己要解決的問題(或者說擅長的領域),對應的就有自己的資料結構,而不同的使用場景和資料結構,需要用不同的索引,才能起到最大化加快查詢的目的。
對 Mysql 來說,是 B+ 樹,對 Elasticsearch/Lucene 來說,是倒排索引。
![es2_2](https://yqfile.alicdn.com/30454cfa320c0a6f47647b0d3200aec46b0b7d86.jpeg)
# 倒排索引是什麼
剛剛胖滾豬說到圖書的例子,目錄和索引頁,其實就很形象的可以比喻為正排索引和倒排索引。為了進一步加深理解,再看看熟悉的搜尋引擎。沒有搜尋引擎時,我們只能直接輸入一個網址,然後獲取網站內容,這時我們的行為是document -> words。此謂「正向索引」。後來,我們希望能夠輸入一個單詞,找到含有這個單詞,或者和這個單詞有關係的文章,即word -> documents。於是我們把這種索引,叫「反向索引」,或者「倒排索引」。
好了,我們來總結一下:
![image](https://yqfile.alicdn.com/0abf024655123558ebe02148f3df86f21025ea07.png)
![es3](https://yqfile.alicdn.com/3361a7bc943900bd28455980354c180e7bbd0f12.jpeg)
# 倒排索引的實現
假如一篇文章當中,有這麼一段話"胖滾豬程式設計讓你收穫快樂",我要通過"胖滾豬"這個詞來搜尋到這篇文章,那麼應該如何實現呢。
我們是很容易想到,可以將這篇文章的詞都拆開,拆分為"胖滾豬"、"程式設計"、"收穫"、"快樂"。注意我們把沒用的詞,比如"讓"去掉了。這個拆分短語的過程涉及到ES的分詞,另外中文分詞還是比較複雜的,不像英文分詞一般用空格分隔就可以。等會我們再來說分詞吧,現在你只要知道,我們是會按一定規則把文章單詞拆分的。
那麼拆開了,怎麼去找呢?自然會維護一個單詞和文件的對應關係,如圖:
![image](https://yqfile.alicdn.com/9c29cd7b531e350c24fd4a64012585f6bd04448b.png)
![es4](https://yqfile.alicdn.com/09db7bfbd5306dbca06b85dfd6aac479a020d8a5.jpeg)
**倒排索引的核心組成**
1、單詞詞典:記錄所有文件的單詞,一般都比較大。還會記錄單詞到倒排列表的關聯資訊。
2、倒排列表:記錄了單詞對應的文件集合,由倒排索引項組成。倒排索引項包含如下資訊:
- 文件ID,用於獲取原始資訊
- 單詞頻率TF,記錄該單詞在該文件中的出現次數,用於後續相關性算分
- 位置Position,記錄單詞在文件中分詞的位置,用於語句搜尋(phrase query)
- 偏移Offset,記錄單詞在文件的開始和結束位置,實現高亮顯示
![es6](https://yqfile.alicdn.com/379de32811f10b324b0b99b788023e87869bdce9.jpeg)
# ES的倒排索引
下圖是 Elasticsearch 中資料索引過程的流程。ES由 Analyzer 元件對文件執行一些操作並將具體子句拆分為 token/term,簡單說就是分詞,然後將這些術語作為倒排索引儲存在磁碟中。
![image](https://yqfile.alicdn.com/7e91b737235c5d870ec0b292e13055f17e3cd274.png)
**ES的JSON文件中的每一個欄位,都有自己的倒排索引**,當然你可以指定某些欄位不做索引,優點是這樣可以節省磁碟空間。但是不做索引的話欄位無法被搜尋到。
注意兩個關鍵詞:分詞和倒排索引。倒排索引我相信你已經懂了!分詞我們馬上就來聊聊!
# ES的分詞
還是回到我們開頭的那個查詢例子,畢竟胖滾豬心心念念為什麼會搜出兩個文件!首先我們用_analyze來分析一下ES會如何對它進行分詞及倒排索引:
![image](https://yqfile.alicdn.com/836c18778ea60a6a5a135278458a988d0ba3ccfb.png)
現在你是不是一目瞭然了呢!先不管_analyze是何方神聖,反正你看到結果了,ES將它分成了一個個字,這是ES中預設的中文分詞。掌握分詞要先懂兩個名詞:analysis與analyzer
** analysis:**
文字分析,是將全文字轉換為一系列單詞的過程,也叫分詞。analysis是通過analyzer(分詞器)來實現的,可以使用Elasticearch內建的分詞器,也可以自己去定製一些分詞器。
** analyzer(分詞器): **
由三部分組成:
- Character Filter:將文字中html標籤剔除掉。
- Tokenizer:按照規則進行分詞,在英文中按照空格分詞
- Token Filter:將切分的單詞進行加工,小寫,刪除 stopwords(停頓詞,a、an、the、is等),增加同義詞
注意:**除了在資料寫入時將詞條進行轉換,查詢的時候也需要使用相同的分析器對語句進行分析**。即我們寫入蘋果的時候分詞成了蘋和果,查詢蘋果的時候同樣也是分詞成蘋和果去查。
![es7](https://yqfile.alicdn.com/b83919170ef8e50bcfa5994f991de528c8da4a41.jpeg)
**ES內建分詞器**
- Standard Analyzer - 預設分詞器,按詞切分,小寫處理
- Simple Analyzer - 按照非字母切分(符號被過濾), 小寫處理
- Stop Analyzer - 小寫處理,停用詞過濾(the,a,is)
- Whitespace Analyzer - 按照空格切分,不轉小寫
- Keyword Analyzer - 不分詞,直接將輸入當作輸出
- Patter Analyzer - 正則表示式,預設\W+(非字元分割)
- Language - 提供了30多種常見語言的分詞器
- Customer Analyzer 自定義分詞器
看概念太虛了!一定要動手實操才有用!我們可以用_analyze進行分析,會輸出分詞後的結果,舉兩個例子吧!其他的你也要自己課後動手試試哦!
```
#預設分詞器 按詞切分 小寫處理
GET _analyze
{
"analyzer": "standard",
"text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}
#可以發現停用詞被去掉了
GET _analyze
{
"analyzer": "stop",
"text": "2 running Quick brown-foxes leap over lazy dogs in the summer evening."
}
```
**中文擴充套件分詞器**
現在來解決胖滾豬的問題,蘋果明明一個詞,不想讓它分為兩個呀!中文分詞在所有搜尋引擎中都是一個很大的難點,中文的句子應該是切分成一個個的詞,但是一句中文,在不同的上下文,其實是不同的理解,例如: 這個蘋果,不大好吃/這個蘋果,不大,好吃。
有一些比較不錯的中文分詞外掛:IK、THULAC等。我們可以試試用IK進行中文分詞。
```
#安裝外掛
https://github.com/medcl/elasticsearch-analysis-ik/releases
在plugins目錄下建立analysis-ik目錄 解壓zip包到當前目錄 重啟ES
#檢視外掛
bin/elasticsearch-plugin list
#檢視安裝的外掛
GET http://localhost:9200/_cat/plugins?v
```
** IK分詞器:支援自定義詞庫、支援熱更新分詞字典 **
- ik_max_word: 會將文字做最細粒度的拆分,比如會將“這個蘋果不大好吃”拆分為"這個,蘋果,不大好,不大,好吃"等,會窮盡各種可能的組合;
- ik_smart: 會做最粗粒度的拆分,比如會將“這個蘋果不大好吃”拆分為"這個,蘋果,不大,好吃"
```
curl -X GET "localhost:9200/_analyze?pretty" -H 'Content-Type: application/json' -d'
{
"analyzer" : "ik_max_word",
"text" : "這個蘋果不大好吃"
}
'
```
![es8](https://yqfile.alicdn.com/f431dd843dda42615b4f17b19b927fb852da5618.jpeg)
** 如何使用分詞器 **
列舉了很多的分詞器,那麼在實際中該如何使用呢?看看下面這個程式碼演示就懂啦!
```
# 建立索引時候指定某個欄位的分詞器
PUT iktest
{
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "ik_smart"
}
}
}
}
# 插入一條文件
PUT iktest/_doc/1
{
"content":"這個蘋果不大好吃"
}
# 測試分詞效果
GET /iktest/_analyze
{
"field": "content",
"text": "這個蘋果不大好吃"
}
```
注:本文來源於公眾號[胖滾豬學程式設計],其中卡通形象來源於微信表情包"胖滾家族",且已獲作者的許可。
![wchat1](https://yqfile.alicdn.com/7221a4636808c3411647d15929767e3ec37f52d6.jpeg)
> 本文來源於公眾號【胖滾豬學程式設計】一個集顏值與才華於一身的女程式媛。以漫畫形式讓程式設計so easy and intere