一、ES 架構
1、ElasticSearch
ElasticSearch是個開源的分布式的搜索引擎,它可以近乎實時的存儲、檢索數據;
Elasticsearch也使用Java開發並使用Lucene作為其核心來實現所有索引和搜索的功能,但是它的目的是通過簡單的RESTful API來隱藏Lucene的復雜性,從而讓全文搜索變得簡單。
ElasticSearch提供javaAPI,使用者可以通過javaAPI調用,但是7.0以后不會提供普通javaAPI,需要使用高級APIrest-high-level調用。
2、Lucene
說到ES就不得不提Lucene,Lucene是一個開放源代碼的全文檢索引擎工具包,它不是一個完整的搜索引擎,而且一個全文搜索引擎的架構。
Lucene之所以比傳統的關系型數據庫檢索的效率高,因為其使用的事倒排索引,這種索引表中的每一項都包括一個屬性值和具有該屬性值的各記錄的地址。
3、elasticsearch的基本概念:
NRT:Near Realtime,近實時,有兩個層面的含義,一是從寫入一條數據到這條數據可以被搜索,有一段非常小的延遲(大約1秒左右),二是基於Elasticsearch的搜索和分析操作,耗時可以達到秒級。
分片(shard):代表索引分片,es可以把一個完整的索引分成多個分片,這樣的好處是可以把一個大的索引拆分成多個,分布到不同的節點上。構成分布式搜索。分片的數量只能在索引創建前指定,並且索引創建后不能更改。ES自動管理和組織分片, 並在必要的時候對分片數據進行再平衡分配, 所以用戶基本上不用擔心分片的處理細節.
副本(replica):索引副本,完全拷貝shard的內容,shard與replica的關系可以是一對多,同一個shard可以有一個或多個replica,並且同一個shard下的replica數據完全一樣,replica作為shard的數據拷貝,承擔以下三個任務:
- shard故障或宕機時,其中一個replica可以升級成shard。
- replica保證數據不丟失(冗余機制),保證高可用。
- replica可以分擔搜索請求,提升整個集群的吞吐量和性能。額外的副本能給帶來更大的容量, 更高的呑吐能力及更強的故障恢復能力。
shard的全稱叫primary shard,replica全稱叫replica shard,primary shard數量在創建索引時指定,后期不能修改,replica shard后期可以修改。默認每個索引的primary shard值為5,replica shard值為1,含義是5個primary shard,5個replica shard,共10個shard。
因此Elasticsearch最小的高可用配置是2台服務器。
Recovery:代表數據恢復或叫數據重新分布,es在有節點加入或退出時會根據機器的負載對索引分片進行重新分配,掛掉的節點重新啟動時也會進行數據恢復。
節點(node): 單個 ElasticSearch 實例. 通常一個節點運行在一個隔離的容器或虛擬機中
索引(index): 索引,具有相同結構的文檔集合,類似於關系型數據庫的數據庫實例(6.0.0版本type廢棄后,索引的概念下降到等同於數據庫表的級別)。一個集群里可以定義多個索引,如客戶信息索引、商品分類索引、商品索引、訂單索引、評論索引等等,分別定義自己的數據結構。
索引命名要求全部使用小寫,建立索引、搜索、更新、刪除操作都需要用到索引名稱。
type:類型,原本是在索引(Index)內進行的邏輯細分,但后來發現企業研發為了增強可閱讀性和可維護性,制訂的規范約束,同一個索引下很少還會再使用type進行邏輯拆分(如同一個索引下既有訂單數據,又有評論數據),因而在6.0.0版本之后,此定義廢棄。
Document:文檔,Elasticsearch最小的數據存儲單元,JSON數據格式,類似於關系型數據庫的表記錄(一行數據),結構定義多樣化,同一個索引下的document,結構盡可能相同。
gateway:-代表es索引的持久化存儲方式,es默認是先把索引放到內存中,當內存滿了的時候再持久化到硬盤。當這個es集群關閉或者重啟時就會從gateway中讀取索引數據。gateway支持多種存儲,默認本地磁盤文件,hadoop 中hdfs 等
discovery.zen:-代表es自動發現同網段節點機制,它先通過廣播需要存在的節點,再通過多播協議進行節點之間通信,同時支持點對點的交互。
-如果集群不同網段節點,禁用自動發現機制 discovery.zen.ping.multicast.enabled:false
設置新節點在啟動時能夠被發現的主節點列表。 discovery.zen.ping.multicast.unicast.hosts:["192.169.1.1","192.169.1.2"]
transport:-代表內部節點或集群與客戶端的交互方式,默認內部使用的TCP協議進行交互。同時支持https(json),merchand,kafka,zeroMq等
settings修改索引庫默認配置
mapping索引庫的索引字段名稱和數據進行定義
如上圖,有集群兩個節點,並使用了默認的分片配置. ES自動把這5個主分片分配到2個節點上, 而它們分別對應的副本則在完全不同的節點上。其中 node1 有某個索引的分片1、2、3和副本分片4、5,node2 有該索引的分片4、5和副本分片1、2、3。
當數據被寫入分片時,它會定期發布到磁盤上的不可變的 Lucene 分段中用於查詢。隨着分段數量的增長,這些分段會定期合並為更大的分段。 此過程稱為合並。 由於所有分段都是不可變的,這意味着所使用的磁盤空間通常會在索引期間波動,因為需要在刪除替換分段之前創建新的合並分段。 合並可能非常耗費資源,特別是在磁盤I / O方面。
分片是 Elasticsearch 集群分發數據的單元。 Elasticsearch 在重新平衡數據時可以移動分片的速度,例如發生故障后,將取決於分片的大小和數量以及網絡和磁盤性能。
注1:避免使用非常大的分片,因為這會對群集從故障中恢復的能力產生負面影響。 對分片的大小沒有固定的限制,但是通常情況下很多場景限制在 50GB 的分片大小以內。
注2:當在ElasticSearch集群中配置好你的索引后, 你要明白在集群運行中你無法調整分片設置. 既便以后你發現需要調整分片數量, 你也只能新建創建並對數據進行重新索引(reindex)(雖然reindex會比較耗時, 但至少能保證你不會停機).
主分片的配置與硬盤分區很類似, 在對一塊空的硬盤空間進行分區時, 會要求用戶先進行數據備份, 然后配置新的分區, 最后把數據寫到新的分區上。
注3:盡可能使用基於時間的索引來管理數據保留期。 根據保留期將數據分組到索引中。 基於時間的索引還可以輕松地隨時間改變主分片和副本的數量,因為可以更改下一個要生成的索引。
節點類型
1. 候選主節點(Master-eligible node)
一個節點啟動后,就會使用Zen Discovery機制去尋找集群中的其他節點,並與之建立連接。集群中會從候選主節點中選舉出一個主節點,主節點負責創建索引、刪除索引、分配分片、追蹤集群中的節點狀態等工作。Elasticsearch中的主節點的工作量相對較輕,用戶的請求可以發往任何一個節點,由該節點負責分發和返回結果,而不需要經過主節點轉發。
正常情況下,集群中的所有節點,應該對主節點的選擇是一致的,即一個集群中只有一個選舉出來的主節點。然而,在某些情況下,比如網絡通信出現問題、主節點因為負載過大停止響應等等,就會導致重新選舉主節點,此時可能會出現集群中有多個主節點的現象,即節點對集群狀態的認知不一致,稱之為腦裂現象。為了盡量避免此種情況的出現,可以通過discovery.zen.minimum_master_nodes來設置最少可工作的候選主節點個數,建議設置為(候選主節點數 / 2) + 1, 比如,當有三個候選主節點時,該配置項的值為(3/2)+1=2,也就是保證集群中有半數以上的候選主節點。
候選主節點的設置方法是設置node.mater為true,默認情況下,node.mater和node.data的值都為true,即該節點既可以做候選主節點也可以做數據節點。由於數據節點承載了數據的操作,負載通常都很高,所以隨着集群的擴大,建議將二者分離,設置專用的候選主節點。當我們設置node.data為false,就將節點設置為專用的候選主節點了。
node.master = true
node.data = false
2. 數據節點(Data node)
數據節點負責數據的存儲和相關具體操作,比如CRUD、搜索、聚合。所以,數據節點對機器配置要求比較高,首先需要有足夠的磁盤空間來存儲數據,其次數據操作對系統CPU、Memory和IO的性能消耗都很大。通常隨着集群的擴大,需要增加更多的數據節點來提高可用性。
前面提到默認情況下節點既可以做候選主節點也可以做數據節點,但是數據節點的負載較重,所以需要考慮將二者分離開,設置專用的數據節點,避免因數據節點負載重導致主節點不響應。
node.master = false
node.data = true
3. 客戶端節點(Client node)
按照官方的介紹,客戶端節點就是既不做候選主節點也不做數據節點的節點,只負責請求的分發、匯總等等,也就是下面要說到的協調節點的角色。這樣的工作,其實任何一個節點都可以完成,單獨增加這樣的節點更多是為了負載均衡。
node.master = false
node.data = false
4. 協調節點(Coordinating node)
協調節點,是一種角色,而不是真實的Elasticsearch的節點,你沒有辦法通過配置項來配置哪個節點為協調節點。集群中的任何節點,都可以充當協調節點的角色。當一個節點A收到用戶的查詢請求后,會把查詢子句分發到其它的節點,然后合並各個節點返回的查詢結果,最后返回一個完整的數據集給用戶。在這個過程中,節點A扮演的就是協調節點的角色。毫無疑問,協調節點會對CPU、Memory要求比較高。
工作原理
以幾個場景介紹一下Elasticsearch的工作原理。
啟動過程
當Elasticsearch的node啟動時,默認使用廣播尋找集群中的其他node,並與之建立連接,如果集群已經存在,其中一個節點角色特殊一些,叫coordinate node(協調者,也叫master節點),負責管理集群node的狀態,有新的node加入時,會更新集群拓撲信息。如果當前集群不存在,那么啟動的node就自己成為coordinate node。
應用程序與集群通信過程
雖然Elasticsearch設置了Coordinate Node用來管理集群,但這種設置對客戶端(應用程序)來說是透明的,客戶端可以請求任何一個它已知的node,如果該node是集群當前的Coordinate,那么它會將請求轉發到相應的Node上進行處理,如果該node不是Coordinate,那么該node會先將請求轉交給Coordinate Node,再由Coordinate進行轉發,搓着各node返回的數據全部交由Coordinate Node進行匯總,最后返回給客戶端。
集群內node有效性檢測
正常工作時,Coordinate Node會定期與拓撲結構中的Node進行通信,檢測實例是否正常工作,如果在指定的時間周期內,Node無響應,那么集群會認為該Node已經宕機。集群會重新進行均衡:
- 重新分配宕機的Node,其他Node中有該Node的replica shard,選出一個replica shard,升級成為primary shard。
- 重新安置新的shard。
- 拓撲更新,分發給該Node的請求重新映射到目前正常的Node上。
分片與集群狀態
分片(Shard),是Elasticsearch中的最小存儲單元。一個索引(Index)中的數據通常會分散存儲在多個分片中,而這些分片可能在同一台機器,也可能分散在多台機器中。這樣做的優勢是有利於水平擴展,解決單台機器磁盤空間和性能有限的問題,試想一下如果有幾十TB的數據都存儲同一台機器,那么存儲空間和訪問時的性能消耗都是問題。
前面提到,中心節點(master)是從一組候選人(master-eligible)中選舉出來的,那設置多少個候選人是合理的?原則是要保證任何時候系統只有一個確定的master節點。考慮到一致性,只有被半數以上候選節點都認可的節點才能成為master節點,否則就會出現多主的情況。只有1個候選節點顯然不能保證高可用;有2個時,半數以上(n/2+1)的個數也是2,任何一個出現故障就無法繼續工作了;有3個時,半數以上的值仍然是2,恰好可以保證master故障或網絡故障時系統可以繼續工作。因此,3個dedicated master-eligible節點是最小配置,也是目前業界標配。
通過API( http://localhost:9200/_cluster/health?pretty )可以查看集群的狀態,通常集群的狀態分為三種:
Red,表示有主分片沒有分配,某些數據不可用。
Yellow,表示主分片都已分配,數據都可用,但是有復制分片沒有分配。
Green,表示主分片和復制分片都已分配,一切正常。
Elasticsearch以REST API形式對外提供服務,數據寫入與查詢都會發送HTTP(S)請求到服務端,由負載均衡將請求分發到集群中的某個節點上(任何非dedicated master-eligible節點)。如下圖所示,節點1收到請求后,會根據相關的元信息將請求分發到shard所在的節點(2和3)上進行處理,處理完成后,節點2和3會將結果返回給節點1,由節點1合並整理后返回給客戶端。這里的節點1扮演着協調者的角色,稱為coordinate節點,任何節點在收到請求后就開始發揮協調者的角色,直到請求結束。在實際使用中,可以根據需要增加一些專用的coordinate節點,用於性能調優。
數據寫入:
1)客戶端選擇一個node發送請求過去,這個node就是coordinating node(協調節點)
2)coordinating node,對document進行路由,將請求轉發給對應的node(有primary shard)
3)實際的node上的primary shard處理請求,然后將數據同步到replica node
4)coordinating node,如果發現primary node和所有replica node都搞定之后,就返回響應結果給客戶端。
Elasticsearch寫數據的底層原理
1)先寫入buffer,在buffer里的時候數據是搜索不到的;同時將數據寫入translog日志文件。
2)如果buffer快滿了,或者到一定時間,就會將buffer數據refresh到一個新的segment file中,但是此時數據不是直接進入segment file的磁盤文件的,而是先進入os cache的。這個過程就是refresh。
每隔1秒鍾,es將buffer中的數據寫入一個新的segment file,每秒鍾會產生一個新的磁盤文件,segment file,這個segment file中就存儲最近1秒內buffer中寫入的數據。
但是如果buffer里面此時沒有數據,那當然不會執行refresh操作咯,每秒創建換一個空的segment file,如果buffer里面有數據,默認1秒鍾執行一次refresh操作,刷入一個新的segment file中。
操作系統里面,磁盤文件其實都有一個東西,叫做os cache,操作系統緩存,就是說數據寫入磁盤文件之前,會先進入os cache,先進入操作系統級別的一個內存緩存中去。
只要buffer中的數據被refresh操作,刷入os cache中,就代表這個數據就可以被搜索到了。
為什么叫es是准實時的?NRT,near real-time,准實時。默認是每隔1秒refresh一次的,所以es是准實時的,因為寫入的數據1秒之后才能被看到。
可以通過es的restful api或者java api,手動執行一次refresh操作,就是手動將buffer中的數據刷入os cache中,讓數據立馬就可以被搜索到。
只要數據被輸入os cache中,buffer就會被清空了,因為不需要保留buffer了,數據在translog里面已經持久化到磁盤去一份了。
數據讀取
Elasticsearch讀取數據的過程
1)客戶端發送請求到任意一個node,成為coordinate node
2)coordinate node對document進行路由,將請求轉發到對應的node,此時會使用round-robin隨機輪詢算法,在primary shard以及其所有replica中隨機選擇一個,讓讀請求負載均衡
3)接收請求的node返回document給coordinate node
4)coordinate node返回document給客戶端
1.寫入document時,每個document會自動分配一個全局唯一的id即doc id,同時也是根據doc id進行hash路由到對應的primary shard上。也可以手動指定doc id,比如用訂單id,用戶id。 2.讀取document時,你可以通過doc id來查詢,然后會根據doc id進行hash,判斷出來當時把doc id分配到了哪個shard上面去,從那個shard去查詢
Elasticsearch搜索數據過程
es最強大的是做全文檢索
1)客戶端發送請求到一個coordinate node
2)協調節點將搜索請求轉發到所有的shard對應的primary shard或replica shard也可以
3)query phase:每個shard將自己的搜索結果(其實就是一些doc id),返回給協調節點,由協調節點進行數據的合並、排序、分頁等操作,產出最終結果
4)fetch phase:接着由協調節點,根據doc id去各個節點上拉取實際的document數據,最終返回給客戶端
搜索的底層原理:倒排索引
三、分片大小如何影響性能
在Elasticsearch中,每個查詢在每個分片的單個線程中執行。 但是,可以並行處理多個分片,對同一分片也可以進行多個查詢和聚合。
這意味着,如果不涉及緩存,則最小查詢延遲將取決於數據、查詢類型以及分片的大小。 查詢大量小的分片將使每個分片的處理速度更快,但是需要按順序排隊和處理更多的任務,它不一定比查詢較少數量的較大分片更快。 如果存在多個並發查詢,則擁有大量小分片也會降低查詢吞吐量。
從查詢性能角度確定最大分片大小的最佳方法是使用實際數據和查詢進行基准測試。 始終以查詢和加載索引的節點在生產中需要處理的內容基准,因為優化單個查詢可能會產生誤導性結果。
大規模以及日益增長的數據場景
對大數據集, 我們非常鼓勵你為索引多分配些分片--當然也要在合理范圍內. 上面講到的每個分片最好不超過30GB的原則依然使用.
不過, 你最好還是能描述出每個節點上只放一個索引分片的必要性. 在開始階段, 一個好的方案是根據你的節點數量按照1.5~3倍的原則來創建分片. 例如,如果你有3個節點, 則推薦你創建的分片數最多不超過9(3x3)個.
隨着數據量的增加,如果你通過集群狀態API發現了問題,或者遭遇了性能退化,則只需要增加額外的節點即可. ES會自動幫你完成分片在不同節點上的分布平衡.
再強調一次, 雖然這里我們暫未涉及副本節點的介紹, 但上面的指導原則依然使用: 是否有必要在每個節點上只分配一個索引的分片. 另外, 如果給每個分片分配1個副本, 你所需的節點數將加倍. 如果需要為每個分片分配2個副本, 則需要3倍的節點數. 更多詳情可以參考基於副本的集群.
四、如何管理分片大小
當使用基於時間的索引時,通常每個索引都與固定的時間段相關聯。 每天的索引非常常見,通常用於保存保留期短的或每日量大的數據。 這些允許以合適的粒度管理保留期,並且可以輕松調整日常基礎量。 具有較長保留期的數據,特別是如果每日的量不能保證使用每天的索引,通常使用每周或每月的索引以保證分片大小。 這減少了隨着時間的推移需要存儲在集群中的索引和分片的數量。
注:如果使用基於時間的索引,這個時間是某個固定的時間段,那么需要根據數據的保留期限和預期的數據量來調整每個索引所覆蓋的時間段,以達到目標分片的大小。也就是說,如果我們要確定最終分片的大小,則需要根據我們的數據保存的期限以及預估預期的數據量來調整我們索引需要按照天還是周還是月的時間來進行評估。
當數據量可以合理預測並且變化緩慢時,具有固定時間間隔的基於時間的索引很有效。 如果索引快速變化,則很難保持統一的目標分片大小。為了能夠更好地處理這種類型的場景,引入了 Rollover and Shrink API (https://www.jianshu.com/writer#/notebooks/27738831/notes/31623194) 。 這些為索引和分片的管理方式增加了很多靈活性,特別是對於基於時間的索引。
Rollover and Shrink API 可以指定應包含的文檔和索引的數量和/或應該向其寫入最大期限的文檔。 一旦超出其中一個標准,Elasticsearch 就可以觸發創建新索引,無需停機即可完成寫入。 可以切換到特定大小的新索引,而不是讓每個索引覆蓋特定的時間段,這使得可以更容易地為所有索引實現均勻的分片大小。如果需要更新數據,在使用此API時,事件的時間戳與其所處的索引之間不再存在明顯的鏈接,這可能會使更新效率大大降低,因為每次更新都需要在搜索之前進行。
注:如果我們有基於時間的不可變數據,其中數據量可能會隨時間發生顯著變化,就可以考慮使用 Rollover API,通過動態更改每個索引所涵蓋的時間段來實現最佳目標分片大小。 這提供了極大的靈活性,並且可以幫助避免在數據量不可預測時具有太大或太小的分片。
Shrink API 允許我們將現有索引縮小為具有較少主分片的新索引。 如果在索引期間需要跨節點均勻擴展分片,但這會導致分片太小,一旦索引不再被索引,此 API 可用於減少主分片的數量。 這將生成更大的分片,更適合長期存儲數據。
如果需要讓每個索引覆蓋特定的時間段,並且希望能夠在大量節點上擴展索引,請考慮使用 Shrink API 在索引不再編入索引時減少主分片的數量。 如果最初配置了太多分片,此 API 還可用於減少分片數量。
參考:https://www.jianshu.com/p/297e13045605
https://blog.csdn.net/zwgdft/java/article/details/54585644
參考:https://blog.csdn.net/zwgdft/java/article/details/83619905
參考:https://www.cnblogs.com/huangying2124/archive/2019/12/05/11986897.html