本篇主要介紹Elasticsearch的數據索引時的分片機制,集羣發現機制,primary shard與replica shard是如何分工合做的,如何對集羣擴容,以及集羣的容錯機制。node
前面基本概念一節中,咱們有提到創建索引時,會自動將數據拆分到多個分片(shard)中,默認數量是5,這個就是索引數據分片機制。咱們在往Elasticsearch集羣插入數據,並無關心過數據最終落地到哪一個shard上,這個過程對客戶端來說是透明的。算法
document要存儲到Elasticsearch中,還要知足後續搜索的需求,路由到分片位置的算法確定不能是隨機的,要否則搜索就無法找了,路由的過程有一個公式:服務器
shard = hash(routing) % number_of_primary_shards
網絡
routing值默認是document的ID值,也能夠自行指定。先對routing信息求hash值,而後將hash結果對primaryshard的數量求模,好比說primaryshard是5,那麼結果確定落在[0,4]區間內,這個結果值就是該document的分片位置,如示意圖所示:架構
這個求模公式間接的解釋了爲何了索引建立時指定了primary shard的值,後續就不讓改了,模數改了,以前路由的document再執行該公式時,值就可能跟改以前獲得的值不一致,這樣document就找不到了,如示意圖所示:併發
在同一個網絡環境下,只要啓動一個Elasticsearch實例,而且cluster.name配置得同樣,這個Elasticsearch實例就會自動加入到集羣當中,這個是如何實現的?負載均衡
這個依賴於Elasticsearch的自動發現機制Zen,在elasticsearch.yml配置文件中,有一行discovery.zen.ping.unicast.hosts: ["192.168.17.137"]
表示單播發現方式,當該Elasticsearch實例啓動時,會向192.168.17.137主機發送請求,並獲得整個集羣裏全部節點的狀態,而後去聯繫master節點,並加入集羣。elasticsearch
摘抄了獲取配置信息,註冊discovery請求的部分源碼以下:org.elasticsearch.discovery.zen.ZenDiscovery啓動時的構造器,會調用org.elasticsearch.discovery.zen.UnicastZenPing的構造器,其中UnicastZenPing的構造方式內會加載discovery.zen.ping.unicast.hosts配置項,併發送"internal:discovery/zen/unicast"請求(代碼有刪節):分佈式
public UnicastZenPing(Settings settings, ThreadPool threadPool, TransportService transportService,
UnicastHostsProvider unicastHostsProvider, PingContextProvider contextProvider) {
super(settings);
final int concurrentConnects = DISCOVERY_ZEN_PING_UNICAST_CONCURRENT_CONNECTS_SETTING.get(settings);
if (DISCOVERY_ZEN_PING_UNICAST_HOSTS_SETTING.exists(settings)) {
configuredHosts = DISCOVERY_ZEN_PING_UNICAST_HOSTS_SETTING.get(settings);
// we only limit to 1 addresses, makes no sense to ping 100 ports
limitPortCounts = LIMIT_FOREIGN_PORTS_COUNT;
} else {
// if unicast hosts are not specified, fill with simple defaults on the local machine
configuredHosts = transportService.getLocalAddresses();
limitPortCounts = LIMIT_LOCAL_PORTS_COUNT;
}
resolveTimeout = DISCOVERY_ZEN_PING_UNICAST_HOSTS_RESOLVE_TIMEOUT.get(settings);
transportService.registerRequestHandler(ACTION_NAME, ThreadPool.Names.SAME, UnicastPingRequest::new,
new UnicastPingRequestHandler());
}複製代碼
一個index的數據,是拆分存儲在多個shard當中,咱們能夠在Elasticsearch的數據目錄裏查看一下索引的存儲結構(Elasticsearch服務器上導出的樹狀目錄結構):ide
.
└── nodes
└── 0
├── indices
│ ├── 48G_CgE7TiWomlYsyQW1NQ #索引location的UUID
│ │ ├── 0 #primary shard,從0-4共5個
│ │ │ ├── index
│ │ │ │ ├── segments_3
│ │ │ │ └── write.lock
│ │ │ ├── _state
│ │ │ │ └── state-2.st
│ │ │ └── translog
│ │ │ ├── translog-2.ckp
│ │ │ ├── translog-2.tlog
│ │ │ ├── translog-3.ckp
│ │ │ ├── translog-3.tlog
│ │ │ ├── translog-4.tlog
│ │ │ └── translog.ckp
│ │ ├── 1
│ │ │ ├── index
│ │ │ │ ├── segments_3
│ │ │ │ └── write.lock
│ │ │ ├── _state
│ │ │ │ └── state-2.st
│ │ │ └── translog
│ │ │ ├── translog-2.ckp
│ │ │ ├── translog-2.tlog
│ │ │ ├── translog-3.ckp
│ │ │ ├── translog-3.tlog
│ │ │ ├── translog-4.tlog
│ │ │ └── translog.ckp
│ │ ├── 2
│ │ │ ├── index
│ │ │ │ ├── _1.cfe
│ │ │ │ ├── _1.cfs
│ │ │ │ ├── _1.si
│ │ │ │ ├── segments_7
│ │ │ │ └── write.lock
│ │ │ ├── _state
│ │ │ │ └── state-2.st
│ │ │ └── translog
│ │ │ ├── translog-4.ckp
│ │ │ ├── translog-4.tlog
│ │ │ ├── translog-5.ckp
│ │ │ ├── translog-5.tlog
│ │ │ ├── translog-6.tlog
│ │ │ └── translog.ckp
│ │ ├── 3
│ │ │ ├── index
│ │ │ │ ├── _1.cfe
│ │ │ │ ├── _1.cfs
│ │ │ │ ├── _1.si
│ │ │ │ ├── segments_7
│ │ │ │ └── write.lock
│ │ │ ├── _state
│ │ │ │ └── state-2.st
│ │ │ └── translog
│ │ │ ├── translog-4.ckp
│ │ │ ├── translog-4.tlog
│ │ │ ├── translog-5.ckp
│ │ │ ├── translog-5.tlog
│ │ │ ├── translog-6.tlog
│ │ │ └── translog.ckp
│ │ ├── 4
│ │ │ ├── index
│ │ │ │ ├── _0.cfe
│ │ │ │ ├── _0.cfs
│ │ │ │ ├── _0.si
│ │ │ │ ├── segments_5
│ │ │ │ └── write.lock
│ │ │ ├── _state
│ │ │ │ └── state-2.st
│ │ │ └── translog
│ │ │ ├── translog-3.ckp
│ │ │ ├── translog-3.tlog
│ │ │ ├── translog-4.ckp
│ │ │ ├── translog-4.tlog
│ │ │ ├── translog-5.tlog
│ │ │ └── translog.ckp
│ │ └── _state
│ │ └── state-16.st
├── node.lock
└── _state
├── global-88.st
└── node-22.st
複製代碼
如上目錄結構所示,展現了location索引(UUID爲48G_CgE7TiWomlYsyQW1NQ)的存儲信息,共5個primary shard,編號從0-4。
primary shard與replica shard,還有其餘幾點特性:
擴容分爲垂直擴容和水平擴容兩種,垂直擴容指增長單臺服務器的CPU、內存大小,磁盤容量,簡單來說就是換更強大的服務器;水平擴容就是增長機器數量,經過集羣化部署與分佈式的技術手段,也能構建出強大的計算和存儲能力。
兩者簡單對比:
Elastisearch很是適合用水平擴容方案,能勝任上百個節點,支撐PB級別的數據規模,而且擴容操做後,每增長新的節點會觸發索引分片的從新分配。
舉個例子,假定Elasticsearch有2個節點,primary shard設置爲3,replica shard設置爲1,這樣1個索引就有3個primary shard,3個replica shard,P表示primary shard,R表示replica shard,分佈示例圖以下:
當新加入一個node-3時,觸發node-1和node-2的shard進行從新分配,假定P0和R1兩個shard移到node-3當中,如圖所示:
重分配完成後,此時集羣的示例以下:
最後補充兩點:
假定Elasticsearch集羣只有一個node,primary shard設置爲3,replica shard設置爲1,這樣1個索引就應該有3個primary shard,3個replica shard,但primary shard不能與其replica shard放在一個node裏,致使replica shard沒法分配,這樣集羣的status爲yellow,示例圖以下:
集羣能夠正常工做,一旦出現node宕機,數據所有丟失,而且集羣不可用。
結論:單node環境容錯性爲0.
primary shard與replica shard的設置與上文相同,此時Elasticsearch集羣只有2個node,shard分佈以下圖所示:
若是其中一臺宕機,如node-2宕機,如圖所示:
此時node-1節點的R2(replica shard)會升爲P2(primary shard),此時集羣還能正經常使用,數據未丟失。
結論:雙node環境容錯性爲1。
咱們先按primary shard爲3,replica shard爲1進行容錯性計算。此時每臺node存放2個shard,若是一臺宕機,此時另外2臺確定還有完整的數據,若是兩臺宕機,剩下的那臺就只有2/3的數據,數據丟失1/3,容錯性爲1臺。若是是這樣設置,那3臺的容錯性和2臺的容錯性同樣,就存在資源浪費的狀況。
那怎麼樣提高容錯性呢?把replica shard的值改爲2,這樣每臺node存放3個shard,以下圖所示:
若是有2臺宕機,就剩下node-2,此時集羣的數據仍是完整的,replica會升成primary shard繼續提供服務,以下圖所示:
結論:3臺node環境容錯性最大能夠是2。
根據上面3個場景,咱們能夠知道,若是shard總數是6個(包含primary shard 和replica shard),那麼node數量上限也爲6,即每臺node存儲1個shard,這個數據即爲擴容極限,若是要突破極限,能夠經過增大replica的值來實現,這樣有更多的replica shard去分擔查詢請求,佔用更多的節點,整個集羣的CPU、IO、Memory資源更多,總體吞吐量也越高。
固然這個replica也不是越大越好,冗餘存儲佔用磁盤資源,replica越大,集羣內有效數據的磁盤利用率就越低。以3臺node爲例,想要達到容錯性,磁盤利用率的最佳值,replica=2是最適宜的。
實際生產中,能夠根據數據量,併發數等實際需求,在建立索引時合理設置primary shard的數量,後期優化時,再調整replica shard的值,這個須要反覆驗證,不斷的演算調整,最終讓生產Elasticsearch集羣的吞吐量達到一個最佳值。
Elasticsearch集羣中,全部的node都是對等的角色,全部的node都能接收請求,而且能自動轉請求到相應的節點上(數據路由),最後能將其餘節點處理的數據進行響應收集,返回給客戶端。在集羣中,也存在一個master節點,它的職責多一些,須要管理與維護集羣的元數據,索引的建立與刪除和節點的增長和刪除,它都會收到相應的請求,而後進行相應的數據維護。master node在承擔索引、搜索請求時,與其餘node一塊兒分攤,並不承擔全部的請求,於是不存在單點故障這個問題。
咱們假設一下集羣有3臺node,其中node-1宕機的過程,若是node-1是master node,關鍵步驟以下:
本篇針對Elasticsearch的一些內部原理進行了簡單的介紹,這些原理針對Elasticsearch的使用者是透明的,爲了增長可閱讀性,自行增長一些講解的原理圖,如有不詳盡之處或錯誤之處請指正,謝謝。
專一Java高併發、分佈式架構,更多技術乾貨分享與心得,請關注公衆號:Java架構社區