Elasticsearch(簡稱ES)是一個分佈式、可擴展、實時的搜索與數據分析引擎。ES不只僅只是全文搜索,還支持結構化搜索、數據分析、複雜的語言處理、地理位置和對象間關聯關係等。node
ES的底層依賴Lucene,Lucene能夠說是當下最早進、高性能、全功能的搜索引擎庫。可是Lucene僅僅只是一個庫。爲了充分發揮其功能,你須要使用Java並將Lucene直接集成到應用程序中。更糟糕的是,您可能須要得到信息檢索學位才能瞭解其工做原理,由於Lucene很是複雜——《ElasticSearch官方權威指南》。web
鑑於Lucene如此強大卻難以上手的特色,誕生了ES。ES也是使用Java編寫的,它的內部使用Lucene作索引與搜索,它的目的是隱藏Lucene的複雜性,取而代之的提供一套簡單一致的RESTful API。算法
整體來講,ES具備以下特色:api
一個分佈式的實時文檔存儲引擎,每一個字段均可以被索引與搜索緩存
一個分佈式實時分析搜索引擎,支持各類查詢和聚合操做安全
能勝任上百個服務節點的擴展,並能夠支持PB級別的結構化或者非結構化數據bash
ES的架構很簡單,集羣的HA不須要依賴任務外部組件(例如Zookeeper、HDFS等),master節點的主備依賴於內部自建的選舉算法,經過副本分片的方式實現了數據的備份的同時,也提升了併發查詢的服務器
ES集羣的服務器分爲如下四種角色:網絡
master節點,負責保存和更新集羣的一些元數據信息,以後同步到全部節點,因此每一個節點都須要保存全量的元數據信息:架構
▫集羣的配置信息
▫集羣的節點信息
▫模板template設置
▫索引以及對應的設置、mapping、分詞器和別名
▫索引關聯到的分片以及分配到的節點
datanode:負責數據存儲和查詢
coordinator:
▫路由索引請求
▫聚合搜索結果集
▫分發批量索引請求
ingestor:
▫相似於logstash,對輸入數據進行處理和轉換
如何配置節點類型
一個節點的缺省配置是:主節點+數據節點兩屬性爲一身。對於3-5個節點的小集羣來說,一般讓全部節點存儲數據和具備得到主節點的資格。
專用協調節點(也稱爲client節點或路由節點)從數據節點中消除了聚合/查詢的請求解析和最終階段,隨着集羣寫入以及查詢負載的增大,能夠經過協調節點減輕數據節點的壓力,可讓數據節點更多專一於數據的寫入以及查詢。
選舉策略
若是集羣中存在master,承認該master,加入集羣
若是集羣中不存在master,從具備master資格的節點中選id最小的節點做爲master
選舉時機
集羣啓動:後臺啓動線程去ping集羣中的節點,按照上述策略從具備master資格的節點中選舉出master
現有的master離開集羣:後臺一直有一個線程定時ping master節點,超過必定次數沒有ping成功以後,從新進行master的選舉
選舉流程
避免腦裂
腦裂問題是採用master-slave模式的分佈式集羣廣泛須要關注的問題,腦裂一旦出現,會致使集羣的狀態出現不一致,致使數據錯誤甚至丟失。
ES避免腦裂的策略:過半原則,能夠在ES的集羣配置中添加一下配置,避免腦裂的發生
#一個節點多久ping一次,默認1sdiscovery.zen.fd.ping_interval: 1s##等待ping返回時間,默認30sdiscovery.zen.fd.ping_timeout: 10s##ping超時重試次數,默認3次discovery.zen.fd.ping_retries: 3##選舉時須要的節點鏈接數,N爲具備master資格的節點數量discovery.zen.minimum_master_nodes=N/2+1複製代碼
注意問題
配置文件中加入上述避免腦裂的配置,對於網絡波動比較大的集羣來講,增長ping的時間和ping的次數,必定程度上能夠增長集羣的穩定性
動態的字段field可能致使元數據暴漲,新增字段mapping映射須要更新mater節點上維護的字段映射信息,master修改了映射信息以後再同步到集羣中全部的節點,這個過程當中數據的寫入是阻塞的。因此建議關閉自動mapping,沒有預先定義的字段mapping會寫入失敗
經過定時任務在集羣寫入的低峯期,將索引以及mapping映射提早建立好
ES集羣是分佈式的,數據分佈到集羣的不一樣機器上,對於ES中的一個索引來講,ES經過分片的方式實現數據的分佈式和負載均衡。建立索引的時候,須要指定分片的數量,分片會均勻的分佈到集羣的機器中。分片的數量是須要建立索引的時候就須要設置的,並且設置以後不能更改,雖然ES提供了相應的api來縮減和擴增分片,可是代價是很高的,須要重建整個索引。
考慮到併發響應以及後續擴展節點的能力,分片的數量不能太少,假如你只有一個分片,隨着索引數據量的增大,後續進行了節點的擴充,可是因爲一個分片只能分佈在一臺機器上,因此集羣擴容對於該索引來講沒有意義了。
可是分片數量也不能太多,每一個分片都至關於一個獨立的lucene引擎,太多的分片意味着集羣中須要管理的元數據信息增多,master節點有可能成爲瓶頸;同時集羣中的小文件會增多,內存以及文件句柄的佔用量會增大,查詢速度也會變慢。
ES經過副本分片的方式,保證集羣數據的高可用,同時增長集羣併發處理查詢請求的能力,相應的,在數據寫入階段會增大集羣的寫入壓力。
數據寫入的過程當中,首先被路由到主分片,寫入成功以後,將數據發送到副本分片,爲了保證數據不丟失,最好保證至少一個副本分片寫入成功之後才返回客戶端成功。
相關配置
5.0以前經過consistency來設置
consistency參數的值能夠設爲 :
one :只要主分片狀態ok就容許執行寫操做
all:必需要主分片和全部副本分片的狀態沒問題才容許執行寫操做
quorum:默認值爲quorum,即大多數的分片副本狀態沒問題就容許執行寫操做,副本分片數量計算方式爲int( (primary + number_of_replicas) / 2 ) + 1
5.0以後經過wait_for_active_shards參數設置
索引時增長參數:?wait_for_active_shards=3
給索引增長配置:index.write.wait_for_active_shards=3
幾個概念:
內存buffer
translog
文件系統緩衝區
refresh
segment(段)
commit
flush
translog
寫入ES的數據首先會被寫入translog文件,該文件持久化到磁盤,保證服務器宕機的時候數據不會丟失,因爲順序寫磁盤,速度也會很快。
同步寫入:每次寫入請求執行的時候,translog在fsync到磁盤以後,纔會給客戶端返回成功
異步寫入:寫入請求緩存在內存中,每通過固定時間以後纔會fsync到磁盤,寫入量很大,對於數據的完整性要求又不是很是嚴格的狀況下,能夠開啓異步寫入
refresh
通過固定的時間,或者手動觸發以後,將內存中的數據構建索引生成segment,寫入文件系統緩衝區
commit/flush
超過固定的時間,或者translog文件過大以後,觸發flush操做:
內存的buffer被清空,至關於進行一次refresh
文件系統緩衝區中全部segment刷寫到磁盤
將一個包含全部段列表的新的提交點寫入磁盤
啓動或從新打開一個索引的過程當中使用這個提交點來判斷哪些segment隸屬於當前分片
刪除舊的translog,開啓新的translog
merge
上面提到,每次refresh的時候,都會在文件系統緩衝區中生成一個segment,後續flush觸發的時候持久化到磁盤。因此,隨着數據的寫入,尤爲是refresh的時間設置的很短的時候,磁盤中會生成愈來愈多的segment:
segment數目太多會帶來較大的麻煩。 每個segment都會消耗文件句柄、內存和cpu運行週期。
更重要的是,每一個搜索請求都必須輪流檢查每一個segment,因此segment越多,搜索也就越慢。
merge的過程大體描述以下:
磁盤上兩個小segment:A和B,內存中又生成了一個小segment:C
A,B被讀取到內存中,與內存中的C進行merge,生成了新的更大的segment:D
觸發commit操做,D被fsync到磁盤
建立新的提交點,刪除A和B,新增D
刪除磁盤中的A和B
segment的不可變性的好處
segment的讀寫不須要加鎖
常駐文件系統緩存(堆外內存)
查詢的filter緩存能夠常駐內存(堆內存)
刪除
磁盤上的每一個segment都有一個.del文件與它相關聯。當發送刪除請求時,該文檔未被真正刪除,而是在.del文件中標記爲已刪除。此文檔可能仍然能被搜索到,但會從結果中過濾掉。當segment合併時,在.del文件中標記爲已刪除的文檔不會被包括在新的segment中,也就是說merge的時候會真正刪除被刪除的文檔。
更新
建立新文檔時,Elasticsearch將爲該文檔分配一個版本號。對文檔的每次更改都會產生一個新的版本號。當執行更新時,舊版本在.del文件中被標記爲已刪除,而且新版本在新的segment中寫入索引。舊版本可能仍然與搜索查詢匹配,可是從結果中將其過濾掉。
經過添加版本號的樂觀鎖機制保證高併發的時候,數據更新不會出現線程安全的問題,避免數據更新被覆蓋之類的異常出現。
使用內部版本號:刪除或者更新數據的時候,攜帶_version參數,若是文檔的最新版本不是這個版本號,那麼操做會失敗,這個版本號是ES內部自動生成的,每次操做以後都會遞增一。
PUT /website/blog/1?version=1 { "title": "My first blog entry", "text": "Starting to get the hang of this..."}複製代碼
使用外部版本號:ES默認採用遞增的整數做爲版本號,也能夠經過外部自定義整數(long類型)做爲版本號,例如時間戳。經過添加參數version_type=external,可使用自定義版本號。內部版本號使用的時候,更新或者刪除操做須要攜帶ES索引當前最新的版本號,匹配上了才能成功操做。可是外部版本號使用的時候,能夠將版本號更新爲指定的值。
PUT /website/blog/2?version=5&version_type=external{ "title": "My first external blog entry", "text": "Starting to get the hang of this..."}複製代碼
因爲文章詳細,篇幅較長,爲了更好的閱讀體驗,本文將分爲上下兩個部分。下半部分請查看今日推送的《技術專欄丨從原理到應用,Elasticsearch詳解(下)》