|
通過(guò)column name存儲(chǔ)數(shù)據(jù)是一項(xiàng)常用的實(shí)踐,同樣如果沒(méi)有必要保存column value,你也可以讓它為空。這樣做的動(dòng)機(jī)是column name是物理有序存儲(chǔ)的的,而column value不是。(譯者注:比如某個(gè)column name為字符串類(lèi)型,那么插入005、001、003、007最終的存儲(chǔ)順序?qū)⑹?01、003、005、007,這將意味著可以根據(jù)column name順序讀取數(shù)據(jù))。 注意: column name(以及row key)最大為64KB,所以請(qǐng)不要存儲(chǔ)如“物品描述”之類(lèi)的信息。 不要單獨(dú)使用timestamp作為column name,那樣會(huì)導(dǎo)致2個(gè)或者多個(gè)應(yīng)用服務(wù)器同時(shí)寫(xiě)入Cassandra時(shí)造成數(shù)據(jù)覆蓋(譯者注:相同row key,相同column name會(huì)覆蓋column value),推薦使用uuid(type-1 uuid)代替(譯者注:time-based UUID主要由cassandra客戶端時(shí)間戳、序列號(hào)、MAC地址組成,這樣的組合可以大大降低數(shù)據(jù)沖突)。 column value最大為2GB,它的采用非流式數(shù)據(jù)讀取,cassandra會(huì)將整個(gè)value加載到heap內(nèi)存中,這是很危險(xiǎn)的,所以請(qǐng)確保column value只存儲(chǔ)不超過(guò)幾MB的數(shù)據(jù)。(使用column value進(jìn)行大數(shù)據(jù)存儲(chǔ)一段時(shí)間內(nèi)都不會(huì)被支持,參見(jiàn) Cassandra-265,但是通過(guò)Cassandra java 客戶端——Astyanax,可以通過(guò)分塊的方式解決這個(gè)問(wèn)題)。 使用寬行(wide row)進(jìn)行排序、分組和過(guò)濾但不要搞的太長(zhǎng) 這個(gè)實(shí)踐與上面的內(nèi)容相關(guān),當(dāng)實(shí)際數(shù)據(jù)通過(guò)column name存儲(chǔ),我們會(huì)考慮使用寬行。 寬行的好處:
示例: 假設(shè)我們系統(tǒng)存儲(chǔ)一些事件日志數(shù)據(jù),然后按照小時(shí)獲取它們。請(qǐng)看下面的模型,row key是日期和小時(shí),column name存儲(chǔ)事件發(fā)生的時(shí)間,column value保存負(fù)荷值(payload)。請(qǐng)注意,這是一個(gè)寬行,事件通過(guò)時(shí)間順序進(jìn)行存儲(chǔ)。寬行的間隔刻度(當(dāng)前示例中,小時(shí)刻度比分鐘更適合)由實(shí)際用例、流量和數(shù)據(jù)大小決定,我們馬上會(huì)討論到它們。
即使是寬行也不要太寬,因?yàn)橐粋€(gè)row的數(shù)據(jù)不會(huì)跨節(jié)點(diǎn)保存 很難準(zhǔn)確的說(shuō)一個(gè)寬行多少個(gè)column才合適,這依賴于你的用例,下面是一些建議: 流量:所有流量相關(guān)數(shù)據(jù)保存到一個(gè)row中將導(dǎo)致只有一個(gè)節(jié)點(diǎn)能夠提供服務(wù)(假設(shè)只有一個(gè)數(shù)據(jù)副本)。那樣的row太龐大,當(dāng)row的數(shù)量小于集群節(jié)點(diǎn)數(shù)(希望這不會(huì)發(fā)生),或者寬行與窄行(skinny row)混合使用,再或者一些行訪問(wèn)更頻繁,都可能出現(xiàn)集群熱點(diǎn)問(wèn)題。但是,集群負(fù)載均衡最終依靠rowkey選擇;相反地,rowkey也決定row的長(zhǎng)度。所以在設(shè)計(jì)模型的時(shí)候要謹(jǐn)記負(fù)載均衡。 大?。?/b>因?yàn)閞ow不會(huì)跨節(jié)點(diǎn)分割,所以單個(gè)row必須適應(yīng)節(jié)點(diǎn)磁盤(pán)大小。但是,row可以擁有大量column,因?yàn)樗粫?huì)加載到內(nèi)存。Cassandra允許每個(gè)row包含20億個(gè)column。在ebay,我們沒(méi)有做任何寬行測(cè)試,我們從不保存超過(guò)百萬(wàn)column或者M(jìn)B大小數(shù)據(jù)到單個(gè)row(我們改變r(jià)owkey刻度或者分割為多個(gè)row)。如果你有興趣,可以看看Aaron Morton關(guān)于寬行性能的文章—— 《Cassandra Query Plans》(譯者注:原文鏈接404)。 但是,這些建議不意味你不應(yīng)該使用寬行,只是不要搞的太長(zhǎng)就好。 譯者注:原文Cassandra-4176和Cassandra-3929,這兩個(gè)bug的狀態(tài)為永不修復(fù),這里就不翻譯了。 選擇合適的rowkey否則,你將死于熱點(diǎn),即使你使用 RandomPartitioner 讓我們?cè)俅慰紤]上面的示例,存儲(chǔ)時(shí)間序列日志,同時(shí)按小時(shí)獲取它們。我們使用日期小時(shí)作為rowkey以保證一小時(shí)數(shù)據(jù)在一個(gè)row里。但是,這里有個(gè)問(wèn)題,當(dāng)前時(shí)間的所有寫(xiě)入都集中到一個(gè)節(jié)點(diǎn)將導(dǎo)致熱點(diǎn)問(wèn)題。減少rowkey刻度從小時(shí)到分鐘并不能真正解決問(wèn)題,因?yàn)?分鐘內(nèi)的數(shù)據(jù)仍然只會(huì)寫(xiě)入一個(gè)節(jié)點(diǎn)。隨著時(shí)間推移,熱點(diǎn)會(huì)移到其他節(jié)點(diǎn)但不會(huì)消失! 糟糕的 row key: “ddmmyyhh” 一種減輕問(wèn)題的方式是添加一些信息到rowkey——事件類(lèi)型、機(jī)器ID,或者適應(yīng)你用例的類(lèi)似值。 不錯(cuò)的 row key: “ddmmyyhh|eventtype”
注意,在這個(gè)column family中,我們現(xiàn)在沒(méi)有對(duì)所有事件按全局時(shí)間進(jìn)行排序,如果我們是通過(guò)事件類(lèi)型來(lái)查詢數(shù)據(jù)的話,這將不是問(wèn)題,如果用例要求按照時(shí)間順序獲取所有事件,我們就需要使用multi-get方法一次查詢多個(gè)event rowkey,然后將數(shù)據(jù)在Cassandra客戶端程序中將數(shù)據(jù)按照時(shí)間順序合并即可。 如果你不能添加任何信息到rowkey或者的確需要時(shí)間周期作為rowkey,另一個(gè)選擇是將你的row key手工分割為:“ddmmyyhh | 1″, “ddmmyyhh | 2″,… “ddmmyyhh | n”, N值為集群的節(jié)點(diǎn)數(shù)。一小時(shí)內(nèi),可以用輪詢的方式寫(xiě)入各個(gè)節(jié)點(diǎn)。當(dāng)讀取數(shù)據(jù)的時(shí)候,你需要使用multi-gets方法獲取所有節(jié)點(diǎn)的數(shù)據(jù)并做合并。(假設(shè)這里使用RandomPartitioner,因此無(wú)法使用rowkey范圍查詢) 讓讀多數(shù)據(jù)與寫(xiě)多數(shù)據(jù)分離這么做,你能夠充分利用Cassandra的off-heap行緩存特性。(譯者注:off--heap是一種脫離java gc的用法,通過(guò)api可以直接分配、釋放內(nèi)存) 無(wú)論NOSQL與否,保持讀寫(xiě)數(shù)據(jù)分離都是一個(gè)不錯(cuò)的實(shí)踐。 注意:行緩存對(duì)于窄行來(lái)說(shuō)很有用,但對(duì)寬行卻無(wú)益,因?yàn)樗鼤?huì)將整個(gè)行數(shù)據(jù)放到內(nèi)存中。通過(guò)Cassandra-1956 和 Cassandra-2864 未來(lái)可能改變這一現(xiàn)象,但是保持讀寫(xiě)分離這項(xiàng)實(shí)踐將仍然適用。 假如你的column family有大量數(shù)據(jù)(超過(guò)可用內(nèi)存),同時(shí)有熱行,開(kāi)啟行緩存可能對(duì)你有用。 確保column key和row key是唯一的否則,數(shù)據(jù)可能被覆蓋 在Cassandra(一個(gè)分布式數(shù)據(jù)庫(kù))中,row key和 column key是沒(méi)有強(qiáng)制唯一性約束的。 同樣地,cassandra也沒(méi)有update操作。cassandra所有操作都是upsert(不存在插入,存在則更新)操作。如果你插入的數(shù)據(jù)之前已經(jīng)存在相同row key和column key,那么之前的column value將會(huì)被悄無(wú)聲息地覆蓋(cassandra column value是沒(méi)有版本的,之前的數(shù)據(jù)將無(wú)法找回)。 使用合適的 comparator 和validator除非你真的需要,否則不要使用默認(rèn)的BytesType comparator 和 validator 在Cassandra中,column value(或者row key)的數(shù)據(jù)類(lèi)型稱為“Validator”。一個(gè)column name的數(shù)據(jù)類(lèi)型稱為“Comparator”。雖然Cassandra不要求你全部定義它們,但你必須至少指定comparator,除非你的column family是靜態(tài)的(就是說(shuō),你不會(huì)存儲(chǔ)實(shí)際的數(shù)據(jù)作為column name的一部分),或者你真的不關(guān)心column排序。 一個(gè)不合適的comparator 將不能按照你預(yù)期所想的順序?qū)olumn name排序并存放到磁盤(pán)上。它將很難(或者不能)根據(jù)column name進(jìn)行范圍查詢。 一旦完成定義,你將不能修改comparator,除非重寫(xiě)所有數(shù)據(jù)。但是validator 是可以事后變更的。 通過(guò)Cassandra文檔看看comparators 和 validators 支持的數(shù)據(jù)類(lèi)型。 保持column name簡(jiǎn)短因?yàn)樗鼘?huì)在被重復(fù)存儲(chǔ) 如果你使用column name存儲(chǔ)數(shù)據(jù),那這個(gè)實(shí)踐是不適用的。除此之外,保持column name簡(jiǎn)短,因?yàn)樗鼘⒑兔總€(gè)column value一起被重復(fù)存儲(chǔ)(根據(jù)數(shù)據(jù)副本數(shù))。當(dāng)column value的大小比column name小很多時(shí),內(nèi)存和存儲(chǔ)的開(kāi)銷(xiāo)可能會(huì)是個(gè)問(wèn)題。 比如,‘fname’ 優(yōu)于 ‘firstname’,‘lname’ 優(yōu)于 ‘lastname’。 注意:Cassandra-4175可能會(huì)導(dǎo)致本實(shí)踐在未來(lái)失效。 將數(shù)據(jù)模型設(shè)計(jì)為支持冪等操作或者確保你能夠接受用例數(shù)據(jù)不準(zhǔn)確或者最終準(zhǔn)確的情況 像Cassandra這樣具有最終一致性以及完全分布式的系統(tǒng),冪等操作非常有用。冪等操作允許系統(tǒng)因出現(xiàn)錯(cuò)誤而導(dǎo)致的重試是安全的,它不會(huì)影響數(shù)據(jù)的最終結(jié)果。此外,冪等有時(shí)可以降低對(duì)強(qiáng)一致性的需求,因?yàn)樗试S在不產(chǎn)生數(shù)據(jù)重復(fù)和其它異常的情況下實(shí)現(xiàn)最終一致性。讓我們看看這些原則如何應(yīng)用到Cassandra上。我將只討論部分失敗的情況,降低強(qiáng)一致性需求放到后續(xù)文章里講,因?yàn)樗浅R蕾囉美?/p> 因?yàn)镃assandra具有完全分布式(多個(gè)master)的特性,寫(xiě)失敗不能確保數(shù)據(jù)沒(méi)有被寫(xiě)入,這與關(guān)系型數(shù)據(jù)庫(kù)不同。換句話說(shuō),即使客戶端接收到一個(gè)寫(xiě)入操作失敗的響應(yīng),數(shù)據(jù)仍可能被寫(xiě)入其中一個(gè)副本,數(shù)據(jù)將最終被傳播到其它副本。沒(méi)有回滾或者清理動(dòng)作來(lái)處理已經(jīng)寫(xiě)入的數(shù)據(jù)。因此,雖然客戶端出現(xiàn)寫(xiě)失敗,但數(shù)據(jù)最終是成功寫(xiě)入的。所以如果你的模型不具有更新冪等性,錯(cuò)誤的重試將導(dǎo)致未知的結(jié)果。 注意:
示例: 假設(shè)我們想計(jì)算某個(gè)Item被多少User所喜愛(ài),一種方式是使用counter column family保存多少個(gè)user喜歡某個(gè)item。因?yàn)閏ounter增加(減少)特性不是更新冪等的,當(dāng)出現(xiàn)失敗并重試的時(shí)候就會(huì)導(dǎo)致數(shù)量被重復(fù)累加的情況。讓模型具有更新冪等性可以通過(guò)維護(hù)user id來(lái)代替增加counter記數(shù)。無(wú)論何時(shí)user喜愛(ài)某個(gè)item,我們都將user id寫(xiě)入item,如果寫(xiě)入失敗,我們可以安全的重試。如果希望獲取所有喜愛(ài)某個(gè)item的user,我們只需要讀取item中user id信息手動(dòng)累加就行。
在上面更新冪等(Update idempotent)模型中,獲取counter值要求讀取所有user id值,這樣的實(shí)現(xiàn)可能不夠好(因?yàn)樗赡苡猩习偃f(wàn)個(gè))。在本用例中,如果counter讀操作很頻繁,同時(shí)你能接受一個(gè)與真實(shí)值接近的值,counter column將是一個(gè)更好的、更高效的選擇。如果需要,counter值也可以周期的通過(guò)user id計(jì)算出來(lái)并更新,這樣的設(shè)計(jì)也是具有更新冪等性。 注意:Cassandra-2495可能會(huì)為失敗的counter請(qǐng)求添加一個(gè)合適的重試機(jī)制。但是,這個(gè)實(shí)踐仍然有效。所以記得經(jīng)常測(cè)試模型更新冪等性。 根據(jù)需要,圍繞事務(wù)建模但這可能不總是能夠?qū)崿F(xiàn),具體情況具體分析 Cassandra沒(méi)有多行、集群范圍的事務(wù)或者回滾機(jī)制,取而代之,它提供行級(jí)別原子性。換句話說(shuō),對(duì)某個(gè)row key的一次mutation操作是原子的。所以當(dāng)你需要事務(wù)性的時(shí)候,嘗試設(shè)計(jì)你的模型,讓它一次永遠(yuǎn)只更新一行。但還是要考慮你的用例,這樣的設(shè)計(jì)不總是有效,如果你的系統(tǒng)需要ACID事務(wù),那你需要重新考慮數(shù)據(jù)庫(kù)的選擇了。 (譯者注:Cassandra2.0.2支持多行事務(wù),詳見(jiàn)CASSANDRA-5633。) 如果可以的話,預(yù)先設(shè)計(jì)好合適的TTL因?yàn)楹茈y改變既有數(shù)據(jù)TTL 在Cassandra中,TTL(存活時(shí)間)不是設(shè)置在column family上,它設(shè)置于每個(gè)column value,一旦設(shè)置后就很難改變?;蛘哒f(shuō),如果創(chuàng)建column時(shí)候沒(méi)有設(shè)置,那之后就很難對(duì)其設(shè)置TTL。對(duì)于既有數(shù)據(jù)修改TTL唯一的方式就是讀取然后再次插入所有數(shù)據(jù),并賦予TTL。所以要考慮好如何清除你的數(shù)據(jù),如果可能進(jìn)來(lái)在創(chuàng)建數(shù)據(jù)的時(shí)候設(shè)置合適的TTL。 不要使用counter column family生成key信息它不是干這個(gè)用的 counter column family保存分布式counter信息,這意味著它要做分布式的累加(遞減)運(yùn)算。不要使用counter生成序列編號(hào)(比如oracle的sequence或者M(jìn)ySQL的自增列)來(lái)生成你的rowkey(column key),否則你將可能得到重復(fù)序列編號(hào),從而導(dǎo)致數(shù)據(jù)覆蓋。大多數(shù)時(shí)間你真的不需要全局序列編號(hào),更好的選擇是使用timeuuid (type-1 uuid)來(lái)生成key信息。如果你真的需要一個(gè)全局序列編號(hào)生成器,這里有個(gè)可行的辦法,就是需要一個(gè)中央?yún)f(xié)調(diào)器,但這會(huì)影響系統(tǒng)的擴(kuò)展性和可用性。 composite columns 優(yōu)于 super columns使用super columns可能會(huì)產(chǎn)生性能瓶頸 在Cassandra中,super column常被用于對(duì)column key進(jìn)行分組,或者建立兩層數(shù)據(jù)結(jié)構(gòu)。但是super column有下列實(shí)現(xiàn)問(wèn)題導(dǎo)致它并不那么美好: 問(wèn)題:
類(lèi)似的功能,可以通過(guò)Composite column實(shí)現(xiàn),它是包含子column的標(biāo)準(zhǔn)的column,因此標(biāo)準(zhǔn)column所有的好處它都具備,比如排序、范圍查詢,同時(shí)你可以設(shè)計(jì)出多層級(jí)的結(jié)構(gòu)。 注意composite columns中子column順序因?yàn)榕判驔Q定數(shù)據(jù)存儲(chǔ)位置 舉個(gè)例子,一個(gè)composite column key為<state | city>,將被先按照state然后按照city的順序存儲(chǔ),換句話說(shuō),一個(gè)state的所有cities在磁盤(pán)存儲(chǔ)上是緊挨著的。 優(yōu)先使用內(nèi)置的composite types,而不是自定義類(lèi)型因?yàn)樽远x類(lèi)型不總是可用 避免使用字符串連接(如果通過(guò)分隔符“:”或者“|”)的composite column key,取而代之,應(yīng)該使用內(nèi)置composite types (以及 comparators),它在cassandra0.8.1版本以上被支持。 Why?
注意:Cassandra內(nèi)置composite types有兩種使用方法:
關(guān)于composite type更多信息,請(qǐng)查看這里: Introduction to composite columns 無(wú)論何時(shí),盡量?jī)?yōu)先使用靜態(tài)composite type因?yàn)閯?dòng)態(tài)composite太靈活 如果一個(gè)column family中的column keys都是相同的composite type,則一定要使用靜態(tài)composite type。創(chuàng)建動(dòng)態(tài)composite type最初的目的是為了在一個(gè)column family中保存多用戶自定義索引。如果可能,請(qǐng)不要在一行中使用不同的composite type,除非真的有必要這么做。Cassandra-3625已經(jīng)修復(fù)了一些動(dòng)態(tài)composite的嚴(yán)重問(wèn)題。 注意:CQL3支持column name使用靜態(tài)composite type。如果想知道CQL3如何處理wide row,請(qǐng)參考這篇文章DataStax docs。 就這么多,如果您能從這些最佳實(shí)踐中獲益,非常歡迎看到您的回復(fù),這就是我們Cassandra今天的使用情況。 – Jay Patel, architect@eBay. |
|
|
來(lái)自: 文檔集成 > 《cassandra》