Elasticsearch分享

Elasticsearch是什麼

Elasticsearch是一個基於Apache Lucene(TM)的開源搜索引擎。無論在開源還是專有領域,Lucene可以被認爲是迄今爲止最先進、性能最好的、功能最全的搜索引擎庫

但是,Lucene只是一個庫。想要使用它,你必須使用Java來作爲開發語言並將其直接集成到你的應用中,更糟糕的是,Lucene非常複雜,你需要深入瞭解檢索的相關知識來理解它是如何工作的。

Elasticsearch也使用Java開發並使用Lucene作爲其核心來實現所有索引和搜索的功能,但是它的目的是通過簡單的RESTful API來隱藏Lucene的複雜性,從而讓全文搜索變得簡單

所以,可以理解成Elasticsearch(以下簡稱Elastic)對Lucene進行了封裝,讓它變得好用

簡化的描述:

  • 文檔數據庫
  • 分佈式的實時分析搜索引擎

Elastic的主要運用領域

Elastic是一個非常著名的開源搜索和分析系統,目前被廣泛應用於互聯網多種領域中,尤其是以下三個領域特別突出。

  • 搜索領域,成爲很多搜索系統的不二之選,比如全文檢索;
  • Json文檔數據庫,相對於MongoDB(筆者未做比較,感興趣可以比較一下),讀寫性能更佳,而且支持更豐富的地理位置查詢以及數字、文本的混合查詢等;
  • 時序數據分析處理,目前是日誌處理、監控數據的存儲、分析和可視化方面做得非常好,比如咱們公司裏面使用的手機日誌的就是Elastic了,然後使用Kibana可視化日誌,感覺還是很方便的

Elastic的一些基本概念的介紹

-節點(Node):物理概念,一個運行的Elasticearch實例,一般是一臺機器上的一個進程,比如我們在自己的機器上面起了兩個Elastic,就相當於是兩個節點了。

  • 索引(Index),邏輯概念,包括配置信息mapping和倒排正排數據文件,一個索引的數據文件可能會分佈於一臺機器,也有可能分佈於多臺機器。索引的另外一層意思是倒排索引文件,索引一個文件的意思。
  • 類型(type):是屬於index下一層級的概念,可以理解成類似Mysql的表結構,然後index理解成庫;
  • 分片(Shard):爲了支持更大量的數據,索引一般會按某個維度分成多個部分,每個部分就是一個分片,分片被節點(Node)管理。一個節點(Node)一般會管理多個分片,這些分片可能是屬於同一份索引,也有可能屬於不同索引,但是爲了可靠性和可用性,同一個索引的分片儘量會分佈在不同節點(Node)上。分片有兩種,主分片和副本分片。
  • 副本(Replica):同一個分片(Shard)的備份數據,一個分片可能會有0個或多個副本,這些副本中的數據保證強一致或最終一致
  • 文檔(doc): 在Elastic中,一條數據稱爲一個文檔,就是類似Mysql裏面的一行數據了。

elastic的各個版本差異較大,這也是比較坑的地方,有些默認配置可能會不太一樣,比如在早期的elastic版本中,一個index裏面可以有多個type,但是多個type裏面如果有相同的字段,則這些字段的類型必須一樣,但是在elastic 6.x版本里面,比如,我們當前使用的是elastic 6.7版本,一個index裏面只可以定義一個type;
所以如果沒有特別說明,全文的elastic表示6.7版本,在這個版本下面,一個index默認有5個主分片和1個副本分片,然後統一主分片和副本分片不可以在同一個節點下面

  • number_of_shards, 主分片數量,索引一旦創建,不可以更改
  • number_of_replicas, 副本分片數量,默認爲1, 這個可以更改

avatar

Elastic的寫操作流程

Elastic整體上採用的是一主多副的架構
avatar

  1. 先根據_routing規則選擇發給哪個Shard, 集羣中找出出該Shard的Primary節點
    Index Request中可以設置使用哪個Filed的值作爲路由參數,如果沒有設置,則使用Mapping中的配置,如果mapping中也沒有配置,則使用_id作爲路由參數,然後通過_routing的Hash值選擇出Shard
  2. 請求接着會發送給Primary Shard
  3. 在Primary Shard上執行成功後, 再從Primary Shard上將請求同時發送給多個Replica Shard
  4. 請求在多個Replica Shard上執行成功並返回給Primary Shard
  5. 寫入請求執行成功,返回結果給客戶端

需要說明的一點是,寫入成功,是指寫入到Lucene,並不包含refresh的操作完成,這個後面會涉及到在實際工作中出現的一個bug

針對上面的寫入的方式,寫入操作的延時就等於latency = Latency(Primary Write) + Max(Replicas Write),缺點是寫入效率比較低,除了寫入主分片,還需要寫入所有副本分片,好處也很明顯,通過副本分片可以拓展讀性能,因爲讀的時候,僅僅需要任一副本分片就可以讀取數據,除此之外,就是避免在某個分片所在的節點崩了之後,數據不會丟失。

Elastic的TransLog

是爲了減少磁盤的IO來保證讀寫性能,同事也爲了保證對於那些已經寫入內存但是還沒寫進磁盤的數據在放生宕機的時候,數據不會丟失。

avatar

針對上圖,需要說明的幾點是:

  • refresh表示將Lucene內存中的對象轉化爲完整的Segment之後,纔可以被搜索到,這個時間一般是1秒,所以我們一般寫入Elastic的數據,1秒之後纔可以被搜索到,Elasticsearch在搜索方面是NRT(Near Real Time)近實時的系統
  • 但是Elastic還有作爲NoSQL的一面,如果我們按照GetById方法查詢,這個時候就變成了RT系統,這是因爲這種查詢方式是直接查詢TransLog(上面那個),這個是可以直接被查到的,這個也是我後面纔看到了,所以當時的那個問題,我並沒有採用這種方式
  • 每隔一段時間之後,Lucene會將內存中的Segment flush到磁盤上,此時數據被持久化了,歷史的Translog就會被清理掉。

Elastic的讀操作

Elasticsearch中每個Shard都會有多個Replica,主要是爲了保證數據可靠性,除此之外,還可以增加讀能力,因爲寫的時候雖然要寫大部分Replica Shard,但是查詢的時候只需要查詢Primary和Replica中的任何一個就可以了。
avatat

然後針對上面說的GetById的查詢方式和普通的Search,下面有個示意圖
avatar

總體而言讀的流程就是

  • 在所有分片中根據篩選條件找出docid(主分片和副本分片只需要一個),query
  • 然後根據docid取出完整的文檔,fetch
  • 搜索裏面有一種算分邏輯是根據TF(Term Frequency)和IDF(Inverse Document Frequency)計算基礎分
  • 上面的三個步驟都是在一個shard裏面進行的,最後一步將所有內容彙總,根據評分排名,取出部分數據

TF:目標詞在字段中出現的次數,加分
IDF:目標詞在所有文檔中出現的次數,減分

Elastic在使用中的出現的問題和需要注意的地方

索引index按照日期分組

  • 代碼實例
class Record(Document):
    class Index:
        name = "purifier-record-*"
        
    def save(self, **kwargs):
        kwargs['index'] = self.date_created.strftime('purifier-record-%Y%m%d')
        return super(Record, self).save(**kwargs)        
  • 按照日期分index的原因:index一旦建立,主分片的數量是不可以改變的,相當於是無法水平拓展,如果後期數據量比較大,有可能會導致一臺機器空間不足,(PS: 如果你要說有錢,加機器硬件,當我沒說,但是一個機器的性能畢竟有限,特別是面對海量日誌之類的東西的時候),所有我們對index進行切分了,按照月份也可以:
  • 導致的問題:我們在使用Elastic搜索的時候,沒有指定index,所以最後相當於是全量搜索es,速度很慢,經常接口超時,lambda是15秒,後來我們查詢的時候加上了時間作爲篩選項,快了很多,基本都是100ms以下吧

Elastic默認的返回數量是10條,這個需要注意,否則有可能會遇到明明數據庫存在,都是就是查找不出來的情況,坑過

Elastic作爲搜索引擎是近乎實時的

這個需要主要,之前我遇到過剛寫入數據,然後立馬更新緩存或者執行異步任務的時候,緩存更新的是之前的數據,異步任務裏面沒有搜到剛纔那條數據,可能是我搜索方式不對,之後可以使用GetbyId的方式,我試試看

最後放上學習資料
Elastic權威指南,版本比較老,看看一些思想,有些操作在新版本需要修改或者廢棄了

Elasticsearch技術探討,阿里的技術人寫的一些分享

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章