「掃盲」 Elasticsearch

Java3y發表於2020-04-06

前言

只有光頭才能變強。

文字已收錄至我的GitHub精選文章,歡迎Star

https://github.com/ZhongFuCheng3y/3y

不知道大家的公司用Elasticsearch多不多,反正我公司的是有在用的。平時聽同事們聊天肯定避免不了不認識的技術棧,例如說:把資料放在引擎,從引擎取出資料等等。

如果對引擎不瞭解的同學,就壓根聽不懂他們在說什麼(我就是聽不懂的一位,扎心了)。引擎一般指的是搜尋引擎,現在用得比較多的就是Elasticsearch。

這篇文章主要是對Elasticsearch一個簡單的入門,沒有高深的知識和使用。至少我想做到的是:以後同事們聊引擎了,至少知道他們在講什麼。

「掃盲」 Elasticsearch

什麼是Elasticsearch?

Elasticsearch is a real-time, distributed storage, search, and analytics engine

Elasticsearch 是一個實時分散式儲存、搜尋、分析的引擎。

介紹那兒有幾個關鍵字:

  • 實時

  • 分散式

  • 搜尋

  • 分析

於是我們就得知道Elasticsearch是怎麼做到實時的,Elasticsearch的架構是怎麼樣的(分散式)。儲存、搜尋和分析(得知道Elasticsearch是怎麼儲存、搜尋和分析的)

這些問題在這篇文章中都會有提及。

我已經寫了200多篇原創技術文章了,後續會寫大資料相關的文章,如果想看我其他文章的同學,不妨關注我吧。公眾號:Java3y

如果覺得我這篇文章還不錯,對你有幫助,不要吝嗇自己的贊

為什麼要用Elasticsearch

在學習一項技術之前,必須先要了解為什麼要使用這項技術。所以,為什麼要使用Elasticsearch呢?我們在日常開發中,資料庫也能做到(實時、儲存、搜尋、分析)。

相對於資料庫,Elasticsearch的強大之處就是可以模糊查詢

有的同學可能就會說:我資料庫怎麼就不能模糊查詢了??我反手就給你寫一個SQL:

select * from user where name like '%公眾號Java3y%'

這不就可以把公眾號Java3y相關的內容搜尋出來了嗎?

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

如果對資料庫索引還不是很瞭解的同學,建議復看一下我以前的文章。我覺得我當時寫得還不賴(哈哈哈)

GitHub搜關鍵字:”索引“

而且,即便給你從資料庫根據模糊匹配查出相應的記錄了,那往往會返回大量的資料給你,往往你需要的資料量並沒有這麼多,可能50條記錄就足夠了。

還有一個就是:使用者輸入的內容往往並沒有這麼的精確,比如我從Google輸入ElastcSeach(打錯字),但是Google還是能估算我想輸入的是Elasticsearch

「掃盲」 Elasticsearch

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

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

  • 從Elasticsearch搜尋到的資料可以根據評分過濾掉大部分的,只要返回評分高的給使用者就好了(原生就支援排序)

  • 沒有那麼準確的關鍵字也能搜出相關的結果(能匹配有相關性的記錄)

下面我們就來學學為什麼Elasticsearch可以做到上面的幾點。

Elasticsearch的資料結構

眾所周知,你要在查詢的時候花得更少的時間,你就需要知道他的底層資料結構是怎麼樣的;舉個例子:

  • 樹型的查詢時間複雜度一般是O(logn)

  • 連結串列的查詢時間複雜度一般是O(n)

  • 雜湊表的查詢時間複雜度一般是O(1)

  • ….不同的資料結構所花的時間往往不一樣,你想要查詢的時候要,就需要有底層的資料結構支援

從上面說Elasticsearch的模糊查詢速度很快,那Elasticsearch的底層資料結構是什麼呢?我們來看看。

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

「掃盲」 Elasticsearch

首先我們得知道為什麼Elasticsearch為什麼可以實現快速的“模糊匹配”/“相關性查詢”,實際上是你寫入資料到Elasticsearch的時候會進行分詞

還是以上圖為例,上圖出現了4次“演算法”這個詞,我們能不能根據這次詞為它找他對應的目錄?Elasticsearch正是這樣乾的,如果我們根據上圖來做這個事,會得到類似這樣的結果:

  • 演算法  ->2,13,42,56

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

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

「掃盲」 Elasticsearch

眾所周知,世界上有這麼多的語言,那Elasticsearch怎麼切分這些詞呢?,Elasticsearch內建了一些分詞器

  • Standard  Analyzer 。按詞切分,將詞小寫

  • Simple Analyzer。按非字母過濾(符號被過濾掉),將詞小寫

  • WhitespaceAnalyzer。按照空格切分,不轉小寫

  • ….等等等

Elasticsearch分詞器主要由三部分組成:

  • ???????Character Filters(文字過濾器,去除HTML)

  • Tokenizer(按照規則切分,比如空格)

  • TokenFilter(將切分後的詞進行處理,比如轉成小寫)

顯然,Elasticsearch是老外寫的,內建的分詞器都是英文類的,而我們使用者搜尋的時候往往搜的是中文,現在中文分詞器用得最多的就是IK

扯了一大堆,那Elasticsearch的資料結構是怎麼樣的呢?看下面的圖:

「掃盲」 Elasticsearch

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

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

由於Term Dictionary的詞實在太多了,不可能把Term Dictionary所有的詞都放在記憶體中,於是Elasticsearch還抽了一層叫做Term Index,這層只儲存  部分   詞的字首Term Index會存在記憶體中(檢索會特別快)

Term Index在記憶體中是以FST(Finite State Transducers)的形式儲存的,其特點是非常節省記憶體。FST有兩個優點:

  • 1)空間佔用小。通過對詞典中單詞字首和字尾的重複利用,壓縮了儲存空間;

  • 2)查詢速度快。O(len(str))的查詢時間複雜度。

前面講到了Term Index是儲存在記憶體中的,且Elasticsearch用FST(Finite State Transducers)的形式儲存(節省記憶體空間)。Term Dictionary在Elasticsearch也是為他進行排序(查詢的時候方便),其實PostingList也有對應的優化。

PostingList會使用Frame Of Reference(FOR)編碼技術對裡邊的資料進行壓縮,節約磁碟空間

「掃盲」 Elasticsearch

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

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

「掃盲」 Elasticsearch

所以到這裡我們總結一下Elasticsearch的資料結構有什麼特點:

「掃盲」 Elasticsearch

Elasticsearch的術語和架構

從官網的介紹我們已經知道Elasticsearch是分散式儲存的,如果看過我的文章的同學,對分散式這個概念應該不陌生了。

如果對分散式還不是很瞭解的同學,建議復看一下我以前的文章。我覺得我當時寫得還不賴(哈哈哈)

GitHub搜關鍵字:

”SpringCloud“,"Zookeeper","Kafka","單點登入"

在講解Elasticsearch的架構之前,首先我們得了解一下Elasticsearch的一些常見術語。

  • Index:Elasticsearch的Index相當於資料庫的Table

  • Type:這個在新的Elasticsearch版本已經廢除(在以前的Elasticsearch版本,一個Index下支援多個Type--有點類似於訊息佇列一個topic下多個group的概念)

  • Document:Document相當於資料庫的一行記錄

  • Field:相當於資料庫的Column的概念

  • Mapping:相當於資料庫的Schema的概念

  • DSL:相當於資料庫的SQL(給我們讀取Elasticsearch資料的API)

「掃盲」 Elasticsearch

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

一個Elasticsearch叢集會有多個Elasticsearch節點,所謂節點實際上就是執行著Elasticsearch程式的機器。

「掃盲」 Elasticsearch

在眾多的節點中,其中會有一個Master Node,它主要負責維護索引後設資料、負責切換主分片和副本分片身份等工作(後面會講到分片的概念),如果主節點掛了,會選舉出一個新的主節點。

「掃盲」 Elasticsearch

從上面我們也已經得知,Elasticsearch最外層的是Index(相當於資料庫 表的概念);一個Index的資料我們可以分發到不同的Node上進行儲存,這個操作就叫做分片

比如現在我叢集裡邊有4個節點,我現在有一個Index,想將這個Index在4個節點上儲存,那我們可以設定為4個分片。這4個分片的資料合起來就是Index的資料

「掃盲」 Elasticsearch

為什麼要分片?原因也很簡單:

  • 如果一個Index的資料量太大,只有一個分片,那隻會在一個節點上儲存,隨著資料量的增長,一個節點未必能把一個Index儲存下來。

  • 多個分片,在寫入或查詢的時候就可以並行操作(從各個節點中讀寫資料,提高吞吐量)

現在問題來了,如果某個節點掛了,那部分資料就丟了嗎?顯然Elasticsearch也會想到這個問題,所以分片會有主分片和副本分片之分(為了實現高可用

資料寫入的時候是寫到主分片,副本分片會複製主分片的資料,讀取的時候主分片和副本分片都可以讀

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

「掃盲」 Elasticsearch

如果某個節點掛了,前面所提高的Master Node就會把對應的副本分片提拔為主分片,這樣即便節點掛了,資料就不會丟。

到這裡我們可以簡單總結一下Elasticsearch的架構了:

「掃盲」 Elasticsearch

Elasticsearch 寫入的流程

上面我們已經知道當我們向Elasticsearch寫入資料的時候,是寫到主分片上的,我們可以瞭解更多的細節。

客戶端寫入一條資料,到Elasticsearch叢集裡邊就是由節點來處理這次請求:

「掃盲」 Elasticsearch

叢集上的每個節點都是coordinating node協調節點),協調節點表明這個節點可以做路由。比如節點1接收到了請求,但發現這個請求的資料應該是由節點2處理(因為主分片在節點2上),所以會把請求轉發到節點2上。

  • coodinate(協調)節點通過hash演算法可以計算出是在哪個主分片上,然後路由到對應的節點

  • shard = hash(document_id) % (num_of_primary_shards)

路由到對應的節點以及對應的主分片時,會做以下的事:

  1. 將資料寫到記憶體快取區

  2. 然後將資料寫到translog快取區

  3. 每隔1s資料從buffer中refresh到FileSystemCache中,生成segment檔案,一旦生成segment檔案,就能通過索引查詢到了

  4. refresh完,memory buffer就清空了。

  5. 每隔5s中,translog 從buffer flush到磁碟中

  6. 定期/定量從FileSystemCache中,結合translog內容flush index到磁碟中。

「掃盲」 Elasticsearch

解釋一下:

  • Elasticsearch會把資料先寫入記憶體緩衝區,然後每隔1s重新整理到檔案系統快取區(當資料被重新整理到檔案系統緩衝區以後,資料才可以被檢索到)。所以:Elasticsearch寫入的資料需要1s才能查詢到

  • 為了防止節點當機,記憶體中的資料丟失,Elasticsearch會另寫一份資料到日誌檔案上,但最開始的還是寫到記憶體緩衝區,每隔5s才會將緩衝區的刷到磁碟中。所以:Elasticsearch某個節點如果掛了,可能會造成有5s的資料丟失。

  • 等到磁碟上的translog檔案大到一定程度或者超過了30分鐘,會觸發commit操作,將記憶體中的segement檔案非同步刷到磁碟中,完成持久化操作。

說白了就是:寫記憶體緩衝區(定時去生成segement,生成translog),能夠讓資料能被索引、被持久化。最後通過commit完成一次的持久化。

「掃盲」 Elasticsearch

等主分片寫完了以後,會將資料並行傳送到副本集節點上,等到所有的節點寫入成功就返回ack給協調節點,協調節點返回ack給客戶端,完成一次的寫入。

Elasticsearch更新和刪除

Elasticsearch的更新和刪除操作流程:

  • 給對應的doc記錄打上.del標識,如果是刪除操作就打上delete狀態,如果是更新操作就把原來的doc標誌為delete,然後重新新寫入一條資料

前面提到了,每隔1s會生成一個segement 檔案,那segement檔案會越來越多越來越多。Elasticsearch會有一個merge任務,會將多個segement檔案合併成一個segement檔案。

在合併的過程中,會把帶有delete狀態的doc物理刪除掉。

「掃盲」 Elasticsearch

Elasticsearch查詢

查詢我們最簡單的方式可以分為兩種:

  • 根據ID查詢doc

  • 根據query(搜尋詞)去查詢匹配的doc

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

根據ID去查詢具體的doc的流程是:

  • 檢索記憶體的Translog檔案

  • 檢索硬碟的Translog檔案

  • 檢索硬碟的Segement檔案

根據query去匹配doc的流程是:

  • 同時去查詢記憶體和硬碟的Segement檔案

「掃盲」 Elasticsearch

從上面所講的寫入流程,我們就可以知道:Get(通過ID去查Doc是實時的),Query(通過query去匹配Doc是近實時的)

  • 因為segement檔案是每隔一秒才生成一次的

Elasticsearch查詢又分可以為三個階段:

  • QUERY_AND_FETCH(查詢完就返回整個Doc內容)

  • QUERY_THEN_FETCH(先查詢出對應的Doc id ,然後再根據Doc id 匹配去對應的文件)

  • DFS_QUERY_THEN_FETCH(先算分,再查詢)

  • 「這裡的分指的是 詞頻率和文件的頻率(Term Frequency、Document Frequency)眾所周知,出現頻率越高,相關性就更強」

「掃盲」 Elasticsearch

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

QUERY_THEN_FETCH總體的流程流程大概是:

  • 客戶端請求傳送到叢集的某個節點上。叢集上的每個節點都是coordinate node(協調節點)

  • 然後協調節點將搜尋的請求轉發到所有分片上(主分片和副本分片都行)

  • 每個分片將自己搜尋出的結果(doc id)返回給協調節點,由協調節點進行資料的合併、排序、分頁等操作,產出最終結果。

  • 接著由協調節點根據 doc id 去各個節點上拉取實際document 資料,最終返回給客戶端。

Query Phase階段時節點做的事:

  • 協調節點向目標分片傳送查詢的命令(轉發請求到主分片或者副本分片上)

  • 資料節點(在每個分片內做過濾、排序等等操作),返回doc id給協調節點

Fetch Phase階段時節點做的是:

  • 協調節點得到資料節點返回的doc id,對這些doc id做聚合,然後將目標資料分片傳送抓取命令(希望拿到整個Doc記錄)

  • 資料節點按協調節點傳送的doc id,拉取實際需要的資料返回給協調節點

主流程我相信大家也不會太難理解,說白了就是:由於Elasticsearch是分散式的,所以需要從各個節點都拉取對應的資料,然後最終統一合成給客戶端

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

「掃盲」 Elasticsearch

最後

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

「掃盲」 Elasticsearch

如果文章寫得有錯誤的地方,歡迎友善指正交流。等年後還會繼續更新大資料相關的入門文章,有興趣的歡迎關注我的公眾號。覺得這篇文章還行,可以給我一個贊?

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69900354/viewspace-2673541/,如需轉載,請註明出處,否則將追究法律責任。

相關文章