面對數(shù)據(jù)量以億級的互聯(lián)網(wǎng)應(yīng)用,為了更好的用戶體驗(yàn)顷牌,不僅要把界面做的美觀剪芍,還要響應(yīng)超快,而數(shù)據(jù)庫支持億級量是很困難的窟蓝,當(dāng)數(shù)據(jù)量達(dá)到百萬以上就很麻煩了罪裹,需要優(yōu)化再優(yōu)化等,若用內(nèi)存來處理億級數(shù)據(jù)量就很容易了运挫。
Hazelcast是什么
“分布式”状共、“集群服務(wù)”、“網(wǎng)格式內(nèi)存數(shù)據(jù)”谁帕、“分布式緩存“峡继、“彈性可伸縮服務(wù)”——這些牛逼閃閃的名詞拿到哪都是ITer裝逼的不二之選。在Javaer的世界匈挖,有這樣一個(gè)開源項(xiàng)目碾牌,只需要引入一個(gè)jar包、只需簡單的配置和編碼即可實(shí)現(xiàn)以上高端技能儡循,他就是 Hazelcast舶吗。
下面來看看它的特點(diǎn):
自治集群(無中心化)
Hazelcast 沒有任何中心節(jié)點(diǎn)(文中的節(jié)點(diǎn)可以理解為運(yùn)行在任意服務(wù)器的獨(dú)立jvm,下同)择膝,或者說Hazelcast 不需要特別指定一個(gè)中心節(jié)點(diǎn)誓琼。在運(yùn)行的過程中,它自己選定集群中的某個(gè)節(jié)點(diǎn)作為中心點(diǎn)來管理所有的節(jié)點(diǎn)。
數(shù)據(jù)按應(yīng)用分布式存儲
Hazelcast 的數(shù)據(jù)是分布式存儲的腹侣。他會將數(shù)據(jù)盡量存儲在需要使用該項(xiàng)數(shù)據(jù)的節(jié)點(diǎn)上呵扛,以實(shí)現(xiàn)數(shù)據(jù)去中心化的目的。在傳統(tǒng)的數(shù)據(jù)存儲模型中(MySql筐带、MongDB今穿、Redis 等等)數(shù)據(jù)都是獨(dú)立于應(yīng)用單獨(dú)存放,當(dāng)需要提升數(shù)據(jù)庫的性能時(shí)伦籍,需要不斷加固單個(gè)數(shù)據(jù)庫應(yīng)用的性能蓝晒。即使是現(xiàn)在大量的數(shù)據(jù)庫支持集群模式或讀寫分離,但是基本思路都是某幾個(gè)庫支持寫入數(shù)據(jù)帖鸦,其他的庫不斷的拷貝更新數(shù)據(jù)副本斥滤。這樣做的壞處一是會產(chǎn)生大量臟讀的問題芜赌,二是消耗大量的資源來傳遞數(shù)據(jù)——從數(shù)據(jù)源頻繁讀寫數(shù)據(jù)會耗費(fèi)額外資源杆故,當(dāng)數(shù)據(jù)量增長或創(chuàng)建的主從服務(wù)越來越多時(shí)舱沧,這個(gè)消耗呈指數(shù)級增長。
使用 Hazelcast 可以有效的解決數(shù)據(jù)中心化問題攻锰。他將數(shù)據(jù)分散的存儲在每個(gè)節(jié)點(diǎn)中晾嘶,節(jié)點(diǎn)越多越分散。每個(gè)節(jié)點(diǎn)都有各自的應(yīng)用服務(wù)娶吞,而Hazelcast集群會根據(jù)每個(gè)應(yīng)用的數(shù)據(jù)使用情況分散存儲這些數(shù)據(jù)垒迂,在應(yīng)用過程中數(shù)據(jù)會盡量“靠近”應(yīng)用存放。這些在集群中的數(shù)據(jù)共享整個(gè)集群的存儲空間和計(jì)算資源妒蛇。
抗單點(diǎn)故障
集群中的節(jié)點(diǎn)是無中心化的机断,每個(gè)節(jié)點(diǎn)都有可能隨時(shí)退出或隨時(shí)進(jìn)入。因此绣夺,在集群中存儲的數(shù)據(jù)都會有一個(gè)備份(可以配置備份的個(gè)數(shù)吏奸,也可以關(guān)閉數(shù)據(jù)備份)。這樣的方式有點(diǎn)類似于 hadoop陶耍,某項(xiàng)數(shù)據(jù)存放在一個(gè)節(jié)點(diǎn)時(shí)奋蔚,在其他節(jié)點(diǎn)必定有至少一個(gè)備份存在。當(dāng)某個(gè)節(jié)點(diǎn)退出時(shí)物臂,節(jié)點(diǎn)上存放的數(shù)據(jù)會由備份數(shù)據(jù)替代旺拉,而集群會重新創(chuàng)建新的備份數(shù)據(jù)产上。
簡易性
所有的 Hazelcast 功能只需引用一個(gè)jar包棵磷,除此之外,他不依賴任何第三方包晋涣。因此可以非常便捷高效的將其嵌入到各種應(yīng)用服務(wù)器中仪媒,而不必?fù)?dān)心帶來額外的問題(jar包沖突、類型沖突等等)。他僅僅提供一系列分布式功能算吩,而不需要綁定任何框架來使用留凭,因此適用于任何場景。
緩存模式
在hazelcast的官方文檔中偎巢,提到了其支持read-through蔼夜,write-through與write-behind三種模式.
- Read-throug
當(dāng)應(yīng)用系統(tǒng)向緩存系統(tǒng)請求數(shù)據(jù)時(shí)(例如使用key=x向緩存請求數(shù)據(jù));如果緩存中并沒有對應(yīng)的數(shù)據(jù)存在(key=x的value不存在)压昼,緩存系統(tǒng)將向底層數(shù)據(jù)源的讀取數(shù)據(jù)求冷。如果數(shù)據(jù)在緩存中存在(命中key=x),則直接返回緩存中存在的數(shù)據(jù)窍霞。 - Write-Through
當(dāng)應(yīng)用系統(tǒng)對緩存中的數(shù)據(jù)進(jìn)行更新時(shí)(例如調(diào)用put方法更新或添加條目)匠题,緩存系統(tǒng)會同步更新緩存數(shù)據(jù)和底層數(shù)據(jù)源。 - Write-Behind
當(dāng)應(yīng)用系統(tǒng)對緩存中的數(shù)據(jù)進(jìn)行更新時(shí)(例如調(diào)用put方法更新或添加條目)但金,緩存系統(tǒng)會在指定的時(shí)間后向底層數(shù)據(jù)源更新數(shù)據(jù)韭山。
實(shí)例剖析
前面說了那么多概念,必須要來一點(diǎn)干貨了冷溃。下面是一個(gè)使用 Hazelcast 的極簡例子钱磅。文中的所有代碼都在github上:https://github.com/chkui/hazelcast-demo。
首先引入Hazelcast的jar包似枕。
Maven(pom.xml):
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>${hazelcast.vertsion}</version>
</dependency>
先創(chuàng)一個(gè)建 Hazelcast 節(jié)點(diǎn):
//org.palm.hazelcast.getstart.HazelcastGetStartServerMaster
public class HazelcastGetStartServerMaster {
public static void main(String[] args) {
// 創(chuàng)建一個(gè) hazelcastInstance實(shí)例
HazelcastInstance instance = Hazelcast.newHazelcastInstance();
// 創(chuàng)建集群Map
Map<Integer, String> clusterMap = instance.getMap("MyMap");
clusterMap.put(1, "Hello hazelcast map!");
// 創(chuàng)建集群Queue
Queue<String> clusterQueue = instance.getQueue("MyQueue");
clusterQueue.offer("Hello hazelcast!");
clusterQueue.offer("Hello hazelcast queue!");
}
}
上面的代碼使用 Hazelcast 實(shí)例創(chuàng)建了一個(gè)節(jié)點(diǎn)续搀。然后通過這個(gè)實(shí)例創(chuàng)建了一個(gè)分布式的Map和分布式的Queue,并向這些數(shù)據(jù)結(jié)構(gòu)中添加了數(shù)據(jù)菠净。運(yùn)行這個(gè)main方法禁舷,會在console看到以下內(nèi)容:
Members [1] {
Member [192.168.1.103]:5701 this
}
隨后再創(chuàng)建另外一個(gè)節(jié)點(diǎn):
// org.palm.hazelcast.getstart.HazelcastGetStartServerSlave
public class HazelcastGetStartServerSlave {
public static void main(String[] args) {
//創(chuàng)建一個(gè) hazelcastInstance實(shí)例
HazelcastInstance instance = Hazelcast.newHazelcastInstance();
Map<Integer, String> clusterMap = instance.getMap("MyMap");
Queue<String> clusterQueue = instance.getQueue("MyQueue");
System.out.println("Map Value:" + clusterMap.get(1));
System.out.println("Queue Size :" + clusterQueue.size());
System.out.println("Queue Value 1:" + clusterQueue.poll());
System.out.println("Queue Value 2:" + clusterQueue.poll());
System.out.println("Queue Size :" + clusterQueue.size());
}
}
該節(jié)點(diǎn)的作用是從Map、Queue中讀取數(shù)據(jù)并輸出毅往。運(yùn)行會看到以下輸出
Members [2] {
Member [192.168.1.103]:5701
Member [192.168.1.103]:5702 this
}
八月 06, 2016 11:33:29 下午 com.hazelcast.core.LifecycleService
信息: [192.168.1.103]:5702 [dev] [3.6.2] Address[192.168.1.103]:5702 is STARTED
Map Value:Hello hazelcast map!
Queue Size :2
Queue Value 1:Hello hazelcast!
Queue Value 2:Hello hazelcast queue!
Queue Size :0
至此牵咙,2個(gè)節(jié)點(diǎn)的集群創(chuàng)建完畢。第一個(gè)節(jié)點(diǎn)向map實(shí)例添加了{(lán)key:1,value:"Hello hazelcast map!"}攀唯,向queue實(shí)例添加[“Hello hazelcast!”,“Hello hazelcast queue!”]洁桌,第二個(gè)節(jié)點(diǎn)讀取并打印這些數(shù)據(jù)。
除了直接使用Hazelcast服務(wù)來組建集群侯嘀,Hazelcast還提供了區(qū)別于服務(wù)端的客戶端應(yīng)用包另凌。客戶端與服務(wù)端最大的不同是:他不會存儲數(shù)據(jù)也不能修改集群中的數(shù)據(jù)戒幔。目前客戶端有C++吠谢、.Net、Java多種版本诗茎。
使用客戶端首先要引入客戶端jar包工坊。
Maven(pom.xml):
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-client</artifactId>
<version>${hazelcast.version}</version>
</dependency>
創(chuàng)建一個(gè)client節(jié)點(diǎn)。
public class HazelcastGetStartClient {
public static void main(String[] args) {
ClientConfig clientConfig = new ClientConfig();
HazelcastInstance instance = HazelcastClient.newHazelcastClient(clientConfig);
Map<Integer, String> clusterMap = instance.getMap("MyMap");
Queue<String> clusterQueue = instance.getQueue("MyQueue");
System.out.println("Map Value:" + clusterMap.get(1));
System.out.println("Queue Size :" + clusterQueue.size());
System.out.println("Queue Value 1:" + clusterQueue.poll());
System.out.println("Queue Value 2:" + clusterQueue.poll());
System.out.println("Queue Size :" + clusterQueue.size());
}
}
然后先啟動(dòng) HazelcastGetStartServerMaster::main,再啟動(dòng) HazelcastGetStartClient::main王污“粘裕可以看到客戶端輸出:
Members [1] {
Member [192.168.197.54]:5701
}
八月 08, 2016 10:54:22 上午 com.hazelcast.core.LifecycleService
信息: HazelcastClient[hz.client_0_dev][3.6.2] is CLIENT_CONNECTED
Map Value:Hello hazelcast map!
Queue Size :2
Queue Value 1:Hello hazelcast!
Queue Value 2:Hello hazelcast queue!
Queue Size :0
至此,客戶端功能也創(chuàng)建完畢 昭齐∧蛘校可以看到客戶端的console輸出內(nèi)容比服務(wù)端少了很多,這是因?yàn)榭蛻舳瞬槐爻休d服務(wù)端的數(shù)據(jù)處理功能阱驾,也不必維護(hù)各種節(jié)點(diǎn)信息泊业。
例子運(yùn)行解析
下面我們根據(jù)console的輸出來看看 Hazelcast 啟動(dòng)時(shí)到底干了什么事。(下面的輸出因環(huán)境或IDE不同啊易,可能會有差異)
class: com.hazelcast.config.XmlConfigLocator
info: Loading 'hazelcast-default.xml' from classpath.
這里輸出的內(nèi)容表示Hazelcast啟動(dòng)時(shí)加載的配置文件吁伺。如果用戶沒有提供有效的配置文件,Hazelcast會使用默認(rèn)配置文件租谈。后續(xù)的文章會詳細(xì)說明 Hazelcast 的配置篮奄。
class: com.hazelcast.instance.DefaultAddressPicker
info: Prefer IPv4 stack is true.
class: com.hazelcast.instance.DefaultAddressPicker
info: Picked Address[192.168.197.54]:5701, using socket ServerSocket[addr=/0:0:0:0:0:0:0:0,localport=5701], bind any local is true
這一段輸出說明了當(dāng)前 Hazelcast 的網(wǎng)絡(luò)環(huán)境。首先是檢測IPv4可用且檢查到當(dāng)前的IPv4地址是192.168.197.54割去。然后使用IPv6啟用socket窟却。在某些無法使用IPv6的環(huán)境上,需要強(qiáng)制指定使用IPv4呻逆,增加jvm啟動(dòng)參數(shù):-Djava.net.preferIPv4Stack=true 即可夸赫。
class: com.hazelcast.system
info: Hazelcast 3.6.2 (20160405 - 0f88699) starting at Address[192.168.197.54]:5701
class: com.hazelcast.system
info: [192.168.197.54]:5701 [dev] [3.6.2] Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
這一段輸出說明了當(dāng)前實(shí)例的初始化端口號是5701。Hazelcast 默認(rèn)使用5701端口咖城。如果發(fā)現(xiàn)該端口被占用茬腿,會+1查看5702是否可用,如果還是不能用會繼續(xù)向后探查直到5800宜雀。Hazelcast 默認(rèn)使用5700到5800的端口切平,如果都無法使用會拋出啟動(dòng)異常。
class: com.hazelcast.system
info: [192.168.197.54]:5701 [dev] [3.6.2] Configured Hazelcast Serialization version : 1
class: com.hazelcast.spi.OperationService
info: [192.168.197.54]:5701 [dev] [3.6.2] Backpressure is disabled
class: com.hazelcast.spi.impl.operationexecutor.classic.ClassicOperationExecutor
info: [192.168.197.54]:5701 [dev] [3.6.2] Starting with 2 generic operation threads and 4 partition operation threads.
這一段說明了數(shù)據(jù)的序列化方式和啟用的線程辐董。Hazelcast 在節(jié)點(diǎn)間傳遞數(shù)據(jù)有2種序列化方式悴品,在后續(xù)的文章中國會詳細(xì)介紹。Hazelcast 會控制多個(gè)線程執(zhí)行不同的工作简烘,有負(fù)責(zé)維持節(jié)點(diǎn)連接的苔严、有負(fù)責(zé)數(shù)據(jù)分區(qū)管理的。
class: com.hazelcast.instance.Node
info: [192.168.197.54]:5701 [dev] [3.6.2] Creating MulticastJoiner
class: com.hazelcast.core.LifecycleService
info: [192.168.197.54]:5701 [dev] [3.6.2] Address[192.168.197.54]:5701 is STARTING
class: com.hazelcast.nio.tcp.nonblocking.NonBlockingIOThreadingModel
info: [192.168.197.54]:5701 [dev] [3.6.2] TcpIpConnectionManager configured with Non Blocking IO-threading model: 3 input threads and 3 output threads
class: com.hazelcast.cluster.impl.MulticastJoiner
info: [192.168.197.54]:5701 [dev] [3.6.2]
上面這一段輸出中孤澎,Creating MulticastJoiner表示使用組播協(xié)議來組建集群届氢。還創(chuàng)建了6個(gè)用于維護(hù)非擁塞信息輸出\輸出。
Members [1] {
Member [192.168.197.54]:5701
Member [192.168.197.54]:5702 this
}
class: com.hazelcast.core.LifecycleService
info: [192.168.197.54]:5701 [dev] [3.6.2] Address[192.168.197.54]:5701 is STARTED
class: com.hazelcast.partition.InternalPartitionService
info: [192.168.197.54]:5701 [dev] [3.6.2] Initializing cluster partition table arrangement...
Members[2]表示當(dāng)前集群只有2個(gè)節(jié)點(diǎn)亥至。2個(gè)節(jié)點(diǎn)都在ip為192.168.197.54的這臺設(shè)備上悼沈,2個(gè)節(jié)點(diǎn)分別占據(jù)了5701端口和5702端口。端口后面的this說明這是當(dāng)前節(jié)點(diǎn)姐扮,而未標(biāo)記this的是其他接入集群的節(jié)點(diǎn)絮供。最后InternalPartitionService輸出的信息表示集群初始化了“數(shù)據(jù)分片”,后面會介紹“數(shù)據(jù)分片”的概念和原理茶敏。
上面就是Hazelcast在默認(rèn)情況下執(zhí)行的啟動(dòng)過程壤靶,可以看出在初始化的過程中我們可以有針對性的修改一些Hazelcast的行為:
- 使用默認(rèn)配置文檔 hazelcast-default.xml 來啟動(dòng)集群。因此我們可以自定義這個(gè)配置文件來影響Hazelcast 的行為惊搏。
- 啟用IPv4或IPv6來建立集群贮乳,因此可以知道Hazelcast集群的通信是基于TCP、UDP恬惯,需要打開socket支持集群交互向拆。因此我們可以指定使用的通訊方案。
- Hazelcast會啟動(dòng)多個(gè)線程來執(zhí)行不同的工作酪耳,有些負(fù)責(zé)維護(hù)數(shù)據(jù)浓恳、有些負(fù)責(zé)集群通信、有些負(fù)責(zé)一些基礎(chǔ)操作碗暗。因此我們可以配置和管理這些線程颈将。
- Hazelcast默認(rèn)使用MulitCast(組播協(xié)議)來組建集群,因此在局域網(wǎng)環(huán)境他可以無需配置自己完成集群組建言疗。因此我們可以指定使用TCP/IP或其他通訊協(xié)議晴圾。
- Hazelcast會自己探尋可以使用的端口,默認(rèn)情況下會使用5700到5800間沒有被占用的端口噪奄。因此我們可以配置這些端口如何使用死姚。
- Hazelcast初始化一個(gè)名為“數(shù)據(jù)分片”的方案來管理和存儲數(shù)據(jù)。因此我們可以調(diào)整和控制這些數(shù)據(jù)分片勤篮。
以上所有紅色字體的部分都可以通過配置文件來影響知允。在后續(xù)的文章中會詳細(xì)介紹相關(guān)的 配置說明。
Hazelcast運(yùn)行結(jié)構(gòu)
Hazelcast的官網(wǎng)上列舉了2種運(yùn)行模式叙谨,一種是p2p(點(diǎn)對點(diǎn))模式温鸽、一種是在點(diǎn)對點(diǎn)模式上擴(kuò)展的C/S模式。下圖是p2p模式的拓補(bǔ)結(jié)構(gòu)手负。
在p2p模式中涤垫,所有的節(jié)點(diǎn)(Node)都是集群中的服務(wù)節(jié)點(diǎn),提供相同的功能和計(jì)算能力竟终。每個(gè)節(jié)點(diǎn)都分擔(dān)集群的總體性能蝠猬,每增加一個(gè)節(jié)點(diǎn)都可以線性增加集群能力。
在p2p服務(wù)集群的基礎(chǔ)上统捶,我們可以增加許多客戶端接入到集群中榆芦,這樣就形成了集群的C/S模式柄粹,提供服務(wù)集群視作S端,接入的客戶端視作C端匆绣。這些客戶端不會分擔(dān)集群的性能驻右,但是會使用集群的各種資源。下圖的結(jié)構(gòu)就是客戶端接入集群的情況崎淳。
可以為客戶端提供特別的緩存功能堪夭,告知集群讓那些它經(jīng)常要使用的數(shù)存放在“離它最近”的節(jié)點(diǎn)。
Hazelcast分片概念與原理
Hazelcast通過分片來存儲和管理所有進(jìn)入集群的數(shù)據(jù)拣凹,采用分片的方案目標(biāo)是保證數(shù)據(jù)可以快速被讀寫森爽、通過冗余保證數(shù)據(jù)不會因節(jié)點(diǎn)退出而丟失、節(jié)點(diǎn)可線性擴(kuò)展存儲能力嚣镜。下面將從理論上說明Hazelcast是如何進(jìn)行分片管理的爬迟。
分片
Hazelcast的每個(gè)數(shù)據(jù)分片(shards)被稱為一個(gè)分區(qū)(Partitions)。分區(qū)是一些內(nèi)存段菊匿,根據(jù)系統(tǒng)內(nèi)存容量的不同雕旨,每個(gè)這樣的內(nèi)存段都包含了幾百到幾千項(xiàng)數(shù)據(jù)條目,默認(rèn)情況下捧请,Hazelcast會把數(shù)據(jù)劃分為271個(gè)分區(qū)凡涩,并且每個(gè)分區(qū)都有一個(gè)備份副本。當(dāng)啟動(dòng)一個(gè)集群成員時(shí)疹蛉,這271個(gè)分區(qū)將會一起被啟動(dòng)活箕。
下圖展示了集群只有一個(gè)節(jié)點(diǎn)時(shí)的分區(qū)情況。
從一個(gè)節(jié)點(diǎn)的分區(qū)情況可以看出可款,當(dāng)只啟動(dòng)一個(gè)節(jié)點(diǎn)時(shí)育韩,所有的271個(gè)分區(qū)都存放在一個(gè)節(jié)點(diǎn)中。然后我們啟動(dòng)第二個(gè)節(jié)點(diǎn)闺鲸。會出現(xiàn)下面這樣的分區(qū)方式筋讨。
二個(gè)節(jié)點(diǎn)的圖中,用黑色文字標(biāo)記的表示主分區(qū)摸恍,用藍(lán)色文字標(biāo)記的表示復(fù)制分區(qū)(備份分區(qū))悉罕。第一個(gè)成員有135個(gè)主分區(qū)(黑色部分),所有的這些分區(qū)都會在第二個(gè)成員中有一個(gè)副本(藍(lán)色部分)立镶,同樣的壁袄,第一個(gè)成員也會有第二個(gè)成員的數(shù)據(jù)副本。
當(dāng)增加更多的成員時(shí)媚媒,Hazelcast會將主數(shù)據(jù)和備份數(shù)據(jù)一個(gè)接一個(gè)的遷移到新成員上嗜逻,最終達(dá)成成員之間數(shù)據(jù)均衡且相互備份。當(dāng)Hazelcast發(fā)生擴(kuò)展的時(shí)候缭召,只有最小數(shù)量的分區(qū)被移動(dòng)栈顷。下圖呈現(xiàn)了4個(gè)成員節(jié)點(diǎn)的分區(qū)分布情況逆日。
上面的幾個(gè)圖說明了的Hazelcast是如何執(zhí)行分區(qū)的。通常情況下萄凤,分區(qū)的分布情況是無序的室抽,他們會隨機(jī)分布在集群中的各個(gè)節(jié)點(diǎn)中。最重要的是蛙卤,Hazelcast會平均分配成員之前的分區(qū)狠半,并均勻在的成員之間創(chuàng)建備份噩死。
在Hazelcast 3.6版本中颤难,新增了一種集群成員:“精簡成員”(lite members),他的特點(diǎn)是不擁有任何分區(qū)已维⌒朽停“精簡成員”的目標(biāo)是用于“高密度運(yùn)算”任務(wù)(computationally-heavy task executions。估計(jì)是指CPU密集型運(yùn)算)或者注冊監(jiān)聽(listener) 垛耳。雖然“精簡成員”沒有自己的分區(qū)栅屏,但是他們同樣可以訪問集群中其他成員的分區(qū)。
總的來說堂鲜,當(dāng)集群中的節(jié)點(diǎn)發(fā)送變動(dòng)時(shí)(進(jìn)入或退出)栈雳,都會導(dǎo)致分區(qū)在節(jié)點(diǎn)中移動(dòng)并再平衡,以確保數(shù)據(jù)均勻存儲缔莲。但若是“精簡節(jié)點(diǎn)”的進(jìn)入或退出哥纫,并不會出現(xiàn)重新劃分分區(qū)情況,因?yàn)榫喒?jié)點(diǎn)并不會保存任何分區(qū)痴奏。
數(shù)據(jù)分區(qū)管理
創(chuàng)建了分區(qū)以后蛀骇,Hazelcast會將所有的數(shù)據(jù)存放到每個(gè)分區(qū)中。它通過哈希運(yùn)算將數(shù)據(jù)分布到每個(gè)分區(qū)中读拆。獲取存儲數(shù)據(jù)Key值(例如map)或value值(例如topic擅憔、list),然后進(jìn)行以下處理:
- 將設(shè)定的key或value轉(zhuǎn)換成byte[]檐晕;
- 對轉(zhuǎn)換后的byte[]進(jìn)行哈希計(jì)算暑诸;
- 將哈希計(jì)算的結(jié)果和分區(qū)的數(shù)量(271)進(jìn)行模運(yùn)算(同余運(yùn)算、mod運(yùn)算辟灰、%運(yùn)算)屠列。
因?yàn)閎yte[]是和271進(jìn)行同模運(yùn)算,因此計(jì)算結(jié)果一定會在0~270之間伞矩,根據(jù)這個(gè)值可以指定到用于存放數(shù)據(jù)的分區(qū)笛洛。
分區(qū)表
當(dāng)創(chuàng)建分區(qū)以后,集群中的所有成員必須知道每個(gè)分區(qū)被存儲到了什么節(jié)點(diǎn)乃坤。因此集群還需要維護(hù)一個(gè)分區(qū)表來追蹤這些信息苛让。
當(dāng)啟動(dòng)第一個(gè)節(jié)點(diǎn)時(shí)沟蔑,一個(gè)分區(qū)表將隨之創(chuàng)建。表中包含分區(qū)的ID和標(biāo)記了他所屬的集群節(jié)點(diǎn)狱杰。分區(qū)表的目標(biāo)就是讓集群中所有節(jié)點(diǎn)(包括“精簡節(jié)點(diǎn)”)都能獲取到數(shù)據(jù)存儲信息瘦材,確保每個(gè)節(jié)點(diǎn)都知道數(shù)據(jù)在哪。集群中最老的節(jié)點(diǎn)(通常情況下是第一個(gè)啟動(dòng)的成員)定期發(fā)送分區(qū)表給所有的節(jié)點(diǎn)仿畸。以這種方式食棕,當(dāng)分區(qū)的所有權(quán)發(fā)生變動(dòng)時(shí),集群中的所有節(jié)點(diǎn)都會被通知到错沽。分區(qū)的所有權(quán)發(fā)生變動(dòng)有很多種情況簿晓,比如,新加入一個(gè)節(jié)點(diǎn)千埃、或節(jié)點(diǎn)離開集群等憔儿。如果集群中最早啟動(dòng)的節(jié)點(diǎn)被關(guān)閉,那么隨后啟動(dòng)的節(jié)點(diǎn)將會繼承發(fā)送分區(qū)表的任務(wù)放可,繼續(xù)將分區(qū)表發(fā)送給所有成員谒臼。
參考:https://www.chkui.com/article/hazelcast/hazelcast_get_started_and_code_sample