2011-12-12

Tags: cassandra , 程式語言 , java

這儿個月斷斷續續的在摸Cassandra,目前有一些小心得,先記錄下來免的忘掉。不過...不確定這些Memo有沒有錯,如果有錯以後再來改吧...:)

註:下列的Memo以1.x版為主,舊的0.8.x版資料多數已被我移除。部份內容來自於TWJUG裡,某大濕的經驗談 (血淚控訴)

======================================================================
無法分類項目

  1. NoSQL = Not Only SQL
  2. Cassandra的資料操作與管理比GAE上的Datastore辛苦很多
  3. 官方目前在開發JDBC driver for CQL。如果這東東成熟的話,我覺的會比Hector API還好用。不過Hector API也會持續的把CQL功能給整合進來。
======================================================================
資料結構項目
  1. 我覺的最重要的儿個keyworld如下
    Row key = Key = K
    Column name = Name = N
    Column value = Value = V
  2. 儲存的資料中,除了Row key會自動建Index之外,Column value不自動建立Index;但是可以利用Secondary Index機制自行建立Column value的index。
  3. 在Column Family建立時,由裡面的comparator參數值決定了Column name的資料型別;由裡面的validation_class參數值決定了Row key、Column value的資料型別。
  4. 儲存資料的排序是依Row key、Column name自動排序,排序的規則跟Column family建立時設定的comparator有關聯,不是依Column value自動排序!!!
======================================================================
資料建置與維護
  1. 一個Cassandra系統一般會設置多個Node,每個Node會各有一個識別用的UUID來識別彼此,此UUID在Cassandra中被稱 為Token。多個Token(或說多個Node)的排列方式在邏輯上是一個環狀的結構被稱為Token ring。每個Token有各自的勢力範圍(e.g. row1~3可存放在token1,row4~6可存放在token2),這個範圍被稱為Partition。勢力範圍的判斷邏輯由 Partitioner來決定(在cassandra.yaml裡設定),常用的有RandomPartitioner、 ByteOrderedPartitioner二種;如果希望Row key要依序排序,要設成ByteOrderedPartitioner。這個條列項目的明細說明可參考這份文件
  2. 在Keyspace建立時,除了指定RF(ReplicationFactor:每筆資料要存放到儿個節點)之外,必需指定存放資料節點的選擇策略。一般常用策略有SimpleStrategy、NetworkTopologyStrategy,不同策略會導致資料分布方式有所不同,詳細說明可參 考這份文件
  3. 當系統有多個節點時,記的要遵守下面這規則,確保存放資料的一致性
    W(每次成功寫入資料存放節點的次數) + R(每次成功讀取資料存放節點的次數) > RF(ReplicationFactor:每筆資料要存放到儿個節點)
  4. 當系統有多個節點時,較佳的資料存取策略有下述三種
    • write all and read one
    • write one and read all
    • write quorum and read quorum
      (quorum公式 = RF/2 + 1)
  5. 系統節點數不同,用的資料存取策略不同,我自己會用的方式如下
    • 一個節點:用什麼策略的效果都一樣。
      (只一個節點的話,還會想用Cassnadra嗎...=_=?)
    • 二個節點:RF設成2,資料存取策略用「write all and read one」。這樣設的好處是資料讀取較快,有一個節點掛了時,Cassandra還是可被讀取;缺點是只要一個節點掛了,資料就無法寫入了。
    • 三個節點(含以上): RF設成3(或3以上),資料存取策略可用「write all and read one」或是「write quorum and read quorum」。「write all and read one」的優缺在二個節點裡有提及,不在多述。使用「write quorum and read quorum」的好處是在少量節點掛掉時,系統還是可以正常的被讀寫。等到掛掉的節點恢復後,系統提供的「hinted handoff」機制會把資料回寫到掛掉的節點。 
  6. Cassandra在執行寫入會比讀取來的快,寫入時會先直接寫到RAM裡面,然後再backend的把資料flush至硬碟。讀取時則是先到RAM裡存放的索引資料裡找出索引,再依找到的索引到硬碟裡把相關資料取出來。

    因為這特性,所以「write all and read one」存取策略對於少量寫入但是海量讀取的Application,有很大的優勢。
  7. 系統中最少要設定一個node為seed node。seed node有整個token ring的相關資訊,該node在啟動時不會去找整個token ring的資訊。反之,非 seed node則是啟動時會去跟seed node查詢整個token ring的資料。
  8. 當有新的node要加入現行的系統中時,必需修改cassandra.yaml。 將auto_bootstrap設成true(1.0版跟之後的版本已經將auto_bootstrap參數移除,這參數變成系統內部參數,在啟動時由系統自動判斷是否要啟用此功能), seeds參數裡需設定目前所有seed node的IP值。

    auto_bootstrap 設成true時,該node在第一次被啟動時會從seed node抓取整個token ring的設定,並把屬於自己partition的資料從其它node裡轉入。這個設定只在第一次啟動時生效,之後再次啟動時就不再做 auto_bootstrap動作。當auto_bootstrap設成true,而該node自身又扮演seed node角色時,auto_bootstrap設定會失效。
  9. 系統的每個node在第一次啟動時,如果不在cassandra.yaml裡的initial_token參數設定token值,系統會自動指派該值。但 是對於一開始就建置多個node的系統而言,自動指派的值不見的合適,可能會造成token ring裡,各自負責的partition大小差異很大(e.g. 建置4個node,但各自佔有整個token ring的partition卻是50%,25%,13%,12%)。要算出合適的token值可參考這篇文章。
  10. 系統的token值一旦第一次啟動後就無法在cassandra.yaml中再次修改,因為相關資料已經進了system keyspace的LocationInfo column family,這時如果要改只能用nodetool下指令的方式來改。
  11. 系統中每個node所佔的partition大小差異很大時,可利用"nodetool...move... "指令來移動token,進而解決partition差異過大的問題。

    "nodetool...move... "指令的動作原理是「先把該node退出token ring,並把該node的資料分到其它node裡面」,接著「再將該node裡舊token值換成新的token值,並重新加入token ring」,在完成後「再到其它node把屬於新token該擁有的資料取回」。整個過程對效能有很大影響,在執行時要考量執行時機。
  12. 在整個系統架構中,資料寫入的流程是"寫到Commitlog --> 寫到Memtable --> 寫到SSTable";資料讀出流程是"先讀RowCache-->再讀Memtable-->最後讀SSTable"。 (from TWJUG)
  13. 每個Node的Partitioner設定,建議設成RandomPartitioner,這樣資料分佈在整個Ring裡會比較平均,不會特別集中在特定儿個Node。(from TWJUG)
  14. Replication Strategy在cassandra.yaml裡的endpoint_snitch參數中設定,一般用預設的SimpleSnitch。如果有多個 DataCenter時,有其它對應的參數值可供設置。(from TWJUG)
  15. 整個Ring裡各Node的資料要維持一致性,分成寫與讀的二方可面的策略。實務建議是write quorum and read quorum,不建議write all and read one的原因是只要有一個Node掛掉這時就只剩下read功能正常而已。(from TWJUG)
  16. Consistency Repair功能中,分成寫與讀二方面的機制。

    寫入方面是靠Hinted Handoff來保証資料一定成功寫到所屬各個Node。實務上,Hinted Handoff可能會出包(1.0以前的版本會出包,官方聲稱這問題在1.0後已修正),因為Hinted Handoff在某些機會下,有可能會寫入"它認為沒掛掉但事實上已掛掉的Node",造成資料流失。

    讀取方面是靠Read Repair機制來確保所屬各個Node的資料正確無誤。在某些狀況下,Read的動作會導致先Write再Read的情況(e.g. 要從Node A,B,C取回同一份資料,執行時發現B的資料異常取不回來,這時Read Repair發生作用,把A或C取得的資料寫回至B,等整個修復完成後才會把Client端要取回的資料回傳)。(from TWJUG)
======================================================================
Client端操作
  1. 目前操作Cassandra最方便的方式依序是

    CQL > Cassandra CLI > Hector API > Thrift API

    基 本上,除了CQL看來比較像是給人用的之外,其它的都不怎麼好用。CQL目前還在快速演進中,功能不完整但可以期待。寫Java code來叫用cassandra的話,目前大概就只能選擇Hector API。如果要直接在Console下Command操作,則是選擇Cassandra CLI。 
  2. 利用Secondary Index機制進行資料查詢時,記的where condition裡的column field最少要包含一個"=",否則查詢會出錯。
======================================================================
前人血淚控訴留下的相關經驗(這一段超重要)
  1. 不要在非官方(Oracle) JVM上建置Cassnadra系統。(from TWJUG)
  2. 每個Node裡的CommitLog跟SSTable最好放在不同的Disk裡面。 (from TWJUG)
  3. JVM裡的heap size最好別超過16GB,不然可能會發生悲劇,系統在進行某些忙錄的運作時,可能整個當在那邊。(from TWJUG)
  4. Client端進行批次操作時,要注意每個批量的大小,每個批量過大可能會讓Server端發生timeout的問題,導致一直進行retry的動作。(from TWJUG)
  5. cassandra.yaml的partitioner參數設定不要用ByteOrderedPartitioner、OrderPreservingPartitioner。(from TWJUG)
  6. cassandra.yaml的initial_token參數一定要設,不要讓系統自動產生。系統產生的token會讓整個Ring裡,每個Partition的大小差異很大。(from TWJUG)
  7. SuperColumn未來會被CompositeColumn取代,不要再用SuperColumn了。(from TWJUG)
  8. Client端利用程式存取資料時,程式碼裡建議先實做寫的動作再實做讀的動作,這樣可避免Client端發生讀到舊資料的問題。(from TWJUG)
  9. Cassandra雖然在1.x版後支援windows系統,但是為了自己生命安全,請不要用它,還是乖乖的架在linux上吧,免的問題一大堆。(from TWJUG)