「掃盲」 Elasticsearch

什么是Elasticsearch?

Elasticsearch is a real-time, distributed storage, search, and analytics engine
Elasticsearch 是一個實時的分布式存儲、搜索、分析的引擎。

介紹那兒有幾個關(guān)鍵字:
實時、分布式、搜索、分析

于是我們就得知道Elasticsearch是怎么做到實時的,Elasticsearch的架構(gòu)是怎么樣的(分布式)。存儲、搜索和分析(得知道Elasticsearch是怎么存儲、搜索和分析的)

為什么要用Elasticsearch

在學(xué)習(xí)一項技術(shù)之前,必須先要了解為什么要使用這項技術(shù)。所以,為什么要使用Elasticsearch呢?我們在日常開發(fā)中,數(shù)據(jù)庫也能做到(實時、存儲、搜索、分析)。

相對于數(shù)據(jù)庫,Elasticsearch的強大之處就是可以模糊查詢。

有的同學(xué)可能就會說:我數(shù)據(jù)庫怎么就不能模糊查詢了??我反手就給你寫一個SQL:
select * from user where name like '%公眾號Java%'
這不就可以把公眾號Java相關(guān)的內(nèi)容搜索出來了嗎?

的確,這樣做的確可以。但是要明白的是:name like %Java%這類的查詢是不走索引的,不走索引意味著:只要你的數(shù)據(jù)庫的量很大(1億條),你的查詢肯定會是秒級別的

而且,即便給你從數(shù)據(jù)庫根據(jù)模糊匹配查出相應(yīng)的記錄了,那往往會返回大量的數(shù)據(jù)給你,往往你需要的數(shù)據(jù)量并沒有這么多,可能50條記錄就足夠了。

還有一個就是:用戶輸入的內(nèi)容往往并沒有這么的精確,比如我從Google輸入ElastcSeach(打錯字),但是Google還是能估算我想輸入的是Elasticsearch

而Elasticsearch是專門做搜索的,就是為了解決上面所講的問題而生的,換句話說:

Elasticsearch對模糊搜索非常擅長(搜索速度很快)

從Elasticsearch搜索到的數(shù)據(jù)可以根據(jù)評分過濾掉大部分的,只要返回評分高的給用戶就好了(原生就支持排序)

沒有那么準確的關(guān)鍵字也能搜出相關(guān)的結(jié)果(能匹配有相關(guān)性的記錄)

下面我們就來學(xué)學(xué)為什么Elasticsearch可以做到上面的幾點。

Elasticsearch的數(shù)據(jù)結(jié)構(gòu)

眾所周知,你要在查詢的時候花得更少的時間,你就需要知道他的底層數(shù)據(jù)結(jié)構(gòu)是怎么樣的;舉個例子:

樹型的查找時間復(fù)雜度一般是O(logn)

鏈表的查找時間復(fù)雜度一般是O(n)

哈希表的查找時間復(fù)雜度一般是O(1)

….不同的數(shù)據(jù)結(jié)構(gòu)所花的時間往往不一樣,你想要查找的時候要快,就需要有底層的數(shù)據(jù)結(jié)構(gòu)支持

從上面說Elasticsearch的模糊查詢速度很快,那Elasticsearch的底層數(shù)據(jù)結(jié)構(gòu)是什么呢?我們來看看。

我們根據(jù)“完整的條件”查找一條記錄叫做正向索引;我們一本書的章節(jié)目錄就是正向索引,通過章節(jié)名稱就找到對應(yīng)的頁碼。

file

http://img.blog.itpub.net/blog/2020/01/15/ec0abd31539b7b77.jpeg?x-oss-process=style/bb

分詞

首先我們得知道為什么Elasticsearch為什么可以實現(xiàn)快速的“模糊匹配”/“相關(guān)性查詢”,實際上是你寫入數(shù)據(jù)到Elasticsearch的時候會進行分詞。

還是以上圖為例,上圖出現(xiàn)了4次“算法”這個詞,我們能不能根據(jù)這次詞為它找他對應(yīng)的目錄?Elasticsearch正是這樣干的,如果我們根據(jù)上圖來做這個事,會得到類似這樣的結(jié)果:

算法 ->2,13,42,56

這代表著“算法”這個詞肯定是在第二頁、第十三頁、第四十二頁、第五十六頁出現(xiàn)過。這種根據(jù)某個詞(不完整的條件)再查找對應(yīng)記錄,叫做倒排索引。

再看下面的圖,好好體會一下:

file

http://img.blog.itpub.net/blog/2020/01/15/49ac85e92050ae30.jpeg?x-oss-process=style/bb

分詞器

眾所周知,世界上有這么多的語言,那Elasticsearch怎么切分這些詞呢?,Elasticsearch內(nèi)置了一些分詞器
Standard Analyzer 。按詞切分,將詞小寫
Simple Analyzer。按非字母過濾(符號被過濾掉),將詞小寫
WhitespaceAnalyzer。按照空格切分,不轉(zhuǎn)小寫
….等等等

Elasticsearch分詞器主要由三部分組成:
Character Filters(文本過濾器,去除HTML)
Tokenizer(按照規(guī)則切分,比如空格)
TokenFilter(將切分后的詞進行處理,比如轉(zhuǎn)成小寫)

顯然,Elasticsearch是老外寫的,內(nèi)置的分詞器都是英文類的,而我們用戶搜索的時候往往搜的是中文,現(xiàn)在中文分詞器用得最多的就是IK。

分詞后的數(shù)據(jù)結(jié)構(gòu)

file

http://img.blog.itpub.net/blog/2020/01/15/e476671247601eb8.jpeg?x-oss-process=style/bb

我們輸入一段文字,Elasticsearch會根據(jù)分詞器對我們的那段文字進行分詞(也就是圖上所看到的Ada/Allen/Sara..),這些分詞匯總起來我們叫做Term Dictionary,而我們需要通過分詞找到對應(yīng)的記錄,這些文檔ID保存在PostingList

在Term Dictionary中的詞由于是非常非常多的,所以我們會為其進行排序,等要查找的時候就可以通過二分來查,不需要遍歷整個Term Dictionary

由于Term Dictionary的詞實在太多了,不可能把Term Dictionary所有的詞都放在內(nèi)存中,于是Elasticsearch還抽了一層叫做Term Index,這層只存儲 部分 詞的前綴,Term Index會存在內(nèi)存中(檢索會特別快)

Term Index在內(nèi)存中是以FST(Finite State Transducers)的形式保存的,其特點是非常節(jié)省內(nèi)存。FST有兩個優(yōu)點:
1)空間占用小。通過對詞典中單詞前綴和后綴的重復(fù)利用,壓縮了存儲空間;
2)查詢速度快。O(len(str))的查詢時間復(fù)雜度。

前面講到了Term Index是存儲在內(nèi)存中的,且Elasticsearch用FST(Finite State Transducers)的形式保存(節(jié)省內(nèi)存空間)。Term Dictionary在Elasticsearch也是為他進行排序(查找的時候方便),其實PostingList也有對應(yīng)的優(yōu)化。
PostingList會使用Frame Of Reference(FOR)編碼技術(shù)對里邊的數(shù)據(jù)進行壓縮,節(jié)約磁盤空間。

file

http://img.blog.itpub.net/blog/2020/01/15/0a79629a90e32902.jpeg?x-oss-process=style/bb

PostingList里邊存的是文檔ID,我們查的時候往往需要對這些文檔ID做交集和并集的操作(比如在多條件查詢時),PostingList使用Roaring Bitmaps來對文檔ID進行交并集操作。

使用Roaring Bitmaps的好處就是可以節(jié)省空間和快速得出交并集的結(jié)果。

file

http://img.blog.itpub.net/blog/2020/01/15/2f498bfcad6bf161.jpeg?x-oss-process=style/bb

所以到這里我們總結(jié)一下Elasticsearch的數(shù)據(jù)結(jié)構(gòu)有什么特點:

file

https://mmbiz.qpic.cn/sz_mmbiz_jpg/2BGWl1qPxib0bRFtypiaZQYvF8c6jrGaXAStRPlG9EdiafSyJeib83230WTOHk1jj3OojmFaU6ra6elEedLIgmibcWg/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

Elasticsearch的術(shù)語和架構(gòu)

在講解Elasticsearch的架構(gòu)之前,首先我們得了解一下Elasticsearch的一些常見術(shù)語。

Index:Elasticsearch的Index相當(dāng)于數(shù)據(jù)庫的Table

Type:這個在新的Elasticsearch版本已經(jīng)廢除(在以前的Elasticsearch版本,一個Index下支持多個Type--有點類似于消息隊列一個topic下多個group的概念)

Document:Document相當(dāng)于數(shù)據(jù)庫的一行記錄

Field:相當(dāng)于數(shù)據(jù)庫的Column的概念

Mapping:相當(dāng)于數(shù)據(jù)庫的Schema的概念

DSL:相當(dāng)于數(shù)據(jù)庫的SQL(給我們讀取Elasticsearch數(shù)據(jù)的API)

file

http://img.blog.itpub.net/blog/2020/01/15/4c87fb1488ae694d.jpeg?x-oss-process=style/bb

相信大家看完上面的對比圖,對Elasticsearch的一些術(shù)語就不難理解了。那Elasticsearch的架構(gòu)是怎么樣的呢?下面我們來看看:

一個Elasticsearch集群會有多個Elasticsearch節(jié)點,所謂節(jié)點實際上就是運行著Elasticsearch進程的機器。

file

http://img.blog.itpub.net/blog/2020/01/15/3c5431e7e272353e.jpeg?x-oss-process=style/bb

在眾多的節(jié)點中,其中會有一個Master Node,它主要負責(zé)維護索引元數(shù)據(jù)、負責(zé)切換主分片和副本分片身份等工作(后面會講到分片的概念),如果主節(jié)點掛了,會選舉出一個新的主節(jié)點。

file

http://img.blog.itpub.net/blog/2020/01/15/a27f04b40d53acb4.jpeg?x-oss-process=style/bb

從上面我們也已經(jīng)得知,Elasticsearch最外層的是Index(相當(dāng)于數(shù)據(jù)庫 表的概念);一個Index的數(shù)據(jù)我們可以分發(fā)到不同的Node上進行存儲,這個操作就叫做分片。

比如現(xiàn)在我集群里邊有4個節(jié)點,我現(xiàn)在有一個Index,想將這個Index在4個節(jié)點上存儲,那我們可以設(shè)置為4個分片。這4個分片的數(shù)據(jù)合起來就是Index的數(shù)據(jù)

file

http://img.blog.itpub.net/blog/2020/01/15/a7d730c3413c4d97.jpeg?x-oss-process=style/bb

為什么要分片?原因也很簡單:
如果一個Index的數(shù)據(jù)量太大,只有一個分片,那只會在一個節(jié)點上存儲,隨著數(shù)據(jù)量的增長,一個節(jié)點未必能把一個Index存儲下來。
多個分片,在寫入或查詢的時候就可以并行操作(從各個節(jié)點中讀寫數(shù)據(jù),提高吞吐量)

現(xiàn)在問題來了,如果某個節(jié)點掛了,那部分數(shù)據(jù)就丟了嗎?顯然Elasticsearch也會想到這個問題,所以分片會有主分片和副本分片之分(為了實現(xiàn)高可用)

數(shù)據(jù)寫入的時候是寫到主分片,副本分片會復(fù)制主分片的數(shù)據(jù),讀取的時候主分片和副本分片都可以讀。

Index需要分為多少個主分片和副本分片都是可以通過配置設(shè)置的

file

http://img.blog.itpub.net/blog/2020/01/15/f2d0ec61b5e4b7c3.jpeg?x-oss-process=style/bb

如果某個節(jié)點掛了,前面所提高的Master Node就會把對應(yīng)的副本分片提拔為主分片,這樣即便節(jié)點掛了,數(shù)據(jù)就不會丟。

到這里我們可以簡單總結(jié)一下Elasticsearch的架構(gòu)了:

file

http://img.blog.itpub.net/blog/2020/01/15/aea5ad333db601f0.jpeg?x-oss-process=style/bb

Elasticsearch 寫入的流程

上面我們已經(jīng)知道當(dāng)我們向Elasticsearch寫入數(shù)據(jù)的時候,是寫到主分片上的,我們可以了解更多的細節(jié)。

客戶端寫入一條數(shù)據(jù),到Elasticsearch集群里邊就是由節(jié)點來處理這次請求:

file

http://img.blog.itpub.net/blog/2020/01/15/ec4b71bc1213e12b.jpeg?x-oss-process=style/bb

集群上的每個節(jié)點都是coordinating node(協(xié)調(diào)節(jié)點),協(xié)調(diào)節(jié)點表明這個節(jié)點可以做路由。比如節(jié)點1接收到了請求,但發(fā)現(xiàn)這個請求的數(shù)據(jù)應(yīng)該是由節(jié)點2處理(因為主分片在節(jié)點2上),所以會把請求轉(zhuǎn)發(fā)到節(jié)點2上。

coodinate(協(xié)調(diào))節(jié)點通過hash算法可以計算出是在哪個主分片上,然后路由到對應(yīng)的節(jié)點
shard = hash(document_id) % (num_of_primary_shards)

路由到對應(yīng)的節(jié)點以及對應(yīng)的主分片時,會做以下的事:
1、將數(shù)據(jù)寫到內(nèi)存緩存區(qū)
2、然后將數(shù)據(jù)寫到translog緩存區(qū)
3、每隔1s數(shù)據(jù)從buffer中refresh到FileSystemCache中,生成segment文件,一旦生成segment文件,就能通過索引查詢到了
4、refresh完,memory buffer就清空了。
5、每隔5s中,translog 從buffer flush到磁盤中
6、定期/定量從FileSystemCache中,結(jié)合translog內(nèi)容flush index到磁盤中。

file

http://img.blog.itpub.net/blog/2020/01/15/25213cbe268544ed.jpeg?x-oss-process=style/bb

解釋一下:
1、Elasticsearch會把數(shù)據(jù)先寫入內(nèi)存緩沖區(qū),然后每隔1s刷新到文件系統(tǒng)緩存區(qū)(當(dāng)數(shù)據(jù)被刷新到文件系統(tǒng)緩沖區(qū)以后,數(shù)據(jù)才可以被檢索到)。所以:Elasticsearch寫入的數(shù)據(jù)需要1s才能查詢到
2、為了防止節(jié)點宕機,內(nèi)存中的數(shù)據(jù)丟失,Elasticsearch會另寫一份數(shù)據(jù)到日志文件上,但最開始的還是寫到內(nèi)存緩沖區(qū),每隔5s才會將緩沖區(qū)的刷到磁盤中。所以:Elasticsearch某個節(jié)點如果掛了,可能會造成有5s的數(shù)據(jù)丟失。
3、等到磁盤上的translog文件大到一定程度或者超過了30分鐘,會觸發(fā)commit操作,將內(nèi)存中的segement文件異步刷到磁盤中,完成持久化操作。

說白了就是:寫內(nèi)存緩沖區(qū)(定時去生成segement,生成translog),能夠讓數(shù)據(jù)能被索引、被持久化。最后通過commit完成一次的持久化。

file

http://img.blog.itpub.net/blog/2020/01/15/ba437a2f703752ad.jpeg?x-oss-process=style/bb

等主分片寫完了以后,會將數(shù)據(jù)并行發(fā)送到副本集節(jié)點上,等到所有的節(jié)點寫入成功就返回ack給協(xié)調(diào)節(jié)點,協(xié)調(diào)節(jié)點返回ack給客戶端,完成一次的寫入。

Elasticsearch更新和刪除

Elasticsearch的更新和刪除操作流程:
給對應(yīng)的doc記錄打上.del標識,如果是刪除操作就打上delete狀態(tài),如果是更新操作就把原來的doc標志為delete,然后重新新寫入一條數(shù)據(jù)

前面提到了,每隔1s會生成一個segement 文件,那segement文件會越來越多越來越多。Elasticsearch會有一個merge任務(wù),會將多個segement文件合并成一個segement文件。

在合并的過程中,會把帶有delete狀態(tài)的doc給物理刪除掉。

file

http://img.blog.itpub.net/blog/2020/01/15/1e88128d2422b165.jpeg?x-oss-process=style/bb

Elasticsearch查詢

查詢我們最簡單的方式可以分為兩種:
1、根據(jù)ID查詢doc
2、根據(jù)query(搜索詞)去查詢匹配的doc

public TopDocs search(Query query, int n);
public Document doc(int docID);

根據(jù)ID去查詢具體的doc的流程是:
1、檢索內(nèi)存的Translog文件
2、檢索硬盤的Translog文件
3、檢索硬盤的Segement文件

根據(jù)query去匹配doc的流程是:
同時去查詢內(nèi)存和硬盤的Segement文件

file

http://img.blog.itpub.net/blog/2020/01/15/e28927442f4c96ed.jpeg?x-oss-process=style/bb

從上面所講的寫入流程,我們就可以知道,因為segement文件是每隔一秒才生成一次的,Get(通過ID去查Doc是實時的),Query(通過query去匹配Doc是近實時的)

Elasticsearch查詢又分可以為三個階段:
1、QUERY_AND_FETCH(查詢完就返回整個Doc內(nèi)容)
2、QUERY_THEN_FETCH(先查詢出對應(yīng)的Doc id ,然后再根據(jù)Doc id 匹配去對應(yīng)的文檔)
3、DFS_QUERY_THEN_FETCH(先算分,再查詢)

「這里的分指的是 詞頻率和文檔的頻率(Term Frequency、Document Frequency)眾所周知,出現(xiàn)頻率越高,相關(guān)性就更強」

file

http://img.blog.itpub.net/blog/2020/01/15/695a11190bcea92c.jpeg?x-oss-process=style/bb

一般我們用得最多的就是QUERY_THEN_FETCH,第一種查詢完就返回整個Doc內(nèi)容(QUERY_AND_FETCH)只適合于只需要查一個分片的請求。

QUERY_THEN_FETCH總體的流程流程大概是:
1、客戶端請求發(fā)送到集群的某個節(jié)點上。集群上的每個節(jié)點都是coordinate node(協(xié)調(diào)節(jié)點)
2、然后協(xié)調(diào)節(jié)點將搜索的請求轉(zhuǎn)發(fā)到所有分片上(主分片和副本分片都行)
3、每個分片將自己搜索出的結(jié)果(doc id)返回給協(xié)調(diào)節(jié)點,由協(xié)調(diào)節(jié)點進行數(shù)據(jù)的合并、排序、分頁等操作,產(chǎn)出最終結(jié)果。
4、接著由協(xié)調(diào)節(jié)點根據(jù) doc id 去各個節(jié)點上拉取實際的 document 數(shù)據(jù),最終返回給客戶端。

Query Phase階段時節(jié)點做的事:
1、協(xié)調(diào)節(jié)點向目標分片發(fā)送查詢的命令(轉(zhuǎn)發(fā)請求到主分片或者副本分片上)
2、數(shù)據(jù)節(jié)點(在每個分片內(nèi)做過濾、排序等等操作),返回doc id給協(xié)調(diào)節(jié)點

Fetch Phase階段時節(jié)點做的是:
1、協(xié)調(diào)節(jié)點得到數(shù)據(jù)節(jié)點返回的doc id,對這些doc id做聚合,然后將目標數(shù)據(jù)分片發(fā)送抓取命令(希望拿到整個Doc記錄)
2、數(shù)據(jù)節(jié)點按協(xié)調(diào)節(jié)點發(fā)送的doc id,拉取實際需要的數(shù)據(jù)返回給協(xié)調(diào)節(jié)點

主流程我相信大家也不會太難理解,說白了就是:由于Elasticsearch是分布式的,所以需要從各個節(jié)點都拉取對應(yīng)的數(shù)據(jù),然后最終統(tǒng)一合成給客戶端

只是Elasticsearch把這些活都干了,我們在使用的時候無感知而已。

file

http://img.blog.itpub.net/blog/2020/01/15/1fd511e83098d3be.jpeg?x-oss-process=style/bb

倒排索引

1. 倒排索引是什么?

假設(shè)有一個交友網(wǎng)站,信息表如下:


file

美女1:“我要找在上海做 PHP 的哥哥?!?br> 需要匹配 性別、城市、語言列。

美女2:“我要找北京的愛旅游、愛美食的 JAVA 哥哥?!?br> 更復(fù)雜了是吧,實際場景中,會有更復(fù)雜的排列組合。

對于這類的搜索,關(guān)系型數(shù)據(jù)庫的索引就很難應(yīng)付了,適合使用全文搜索的倒排索引。
倒排索引是一種數(shù)據(jù)庫的索引形式,存儲了 “內(nèi)容 -> 文檔” 映射關(guān)系,目的是快速的進行全文搜索。

2. 倒排索引是怎么工作的?

主要包括2個過程:創(chuàng)建倒排索引、倒排索引搜索

2.1 創(chuàng)建倒排索引

舉個例子,有2個文檔:

Document#1:“Recipe of pasta with sauce pesto”

Document#2:“Recipe of delicious carbonara pasta”

先對文檔進行分詞,形成一個個的 token,也就是 單詞,然后保存這些 token 與文檔的對應(yīng)關(guān)系。結(jié)果如下:


file

2.2 倒排索引搜索

搜索示例:搜索 “pasta recipe”
先分詞,得到2個 token,( “pasta”、“recipe” )。
然后去倒排索引中進行匹配。


file

這2個詞在2個文檔中都匹配,所以2個文檔都會返回,而且分數(shù)相同。

搜索 “carbonara pasta”


file

同樣,2個文檔都匹配,都會返回。
這次 document#2 的分數(shù)要比 document#1 高。因為 #2 匹配了2個詞(“carbonara”、“pasta”),#1 只匹配了一個(“pasta”)。

2.3 轉(zhuǎn)換

有時我們可以在保存和搜索之前對 token 進行一些轉(zhuǎn)換,最普遍的例如:

1、扔掉停止詞
停止詞是那些使用量非常大,但又沒有什么意義的詞。
例如英文中的 “of”, “the”, “for” ……

2、元素化
把單詞處理為字典中的標準詞,例如:
“running” => “run”
“walks” => “walk”
“thought” =>“think”

3、詞干分析
通過切斷詞尾將一個詞轉(zhuǎn)換成詞根形式的過程。
不能處理不規(guī)則動詞的情況,但可以處理字典中沒有的詞。

最后

這篇文章主要對Elasticsearch簡單入了個門,實際使用肯定還會遇到很多坑,但我目前就到這里就結(jié)束了。


file

參考:
https://mp.weixin.qq.com/s/tq3zMbs-ZmSK-trprq82gg
http://blog.itpub.net/69900354/viewspace-2673541/
https://www.cnblogs.com/yogoup/p/12216663.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容