久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Cassandra分布式數(shù)據(jù)庫詳解,,第1部分:配置、啟動與集群

 冬木*細(xì)雪 2015-05-26

Cassandra 的配置詳解

了解一個軟件的配置項的意義是使用這個軟件的前提,,這里詳細(xì)介紹 Cassandra 的配置文件(storage-config.xml)中各個配置項的意義,,這其中包含有很多配置參數(shù),我們可以對其進(jìn)行調(diào)整以達(dá)到理想的性能,。為了節(jié)省篇幅這里沒有列出 storage-config.xml 文件的內(nèi)容,,你可以對照著這個文件看下面的內(nèi)容。

ClusterName

Cluster Name 代表一個族的標(biāo)識,它通常代表一個集群,。這個配置項在 Cassandra 沒有存儲數(shù)據(jù)時就必須指定,,當(dāng) Cassandra 第一次啟動后,它就會被寫到 Cassandra 的系統(tǒng)表中,,如果你要修改 Cluster Name 必須要刪除 Cassandra 中數(shù)據(jù),。

AutoBootstrap

這個配置項看起來十分簡單,但是如果你對 Cassandra 沒有深入了解的話,,恐怕不知道當(dāng)你改變這個配置項時 Cassandra 可能會發(fā)生什么,?

我們知道 Cassandra 集群是通過維護(hù)一個自適應(yīng)的 Token 環(huán)來達(dá)到集群中的節(jié)點的自治理,,它們不僅要保證每臺機器的狀態(tài)的同步和一致性還要保證它們之間 Token 分布的合理性,,通過重新劃分 Token 來達(dá)到每臺機器的負(fù)載的均衡性。

那這個配置項與 Token 和負(fù)載又有何關(guān)聯(lián)性,?其實表面上看起來這個配置項是當(dāng)這個節(jié)點啟動時是否自動加入集群,。但是,當(dāng)你設(shè)置成 False 時它是不是就不加入集群呢,?顯然不是,,這還要看你有沒有配置 seeds,如果你配置了其它 seed,,那么它仍然會去加入集群,。

那么到底有何區(qū)別,通過分析其啟動代碼發(fā)現(xiàn),,這個配置項不僅跟 seed 配置項有關(guān)而且和 Cassandra 是否是第一次啟動也有關(guān),。Cassandra 的啟動規(guī)則大慨如下:

當(dāng) AutoBootstrap 設(shè)為 FALSE,第一次啟動時 Cassandra 會在系統(tǒng)表中記錄 AutoBootstrap=TRUE,,以表示這是由系統(tǒng)自動設(shè)置的,,其實這個只是作為一個標(biāo)志來判斷你以后的啟動情況。

當(dāng) AutoBootstrap 設(shè)為 TRUE,,第一次啟動,,Cassandra 會判斷當(dāng)前節(jié)點有沒有被配置成 seed 節(jié)點,也就是在本機 ip 有沒有在 seeds 中,。如果在 seeds 中,,Cassandra 的啟動情況和 1 是一樣的。

當(dāng) AutoBootstrap 設(shè)為 TRUE,,第一次啟動,,并且沒有配置為 seed,Cassandra 將會有一個漫長的啟動過程,,當(dāng)然這個時間的長短和你的當(dāng)前的集群的數(shù)據(jù)量有很大的關(guān)系,。這時 Cassandra 將會根據(jù)當(dāng)前集群的負(fù)載,來動態(tài)調(diào)整它們的均衡。調(diào)整均衡的方式就是根據(jù)當(dāng)前的 Token 環(huán)分配一個合適的 Token 給這個節(jié)點,,并將這個符合這個 Token 的數(shù)據(jù)傳給它,。

從以上分析可以看出,AutoBootstrap 設(shè)置的主要目的是是否調(diào)整當(dāng)前集群中的負(fù)載均衡,。這其實還有一個很重要的問題就是,,如果按照第一種情況啟動,如果沒有指定 Token,,這個節(jié)點的 Token 將會是隨機生成的,,那么問題就來了,當(dāng)這個隨機生成是 Token 加入集群的 Token 環(huán)時,,Cassandra 如何保證 Token 和 Token 所對應(yīng)的數(shù)據(jù)的一致性,,這個問題將在后面說明。

Keyspaces

Cassandra 中 Keyspace 相當(dāng)于關(guān)系數(shù)據(jù)庫中的表空間的概念,,可以理解為操作表的一個容器,,它下面可以定義多個 ColumnFamily,這個 ColumnFamily 就相當(dāng)于表了,,它是存儲數(shù)據(jù)的實體,。

ColumnFamily 中幾個屬性的意義如下:

ColumnType。列的類型,,有兩種:Standard 和 Super,,分別是標(biāo)準(zhǔn)列和超列,超列的含義是列還有一個父列,。

CompareWith,。表示的是列的排序規(guī)則,可以根據(jù)不同的數(shù)據(jù)類型進(jìn)行排序如 TimeUUIDType,,可以根據(jù)插入的時間排序

CompareSubcolumnsWith,。子列的排序規(guī)則與 CompareWith 類似

RowsCached。查詢時緩存的數(shù)據(jù)量,,可以是多少條,,也可以是百分比,如 10% 就是緩存 10% 的數(shù)據(jù)量,,這個對查詢性能影響很大,,如果命中率高的話,可以顯著提高查詢效率,。

KeysCached,。緩存 ColumnFamily 中的 key,這個 key 就是對應(yīng)到 Index.db 中的數(shù)據(jù),,如果沒有在 RowsCached 中命中,,那么就要到每個 SSTable 中查詢,,這時必然要查詢 key,如果在 KeysCached 能命中就不需要到 Index.db 中查詢了,,省去了 IO 操作,。

Cassandra 是一個 Key/Value 系統(tǒng),從它的存儲的邏輯結(jié)構(gòu)來看分為:Keyspace,、Key,、ColumnFamily、Super Column 以及 Column 幾個部分,。很明顯我們能看出每一對 Key/Value 都有一個寄生的容器,,所以它實際上是由一個個 Map 容器構(gòu)成的。這個容器結(jié)構(gòu)可以用圖 1 和圖 2 來表示:

圖 1. 標(biāo)準(zhǔn)的 Column 結(jié)構(gòu)圖

462x113

圖 2. 含有 Super Column 的結(jié)構(gòu)圖

508x82

ReplicaPlacementStrategy

定義數(shù)據(jù)復(fù)制策略,,默認(rèn)是 org.apache.cassandra.locator.RackUnawareStrategy,,數(shù)據(jù)復(fù)制到其它節(jié)點沒有特別的規(guī)定。 org.apache.cassandra.locator.RackAwareStrategy 是將節(jié)點分為不同的 Rack,,這種方式不管是存數(shù)據(jù)還是查數(shù)據(jù),,都從不同的 Rack 的節(jié)點取數(shù)據(jù)或?qū)憯?shù)據(jù),。org.apache.cassandra.locator.DatacenterShardStategy 又將節(jié)點劃分為不同的 Data Center,,讓數(shù)據(jù)放在不同數(shù)據(jù)中心,從而保證數(shù)據(jù)的安全性,,例如可以按機房劃分 Data Center,,從而避免一個機房出現(xiàn)故障,會影響整個集群,。

ReplicationFactor

定義數(shù)據(jù)要保存幾個備份,,結(jié)合 ReplicaPlacementStrategy 可以把數(shù)據(jù)放在不同的地方。

EndPointSnitch

org.apache.cassandra.locator.EndPointSnitch 可以根據(jù)當(dāng)前的網(wǎng)絡(luò)情況選擇更好的節(jié)點路由,,一般默認(rèn)即可,。

Authenticator

這個配置項可以控制數(shù)據(jù)訪問的安全性,可以在 access.properties 和 passwd.properties 設(shè)置用戶和密碼,。

Partitioner

控制數(shù)據(jù)的分布規(guī)則,,org.apache.cassandra.dht.RandomPartitioner 是隨機分布,Cassandra 控制數(shù)據(jù)在不同的節(jié)點是通過 key 的來劃分的,,這個方式是將 key 進(jìn)行 MD5 Hash,,從而形成隨機分布的 Token,然后根據(jù)這個 Token 將數(shù)據(jù)分布到不同的節(jié)點上,。

org.apache.cassandra.dht.OrderPreservingPartitioner 是取 key 的 Ascii 字符來劃分的,,因此我們可以根據(jù) key 來主動控制數(shù)據(jù)的分布,例如我們可以給 key 加一個前綴,,相同前綴的 key 分布在同一個節(jié)點中,。

InitialToken

給節(jié)點分配一個初始 Token,當(dāng)節(jié)點第一次啟動后這個 Token 就被寫在系統(tǒng)表中。結(jié)合 Partitioner 就可以控制數(shù)據(jù)的分布,。這個配置項可以讓我們能調(diào)整集群的負(fù)載均衡,。

CommitLogDirectory、DataFileDirectories

這兩個配置項是設(shè)置 CommitLog 和 SSTable 存儲的目錄,。

Seeds

關(guān)于 Seeds 節(jié)點的配置有這樣幾個疑問:

是不是集群中的所有節(jié)點都要配置在 seed 中,。

本機需不需要配置在 seed 中。

關(guān)于第二個問題在前面中已經(jīng)說明了,,是否配置就決定是否作為 seed 節(jié)點來啟動,。關(guān)于第一個問題,答案是否定的,,因為即使你把集群中的所有節(jié)點都配置在 seed 中,,當(dāng) Cassandra 在啟動時它也不會往每個 seed 發(fā)送心跳信息,而是隨機選擇一個節(jié)點與其同步集群中的其他所有節(jié)點狀態(tài),。幾個回合后這個節(jié)點同樣能夠獲取集群中所有的節(jié)點的列表,。這就是集群自治理的優(yōu)點,只要能發(fā)現(xiàn)其中一個節(jié)點就能發(fā)現(xiàn)全部節(jié)點,。

ListenAddress

ListenAddress 這個配置是用來監(jiān)聽集群中其它節(jié)點與本節(jié)點交換狀態(tài)信息和數(shù)據(jù)的地址,。需要注意的是當(dāng)你配置為本機的 ip 地址沒有問題,不配置通常也沒問題,,但是如果你沒有配置或者配置成主機名,,而你又把你的主機名綁定到 127.0.0.1 時,這時將會導(dǎo)致本節(jié)點不能加入到集群中,,因為它接受不到其他節(jié)點過來的任何信息,,防止出錯直接綁定本機 ip 最好。

ThriftAddress

監(jiān)聽 Client 的連接請求,,不設(shè)或者配置成 0.0.0.0,,監(jiān)聽所有地址的請求。

RowWarningThresholdInMB

當(dāng) Cassandra 壓縮時,,如果一個 row 超出了配置的大小時打印 warn 日志,,沒有任何其它作用。

SlicedBufferSizeInKB 和 ColumnIndexSizeInKB

分別是用來配置,,根據(jù) Slice 和 Column Name 來查詢時 Cassandra 緩存數(shù)據(jù)的大小,,當(dāng)查詢范圍較小時可以適當(dāng)設(shè)置大一點以提高命中率。

FlushDataBufferSizeInMB 和 FlushIndexBufferSizeInMB

這兩個配置項是設(shè)置 Cassandra 在將內(nèi)存中的數(shù)據(jù)寫到磁盤時一次寫入的緩存量,,適當(dāng)提高這個兩個值可以提高 Cassandra 的寫性能,。

MemtableThroughputInMB、MemtableOperationsInMillions 和 MemtableFlushAfterMinutes

MemtableOperationsInMillions 是定義當(dāng)前 Keyspace 對應(yīng)的數(shù)據(jù)在內(nèi)存中的緩存大小,,Cassandra 默認(rèn)是 64M,,也就是當(dāng)寫到 Cassandra 的數(shù)據(jù)達(dá)到 64M 時,,Cassandra 會將內(nèi)存的數(shù)據(jù)寫到本地磁盤中。

MemtableOperationsInMillions 是定義當(dāng)前這個 Memtable 中所持有數(shù)據(jù)對象的個數(shù),,真實的個數(shù)是 MemtableOperationsInMillions*1024*1024,。當(dāng)超出這個數(shù)值時 Memtable 同樣會被寫到磁盤中。

MemtableFlushAfterMinutes 的作用是,,當(dāng)前兩個條件都長時間不滿足時,,Memtable 中數(shù)據(jù)會一直不會寫到磁盤,這也不合適,,所以設(shè)置了一個時間限制,,當(dāng)超過這個時間長度時 Memtable 中的數(shù)據(jù)也會被寫到磁盤中。

所以 Memtable 中的數(shù)據(jù)何時被寫到寫到磁盤是由這三個值決定,,任何一個條件滿足都會寫到磁盤,。

ConcurrentReads 和 ConcurrentWrites

這兩個是定義 Cassandra 用來處理 read 和 write 的線程池中線程的個數(shù),根據(jù)當(dāng)前的測試結(jié)果,,讀寫的性能大慨是 1:10,,適當(dāng)?shù)脑O(shè)置這兩個值不僅要根據(jù)讀寫的性能,還要參考當(dāng)前機器的處理性能,。當(dāng)機器的 load 很高,,但是 cpu 的利用率卻很低時,很明顯是由于連接數(shù)過多,,Cassandra 的已經(jīng)處理不過來都處于等待狀態(tài),。這樣就可以適當(dāng)增加讀寫的線程數(shù),,同樣如果當(dāng)讀的請求大于寫的請求時,,也應(yīng)該適當(dāng)增加讀的線程數(shù),反之亦然,。

CommitLogSync,、CommitLogSyncPeriodInMS 和 CommitLogSyncBatchWindowInMS

我們知道 Cassandra 是先寫到 CommitLog 中再寫到 Memtable 和磁盤中。如果每寫一條數(shù)據(jù)都要寫一次到磁盤那樣性能將會大打折扣,。Cassandra 為了提高寫 CommitLog 的性能提供了兩種寫的方式,。

Periodic。周期性的把 CommitLog 數(shù)據(jù)寫到磁盤中,,這個時間周期由 CommitLogSyncPeriodInMS 指定,,默認(rèn)是 10000MS, 如果是這種方式,可想而知 Cassandra 并不能完全保證寫到 Cassandra 的數(shù)據(jù)不會丟失,,最壞的情況就是在這個時間段的數(shù)據(jù)會被丟失,,但是 Cassandra 的解釋是通過數(shù)據(jù)的多個備份,來能提高安全性,。但是如果是單機存儲數(shù)據(jù),,最壞的情況仍然會丟失 10000MS 時間段寫入的數(shù)據(jù),。可以說這種方式寫 CommitLog 是完全的異步的方式,。

Batch,。這種方式是等待數(shù)據(jù)被寫到磁盤中才會返回,與前面相比安全性會得到保證,,它能保證 100% 數(shù)據(jù)的正確性,。但也并不是每寫一條數(shù)據(jù)都立即寫到磁盤中,而是有一個延遲時間,,這個延遲時間就是由 CommitLogSyncBatchWindowInMS 指定的,,也就是寫一條數(shù)據(jù)到 CommitLog 的最大時間是 CommitLogSyncBatchWindowInMS 指定的時間,理想的時間范圍是 0.1~10MS 之間,。這個時間既要平衡客戶端的相應(yīng)時間也要考慮服務(wù)器寫數(shù)據(jù)到磁盤的性能,。

這兩種方式各有好處,如果數(shù)據(jù)是存儲在有多個備份的集群中,,第一種情況下,,丟數(shù)據(jù)的情況幾乎為零,但是性能肯定會比第二種要好很多,。如果是單機情況下,,要保證數(shù)據(jù)的安全性第二種較合適。

GCGraceSeconds

這個配置項不是 Java 中的 gc 回收內(nèi)存,,但是其功能類似于 jvm 中 gc,,它也是回收已經(jīng)沒有被關(guān)聯(lián)的數(shù)據(jù),例如已經(jīng)被標(biāo)識為刪除的數(shù)據(jù),,Cassandra 處理數(shù)據(jù)有點奇怪,,即使數(shù)據(jù)被標(biāo)識為刪除,但是只要是沒有超過 GCGraceSeconds 的時間這個數(shù)據(jù)仍然是存在的,,也就是可以定制數(shù)據(jù)的實效時間,,超出這個時間數(shù)據(jù)將會被回收。

Cassandra 的啟動過程

Cassandra 的功能模塊

按照我的理解我將 Cassandra 的功能模塊劃分為三個部分:

客戶端協(xié)議解析,。目前這個版本 Cassandra 支持兩個客戶端 avro 和 thrift,,使用的較多的是后者,它們都是通過 socket 協(xié)議作為網(wǎng)絡(luò)層協(xié)議,,然后再包裝一層應(yīng)用層協(xié)議,,這個應(yīng)用層協(xié)議的包裝和解析都是由它們的客戶端和相應(yīng)的服務(wù)端模塊來完成的。這樣設(shè)計的目的是解決多種多樣的客戶端的連接方式,,既可以是短連接也可以是長連接,。既可以是 Java 程序調(diào)用也可以是 PHP 調(diào)用或者多種其它編程語言都可以調(diào)用,。

集群 Gossip 協(xié)議,。集群中節(jié)點之間相互通信是通過 Gossip 協(xié)議來完成的,它的實現(xiàn)都在 org.apache.cassandra.gms.Gossiper 類中。它的主要作用就是每個節(jié)點向集群中的其它節(jié)點發(fā)送心跳,,心跳攜帶的信息是本身這個節(jié)點持有的其它節(jié)點的狀態(tài)信息包括本節(jié)點的狀態(tài),,如果發(fā)現(xiàn)兩邊的狀態(tài)信息不是不一致,,則會用最新的狀態(tài)信息替換,,同時通過心跳來判斷某個節(jié)點是否還在線,把這種狀態(tài)變化通知感興趣的事件監(jiān)聽者,,以做出相應(yīng)的修改,,包括新增節(jié)點、節(jié)點死去,、節(jié)點復(fù)活等,。除了維護(hù)節(jié)點狀態(tài)信息外,還需做另外一些事,,如集群之間的數(shù)據(jù)的轉(zhuǎn)移,,這些數(shù)據(jù)包括:讀取的數(shù)據(jù)、寫入的數(shù)據(jù),、狀態(tài)檢查的數(shù)據(jù),、修復(fù)的數(shù)據(jù)等等。

數(shù)據(jù)的存儲,。數(shù)據(jù)的存儲包括,,內(nèi)存中數(shù)據(jù)的組織形式,它又包括 CommitLog 和 Memtable,。磁盤的數(shù)據(jù)組織方式,,它又包括 date、filter 和 index 的數(shù)據(jù),。

其它剩下的就是如何讀取和操作這些數(shù)據(jù)了,,可以用下圖來描述 Cassandra 是如何工作的:

圖 3. Cassandra 的工作模型

Cassandra 的啟動過程

這里將詳細(xì)介紹 Cassandra 的啟動過程。Cassandra 的啟動過程大慨分為下面幾個階段:

storage-config.xml 配置文件的解析

配置文件的讀取和解析都是在 org.apache.cassandra.config.DatabaseDescriptor 類中完成的,,這個類的作用非常簡單,,就是讀取配置文件中各個配置項所定義的值,經(jīng)過簡單的驗證,,符合條件就將其值賦給 DatabaseDescriptor 的私有靜態(tài)常量。值得注意的是關(guān)于 Keyspace 的解析,,按照 ColumnFamily 的配置信息構(gòu)建成 org.apache.cassandra.config.CFMetaData 對象,,最后把這些所有 ColumnFamily 放入 Keyspace 的 HashMap 對象 org.apache.cassandra.config.KSMetaData 中,每個 Keyspace 就是一個 Table,。這些信息都是作為基本的元信息,,可以通過 DatabaseDescriptor 類直接獲取。DatabaseDescriptor 類相關(guān)的類結(jié)構(gòu)如下圖 4 所示:

圖 4. DatabaseDescriptor 類相關(guān)的類結(jié)構(gòu)

創(chuàng)建每個 Table 的實例

創(chuàng)建 Table 的實例將完成:1)獲取該 Table 的元信息 TableMatedate,。2)創(chuàng)建改 Table 下每個 ColumnFamily 的存儲操作對象 ColumnFamilyStore,。3)啟動定時程序,,檢查該 ColumnFamily 的 Memtable 設(shè)置的 MemtableFlushAfterMinutes 是否已經(jīng)過期,過期立即寫到磁盤,。與 Table 相關(guān)的類如圖 5 所示:

圖 5. Table 相關(guān)的類圖

一個 Keyspace 對應(yīng)一個 Table,,一個 Table 持有多個 ColumnFamilyStore,而一個 ColumnFamily 對應(yīng)一個 ColumnFamilyStore,。Table 并沒有直接持有 ColumnFamily 的引用而是持有 ColumnFamilyStore,,這是因為 ColumnFamilyStore 類中不僅定義了對 ColumnFamily 的各種操作而且它還持有 ColumnFamily 在各種狀態(tài)下數(shù)據(jù)對象的引用,所以持有了 ColumnFamilyStore 就可以操作任何與 ColumnFamily 相關(guān)的數(shù)據(jù)了,。與 ColumnFamilyStore 相關(guān)的類如圖 6 所示

圖 6. ColumnFamilyStore 相關(guān)的類

487x330

CommitLog 日志恢復(fù)

這里主要完成這幾個操作,,發(fā)現(xiàn)是否有沒有被寫到磁盤的數(shù)據(jù),恢復(fù)這個數(shù)據(jù),,構(gòu)建新的日志文件,。CommitLog 日志文件的恢復(fù)策略是,在頭文件中發(fā)現(xiàn)沒有被序列化的最新的

ColumnFamily Id,,然后取出這個這個被序列化 RowMutation 對象的起始地址,,反序列化成為 RowMutation 對象,后面的操作和新添一條數(shù)據(jù)的流程是一樣的,,如果這個 RowMutation 對象中的數(shù)據(jù)被成功寫到磁盤中,,那么會在 CommitLog 去掉已經(jīng)被持久化的 ColumnFamily Id。關(guān)于 CommitLog 日志文件的存儲格式以及數(shù)據(jù)如何寫到 CommitLog 文件中,,將在后面第三部分詳細(xì)介紹,。

啟動存儲服務(wù)

這里是啟動過程中最重要的一步。這里將會啟動一系列服務(wù),,主要包括如下步驟,。

創(chuàng)建 StorageMetadata。StorageMetadata 將包含三個關(guān)鍵信息:本節(jié)點的 Token,、當(dāng)前 generation 以及 ClusterName,,Cassandra 判斷如果是第一次啟動,Cassandra 將會創(chuàng)建三列分別存儲這些信息并將它們存在在系統(tǒng)表的 LocationInfo ColumnFamily 中,,key 是“L”,。如果不是第一次啟動將會更新這三個值。這里的 Token 是判斷用戶是否指定,,如果指定了使用用戶指定的,,否則隨機生成一個 Token。但是這個 Token 有可能在后面被修改,。這三個信息被存在 StorageService 類的 storageMetadata_ 屬性中,,以便后面隨時調(diào)用。

GCInspector.instance.start 服務(wù),。主要是統(tǒng)計統(tǒng)計當(dāng)前系統(tǒng)中資源的使用情況,,將這個信息記錄到日志文件中,,這個可以作為系統(tǒng)的監(jiān)控日志使用。

啟動消息監(jiān)聽服務(wù),。這個消息監(jiān)聽服務(wù)就是監(jiān)聽整個集群中其它節(jié)點發(fā)送到本節(jié)點的所有消息,,Cassandra 會根據(jù)每個消息的類型,做出相應(yīng)的反應(yīng),。關(guān)于消息的處理將在后面詳細(xì)介紹,。

StorageLoadBalancer.instance.startBroadcasting 服務(wù)。這個服務(wù)是每個一段時間會收集當(dāng)前這個節(jié)點所存的數(shù)據(jù)總量,,也就是節(jié)點的 load 數(shù)據(jù),。把這個數(shù)據(jù)更新到本節(jié)點的 ApplicationState 中,然后就可以通過這個 state 來和其它節(jié)點交換信息,。這個 load 信息在數(shù)據(jù)的存儲和新節(jié)點加入的時候,,會有參考價值。

啟動 Gossiper 服務(wù),。在啟動 Gossiper 服務(wù)之前,,將 StorageService 注冊為觀察者,一旦節(jié)點的某些狀態(tài)發(fā)生變化,,而這些狀態(tài)是 StorageService 感興趣的,,StorageService 的 onChange 方法就會觸發(fā)。Gossiper 服務(wù)就是一個定時程序,,它會向本節(jié)點加入一個 HeartBeatState 對象,,這個對象標(biāo)識了當(dāng)前節(jié)點是 Live 的,并且記錄當(dāng)前心跳的 generation 和 version,。這個 StorageMetadata 和前面的 StorageMetadata 存儲的 generation 是一致的,,version 是從 0 開始的。這個定時程序每隔一秒鐘隨機向 seed 中定義的節(jié)點發(fā)送一個消息,,而這個消息是保持集群中節(jié)點狀態(tài)一致的唯一途徑,。這個消息如何同步,將在后面詳細(xì)介紹,。

判斷啟動模式,。是否是 AutoBootstrap 模式啟動,又是如何判斷的,,以及應(yīng)作出那些相應(yīng)的操作,,在前面的第一部分中已有介紹,這里不再贅述,。這里主要說一下,當(dāng)是 Bootstrap 模式啟動時,,Cassandra 都做了那些事情,。這一步很重要,,因為它關(guān)系到后面的很多操作,對 Cassandra 的性能也會有影響,。

這個過程如下:

通過之前的消息同步獲取集群中所有節(jié)點的 load 信息

找出 load 最大的節(jié)點的 ip 地址

向這個節(jié)點發(fā)送消息,,獲取其一半 key 范圍所對應(yīng)的 Token,這個 Token 是前半部分值,。

將這個 Token 寫到本地節(jié)點

本地節(jié)點會根據(jù)這個 Token 計算以及集群中的 Token 環(huán),,計算這個 Token 應(yīng)該分?jǐn)偧褐袛?shù)據(jù)的一個范圍(range)這個環(huán)應(yīng)該就是,最大 load 節(jié)點的一半 key 的所對應(yīng)的 range,。

向這個 range 所在的節(jié)點請求數(shù)據(jù),。發(fā)送 STREAM-STAGE 類型的消息,要經(jīng)過 STREAM_REQUEST,、STREAM_INITIATE,、STREAM_INITIATE_DONE、STREAM_FINISHED 幾次握手,,最終才將正確的數(shù)據(jù)傳輸?shù)奖竟?jié)點,。

數(shù)據(jù)傳輸完成時設(shè)置 SystemTable.setBootstrapped(true) 標(biāo)記 Bootstrap 已經(jīng)啟動,這個標(biāo)記的目的是防止再次重啟時,,Cassandra 仍然會執(zhí)行相同的操作,。

這個過程可以用下面的時序圖來描述:

圖 7. StorageService 服務(wù)啟動時序圖

以上是 AutoBootstrap 模式啟動,如果是以非 AutoBootstrap 模式啟動,,那么啟動將會非常簡單,,這個過程如下:

檢查配置項 InitialToken 有沒有指定,如果指定了初始 Token,,使用用戶指定的 Token,,否則將根據(jù) Partitioner 配置項指定的數(shù)據(jù)分配策略生成一個默認(rèn)的 Token,并把它寫到系統(tǒng)表中,。

更新 generation=generation+1 到系統(tǒng)表中

設(shè)置 SystemTable.setBootstrapped(true),,標(biāo)記啟動方式,防止用戶再修改 AutoBootstrap 的啟動模式,。

Cassandra 集群中的節(jié)點狀態(tài)的同步策略

我們知道 Cassandra 集群中節(jié)點是通過自治理來對外提供服務(wù)的,,它不像 Hadoop 這種 Master/Slave 形式的集群結(jié)構(gòu),會有一個主服務(wù)節(jié)點來管理所有節(jié)點中的原信息和對外提供服務(wù)的負(fù)載均衡,。這種方式管理集群中的節(jié)點邏輯上比較簡單也很方便,,但是也有其弱點,那就是這個 Master 容易形成瓶頸,,其穩(wěn)定性也是一種挑戰(zhàn),。而 Cassandra 的集群管理方式就是一種自適應(yīng)的管理方式,集群中的節(jié)點沒有 Master、Slave 之分,,它們都是平等的,,每個節(jié)點都可以單獨對外提供服務(wù),某個節(jié)點 Crash 也不會影響到其它節(jié)點,。但是一旦某個節(jié)點的狀態(tài)發(fā)生變化,,整個集群中的所有節(jié)點都要知道,并且都會執(zhí)行預(yù)先設(shè)定好的應(yīng)對方案,,這會造成節(jié)點間要發(fā)送大量的消息交換各自狀態(tài),,這樣也增加了集群中狀態(tài)和數(shù)據(jù)一致性的復(fù)雜度,但是優(yōu)點是它是一個高度自治的組織,,健壯性比較好,。

消息交換

那么 Cassandra 是如何做到這么高度自治的呢?這個問題的關(guān)鍵就是它們?nèi)绾瓮礁髯缘臓顟B(tài)信息,,同步消息的前提是它們有一種約定的消息交換機制,。這個機制就是 Gossip 協(xié)議,Cassandra 就是通過 Gossip 協(xié)議相互交換消息,。

前面在 Cassandra 服務(wù)啟動時提到了 Gossiper 服務(wù)的啟動,,一旦 Cassandra 啟動成功,Gossiper 服務(wù)就是一直執(zhí)行下去,,它是一個定時程序,。這個服務(wù)的代碼在 org.apache.cassandra.gms.Gossiper 類中,下面是定時程序執(zhí)行的關(guān)鍵代碼如清單 1 所示:

清單 1. Gossiper.GossipTimerTask.run

public void run(){
   synchronized( Gossiper.instance ){
     endPointStateMap_.get(localEndPoint_).getHeartBeatState().updateHeartBeat();
     List gDigests = new ArrayList();
     Gossiper.instance.makeRandomGossipDigest(gDigests);
     if ( gDigests.size() > 0 ){
      Message message = makeGossipDigestSynMessage(gDigests);
      boolean gossipedToSeed = doGossipToLiveMember(message);
      doGossipToUnreachableMember(message);
      if (!gossipedToSeed || liveEndpoints_.size() < seeds_.size())
             doGossipToSeed(message);
             doStatusCheck();
      }
     }
  }

Cassandra 通過向其它節(jié)點發(fā)送心跳來證明自己仍然是活著的,,心跳里面包含有當(dāng)前的 generation,,用來表示有的節(jié)點是不是死了又復(fù)活的。

本地節(jié)點所保存的所有其它節(jié)點的狀態(tài)信息都被放在了 GossipDigest 集合中,。一個 GossipDigest 對象將包含這個節(jié)點的 generation,、maxVersion 和節(jié)點地址。接著將會組裝一個 Syn 消息(關(guān)于 Cassandra 中的消息格式將在后面介紹),,同步一次狀態(tài)信息 Cassandra 要進(jìn)行三次會話,,這三次會話分別是 Syn、Ack 和 Ack2,。當(dāng)組裝成 Syn 消息后 Cassandra 將隨機在當(dāng)前活著的節(jié)點列表中選擇一個向其發(fā)送消息,。

Cassandra 中的消息格式如下:

header:消息頭 org.apache.cassandra.net.Header,消息頭中包含五個屬性:消息編號(messageId),、發(fā)送方地址(from),、消息類型(type)、所要做的動作(verb)和一個 map 結(jié)構(gòu)(details)

body:消息內(nèi)容,,是一個 byte 數(shù)組,,用來存放序列化的消息主體,。

可以用下面的圖 8 更形象的表示:

圖 8. message 消息結(jié)構(gòu)

573x415

當(dāng)組裝成一個 message 后,再將這個消息按照 Gossip 協(xié)議組裝成一個 pocket 發(fā)送到目的地址,。關(guān)于這個 pocket 數(shù)據(jù)包的結(jié)構(gòu)如下:

header:包頭,,4 bytes,。前兩個是 serializer type,;第三個是是否壓縮包,默認(rèn)是否,;最后一個 byte 表示是否是 streaming mode,。

body:包體,message 的序列化字節(jié)數(shù)據(jù),。

這個 pocket 的序列化字節(jié)結(jié)構(gòu)如下:

圖 9. 通信協(xié)議包的結(jié)構(gòu)

555x44

當(dāng)另外一個節(jié)點接受到 Syn 消息后,,反序列化 message 的 byte 數(shù)組,它會取出這個消息的 verb 執(zhí)行相應(yīng)的動作,,Syn 的 verb 就是解析出發(fā)送節(jié)點傳過來的節(jié)點的狀態(tài)信息與本地節(jié)點的狀態(tài)信息進(jìn)行比對,,看哪邊的狀態(tài)信息更新,如果發(fā)送方更新,,將這個更新的狀態(tài)所對應(yīng)的節(jié)點加入請求列表,,如果本地更新,則將本地的狀態(tài)再回傳給發(fā)送方,?;厮偷南⑹?Ack,當(dāng)發(fā)送方接受到這個 Ack 消息后,,將接受方的狀態(tài)信息更新的本地對應(yīng)的節(jié)點,。再將接收方請求的節(jié)點列表的狀態(tài)發(fā)送給接受方,這個消息是 Ack2,,接受方法接受到這個 Ack2 消息后將請求的節(jié)點的狀態(tài)更新到本地,,這樣一次狀態(tài)同步就完成了。

不管是發(fā)送方還是接受方每當(dāng)節(jié)點的狀態(tài)發(fā)生變化時都將通知感興趣的觀察者做出相應(yīng)的反應(yīng),。消息同步所涉及到的類由下面圖 10 的關(guān)系圖表示:

圖 10. 節(jié)點狀態(tài)同步相關(guān)類結(jié)構(gòu)圖

547x180

節(jié)點的狀態(tài)同步操作有點復(fù)雜,,如果前面描述的還不是很清楚的話,再結(jié)合下面的時序圖,,你就會更加明白了,,如圖 11 所示:

圖 11. 節(jié)點狀態(tài)同步時序圖

上圖中省去了一部分重復(fù)的消息,還有節(jié)點是如何更新狀態(tài)也沒有在圖中反映出來,,這些部分在后面還有介紹,,這里也無法完整的描述出來。

狀態(tài)更新

前面提到了消息的交換,,它的目的就是可以根據(jù)交換的信息更新各自的狀態(tài),。Cassandra 更新狀態(tài)是通過觀察者設(shè)計模式來完成的,,訂閱者被注冊在 Gossiper 的集合中,當(dāng)交換的消息中的節(jié)點的狀態(tài)和本地節(jié)點不一致時,,這時就會更新本地狀態(tài),,更改本地狀態(tài)本身并沒有太大的意義,有意義的是狀態(tài)發(fā)生變化這個動作,,這個動作發(fā)生時,,就會通知訂閱者來完成這個狀態(tài)發(fā)生變化后應(yīng)該做出那些相應(yīng)的改動,例如,,發(fā)現(xiàn)某個節(jié)點已經(jīng)不在集群中時,,那么對這個節(jié)點應(yīng)該要在本地保存的 Live 節(jié)點列表中移去,防止還會有數(shù)據(jù)發(fā)送到這個無法到達(dá)的節(jié)點,。和狀態(tài)相關(guān)的類如下:

圖 12. 更新狀態(tài)相關(guān)的類

526x165

從上圖可以看出節(jié)點的狀態(tài)信息由 ApplicationState 表示,,并保存在 EndPointState 的集合中。狀態(tài)的修改將會通知 IendPointStateChangeSubscriber,,繼而再更新 Subscriber 的具體實現(xiàn)類修改相應(yīng)的狀態(tài),。

下面是新節(jié)點加入的時序圖,如圖 13 所示:

圖 13. 新加入節(jié)點的時序圖

541x314

上圖基本描述了 Cassandra 更新狀態(tài)的過程,,需要說明的點是,,Cassandra 為何要更新節(jié)點的狀態(tài),這實際上就是關(guān)于 Cassandra 對集群中節(jié)點的管理,,它不是集中管理的方式,,所以每個節(jié)點都必須保存集群中所有其它節(jié)點的最新狀態(tài),所以將本節(jié)點所持有的其它節(jié)點的狀態(tài)與另外一個節(jié)點交換,,這樣做有一個好處就是,,并不需要和某個節(jié)點通信就能從其它節(jié)點獲取它的狀態(tài)信息,這樣就加快了獲取狀態(tài)的時間,,同時也減少了集群中節(jié)點交換信息的頻度,。另外,節(jié)點狀態(tài)信息的交換的根本還是為了控制集群中 Cassandra 所維護(hù)的一個 Token 環(huán),,這個 Token 是 Cassandra 集群管理的基礎(chǔ),。因為數(shù)據(jù)的存儲和數(shù)據(jù)流動都在這個 Token 環(huán)上進(jìn)行,一旦環(huán)上的節(jié)點發(fā)生變化,,Cassandra 就要馬上調(diào)整這個 Token 環(huán),,只有這樣才能始終保持整個集群正確運行。

到底哪些狀態(tài)信息對整個集群是重要的,,這個在 TokenMetadata 類中,,它主要記錄了當(dāng)前這個集群中,哪些節(jié)點是 live 的哪些節(jié)點現(xiàn)在已經(jīng)不可用了,,哪些節(jié)點可能正在啟動,,以及每個節(jié)點它們的 Token 是多少,。而這些信息都是為了能夠精確控制集群中的那個 Token 環(huán)。只要每個集群中每個節(jié)點所保存的是同一個 Token 環(huán),,整個集群中的節(jié)點的狀態(tài)就是同步的,,反之,集群中節(jié)點的狀態(tài)就沒有同步,。

當(dāng)然 Cassandra 用這種集群管理方式有其優(yōu)點,,但也存在一些缺點。例如現(xiàn)在部分使用者在大規(guī)模集群(上千臺服務(wù)器)的使用中發(fā)現(xiàn)不太穩(wěn)定,,這個跟 gossip 協(xié)議的本身也有關(guān),,所以這是 Cassandra 社區(qū)要致力解決的問題。

總結(jié)

本文從配置文件開始介紹了 Cassandra 的啟動過程,,以及 Cassandra 是如何管理集群的。實際上 Cassandra 的啟動和集群的管理是連在一起的,,啟動過程中的很多步驟都是集群管理的一部分,,如節(jié)點以 AutoBootstrap 方式啟動,在啟動過程中就涉及到數(shù)據(jù)的重新分配,,這個分配的過程正是在動態(tài)調(diào)整集群中 Token 環(huán)的過程,。所以當(dāng)你掌握了 Cassandra 是如何動態(tài)調(diào)整這個 Token 環(huán),你也就掌握了 Cassandra 的集群是如何管理的了,。下一篇將詳細(xì)介紹 Cassandra 內(nèi)部是如何組織數(shù)據(jù)和操作數(shù)據(jù),。

1 2
聲明:該文章系網(wǎng)友上傳分享,此內(nèi)容僅代表網(wǎng)友個人經(jīng)驗或觀點,,不代表本網(wǎng)站立場和觀點,;若未進(jìn)行原創(chuàng)聲明,則表明該文章系轉(zhuǎn)載自互聯(lián)網(wǎng),;若該文章內(nèi)容涉嫌侵權(quán),,請及時向上學(xué)吧網(wǎng)站投訴>>

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多