圖解分布式搜索引擎ElasticSearch
1.基礎概念

index:索引
一個索引就是一個擁有幾分相似特征的檔案的集合,比如說,你可以有一個客戶資料的索引,另一個產品目錄的索引,還有一個訂單資料的索引,一個索引由一個名字來標識(必須全部是小寫字母的),并且當我們要對對應于這個索引中的檔案進行索引、搜索、更新和洗掉的時候,都要使用到這個名字,在一個集群中,可以定義任意多的索引,
type:型別
在一個索引中,你可以定義一種或多種型別,一個型別是你的索引的一個邏輯上的分類/磁區,其語意完全由你來定,通常,會為具有一組共同欄位的檔案定義一個型別,比如說,我們假設你運營一個博客平臺并且將你所有的資料存盤到一個索引中,在這個索引中,你可以為用戶資料定義一個型別,為博客資料定義另一個型別,當然,也可以為評論資料定義另一個型別,

field:欄位
相當于是資料表的欄位,對檔案資料根據不同屬性進行的分類標識,
document:檔案
一個檔案是一個可被索引的基礎資訊單元,比如,你可以擁有某一個客戶的檔案,某一個產品的一個檔案,當然,也可以擁有某個訂單的一個檔案,檔案以JSON格式來表示,
如:
{
"user": "Alice",
"title": "Dev",
"desc": "996"
}
在一個index/type里面,你可以存盤任意多的檔案,注意,盡管一個檔案,物理上存在于一個索引之中,檔案必須被索引/賦予一個索引的type,而doucument是ElasticSearch中最小的資料單元
mapping:映射
mapping是處理資料的方式和規則方面做一些限制,如某個欄位的資料型別、默認值、分析器、是否被索引等等,這些都是映射里面可以設定的,其它就是處理es里面資料的一些使用規則設定也叫做映射,按著最優規則處理資料對性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能對性能更好,
NRT(near real-time):接近實時
ElasticSearch是一個接近實時的搜索平臺,這意味著,從索引一個檔案直到這個檔案能夠被搜索到有一個輕微的延遲(通常是1秒以內),
cluster:集群
一個集群就是由一個或多個節點組織在一起,它們共同持有整個的資料,并一起提供索引和搜索功能,一個集群由一個唯一的名字標識,這個名字默認就是“elasticsearch”,這個名字是重要的,因為一個節點只能通過指定某個集群的名字,來加入這個集群,
node:節點
一個節點是集群中的一個服務器,作為集群的一部分,它存盤資料,參與集群的索引和搜索功能,和集群類似,一個節點也是由一個名字來標識的,默認情況下,這個名字是一個隨機的漫威漫畫角色的名字,這個名字會在啟動的時候賦予節點,這個名字對于管理作業來說挺重要的,因為在這個管理程序中,你會去確定網路中的哪些服務器對應于ElasticSearch集群中的哪些節點,
一個節點可以通過配置集群名稱的方式來加入一個指定的集群,默認情況下,每個節點都會被安排加入到一個叫做“elasticsearch”的集群中,這意味著,如果你在你的網路中啟動了若干個節點,并假定它們能夠相互發現彼此,它們將會自動地形成并加入到一個叫做“elasticsearch”的集群中,
在一個集群里,只要你想,可以擁有任意多個節點,而且,如果當前你的網路中沒有運行任何Elasticsearch節點,這時啟動一個節點,會默認創建并加入一個叫做“elasticsearch”的集群,
shards&replicas:分片和復制

一個索引可以存盤超出單個結點硬體限制的大量資料,比如,一個具有10億檔案的索引占據1TB的磁盤空間,而任一節點都沒有這樣大的磁盤空間;或者單個節點處理搜索請求,回應太慢,為了解決這個問題,ElasticSearch提供了將索引劃分成多份的能力,這些份就叫做分片,當創建一個索引的時候,可以指定想要的分片的數量,每個分片本身也是一個功能完善并且獨立的“索引”,這個“索引”可以被放置到集群中的任何節點上,
分片很重要,主要有兩方面的原因:
1)允許你水平分割/擴展你的內容容量,
2)允許你在分片(潛在地,位于多個節點上)之上進行分布式的、并行的操作,進而提高性能/吞吐量,
至于一個分片怎樣分布,它的檔案怎樣聚合回搜索請求,是完全由ElasticSearch管理的,對于作為用戶的你來說,這些都是透明的,
在一個網路/云的環境里,失敗隨時都可能發生,在某個分片/節點不知怎么的就處于離線狀態,或者由于任何原因消失了,這種情況下,有一個故障轉移機制是非常有用并且是強烈推薦的,為此目的,ElasticSearch允許你創建分片的一份或多份拷貝,這些拷貝叫做復制分片,或者直接叫復制,
復制之所以重要,有兩個主要原因: 在分片/節點失敗的情況下,提供了高可用性,因為這個原因,注意到復制分片從不與原/主要(original/primary)分片置于同一節點上是非常重要的,擴展你的搜索量/吞吐量,因為搜索可以在所有的復制上并行運行,總之,每個索引可以被分成多個分片,一個索引也可以被復制0次(意思是沒有復制)或多次,一旦復制了,每個索引就有了主分片(作為復制源的原來的分片)和復制分片(主分片的拷貝)之別,分片和復制的數量可以在索引創建的時候指定,在索引創建之后,你可以在任何時候動態地改變復制的數量,但是你事后不能改變分片的數量,
默認情況下,ElasticSearch中的每個索引被分片5個主分片和1個復制,這意味著,如果你的集群中至少有兩個節點,你的索引將會有5個主分片和另外5個復制分片(1個完全拷貝),這樣的話每個索引總共就有10個分片,
2.讀寫資料
寫入資料

- 客戶端選擇一個 node 發送請求過去,這個 node 就是 coordinating node(協調節點)
- coordinating node 對 document 進行路由,將請求轉發給對應的 node(有 primary shard)
- node 上的 主分片(primary shard)處理請求,然后將資料同步到 復制分片(replica node)
- node報告成功到協調節點,協調節點再報告給客戶端
寫一致性如何保證
- one:只要有一個primary shard是active活躍可用的,就可以執行,
- all:必須所有的primary shard和replica shard都是活躍的,才可以執行這個寫操作
- quorum:默認的值,要求所有的shard中,必須是大部分的shard都是活躍的,可用的,才可以執行這個寫操作
上面三點其實很好理解,只有quorum所謂的“大部分”感覺不是那么的明確,下面有個公式,當集群中的active(可用)分片數量達到如下公式結果時寫操作就是可以執行的,否則該操作將無法進行,
int( (primary + number_of_replicas) / 2 ) + 1
假設我們創建了一個student索引,并且設定primary shard為3個,replica shard有1個(這個1個是相對于索引來說的,對于主分片該數字1意味著每個primary shard都對應的存在一個副本),也就意味著primary=3,number_of_replicas=1(依然是相對于索引),shard總數為6,
此時計算上面公式可知:
int((3+1)/2) + 1 = 3
也就是說當集群中可用的shard數量>=3寫操作就是可以執行的,
讀取資料

可以通過 doc id 來查詢,會根據 doc id 進行 hash,判斷出來當時把 doc id 分配到了哪個 shard 上面去,從那個 shard 去查詢,
- 客戶端發送請求到任意一個 node,成為 coordinate node
- coordinate node 對 doc id 進行哈希路由,將請求轉發到對應的 node,此時會使用 round-robin隨機輪詢演算法,在 primary shard 以及其所有 replica 中隨機選擇一個,讓讀請求負載均衡
- 接收請求的 node 回傳 document 給 coordinate node
- coordinate node 回傳 document 給客戶端
搜索資料
MySQL中索引實作
MyISAM的索引實作
MyISAM表的索引和資料是分離的,索引保存在”表名.MYI”檔案內,而資料保存在“表名.MYD”檔案內,
MyISAM的索引方式也叫做“非聚集”的,之所以這么稱呼是為了與InnoDB的聚集索引區分,

InnoDB的索引實作
Primary key 這種索引叫做聚集索引,因為InnoDB的資料檔案本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則MySQL系統會自動選擇一個可以唯一標識資料記錄的列作為主鍵,如果不存在這種列,則MySQL自動為InnoDB表生成一個隱含欄位作為主鍵,這個欄位長度為6個位元組,型別為長整形,

這里以英文字符的ASCII碼作為比較準則,聚集索引這種實作方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然后用主鍵到主索引中檢索獲得記錄
倒排索引
以「冰與火之歌」中的文本作為例子:

流程
- 客戶端發送請求到一個 coordinate node
- 協調節點將搜索請求轉發到所有的 shard 對應的 primary shard 或 replica shard,都可以
- query phase:每個 shard 將自己的搜索結果(其實就是一些 doc id)回傳給協調節點,由協調節點進行資料的合并、排序、分頁等操作,產出最終結果
- fetch phase:接著由協調節點根據 doc id 去各個節點上拉取實際的 document 資料,最侄訓傳給客戶端
寫入資料的底層邏輯
write
資料先寫入in-memory buffer,在寫入buffer的同時將資料寫入translog日志檔案,注意:此時資料還沒有被成功es索引記錄,因此無法搜索到對應資料;

refresh
refresh 默認 1s,執行一次下圖流程,ES 是支持修改這個值的,通過 index.refresh_interval 設定 refresh間隔時間,refresh 流程大致如下:
- in-memory buffer 中的檔案寫入到新的 segment 中,但 segment 是存盤在檔案系統的快取中,此時檔案可以被搜索到
- 最后清空 in-memory buffer,注意: Translog 沒有被清空,為了將 segment 資料寫到磁盤,檔案經過 refresh 后, segment 暫時寫到檔案系統快取,這樣避免了性能 IO 操作,又可以使檔案搜索到,refresh 默認1s執行一次,性能損耗太大,一般建議稍微延長這個 refresh 時間間隔,比如 5s,因此,ES 其實就是準實時,達不到真正的實時,

flush
上個程序中 segment 在檔案系統快取中,會有意外故障檔案丟失,那么,為了保證檔案不會丟失,需要將檔案寫入磁盤,那么檔案從檔案快取寫入磁盤的程序就是 flush,寫入磁盤后,清空 translog,
translog有如下的作用:
- 保證檔案快取中的檔案不丟失
- 系統重啟時,從 translog 中恢復
- 新的 segment 收錄到 commit point 中

merge
segment 會越來越多,那么搜索會越來越慢,需要通過merge程序解決:
- 就是各個小segment檔案,合并成一個大segment檔案
- Segment合并結束,舊的segment檔案會被洗掉
- .liv 檔案維護的洗掉檔案,會通過這個程序進行清除

3.性能優化方案
- filesystem cache:ES的搜索引擎依賴底層的filesystem cache,如果給filesystem cache更多的記憶體,盡量讓記憶體可以容納所有的index segment file索引資料檔案
- 資料預熱:對于那些你覺得比較熱的資料,即經常會有人訪問的資料,最好做一個專門的快取預熱子系統,對于熱資料,每隔一段時間,系統本身就提前訪問一下,讓資料進入filesystem cache里面去,這樣下次訪問的時候,性能會更好一些,
- 冷熱分離:
- 冷資料索引:查詢頻率低,基本無寫入,一般為當天或最近2天以前的資料索引,這種資料可以存盤在機械硬碟HDD中
- 熱資料索引:查詢頻率高,寫入壓力大,一般為當天的資料索引,這種資料可以存盤在SSD中
- document的模型設計:不要在搜索的時候去執行各種復雜的操作,盡量在document模型設計和資料寫入的時候就將復雜操作處理掉
- 分頁性能優化:翻頁翻得越深,每個shard回傳的資料越多,而且協調節點處理的時間越長,此時,要用scroll,scroll會一次性的生成所有資料的快照,然后每次翻頁都是通過移動游標來完成
Reference
- 分布式搜索引擎Elasticsearch(一)
- 分布式搜索引擎Elasticsearch的架構分析
- 深入詳解Elasticsearch
- 「干貨」圖解 Elasticsearch 寫入流程
- 倒排索引與ElasticSearch
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/295353.html
標籤:其他
上一篇:Java面試題
