一、cassandra數據模型的特點
1.它基於key-value模型
cassandra的數據庫由ColumnFamilies組成,一個ColumnFamily是一個key-value鍵值對的集合。若和關係型數據庫類比,ColumnFamily相當於表,而裏面的key-value鍵值對相當於表裏的一條記錄。
2.它的key-value模型有多層嵌套
ColumnFamily裏的每條記錄都是一個key-value對,value部分存放的是無限制的Columns。每個Column都有一個Column Name和value,因此Column實際也是一個key-value對。但Column的value部分已經是最基本的數據存儲單元,不能再向下嵌套了。在這種嵌套下,ColumnFamily的每條記錄都包含一個key和一個由Columns組成的value(至少有一個Column),也就是說ColumnFamily的value只是一箇中間人,實際存儲數據的是value裏的Columns。如下圖所示。
上面所說的是雙層嵌套,還有一種三層嵌套。在這種情況下,ColumnFamily每條記錄的value部分不是Columns,而是一種被稱爲SuperColumn的結構。SuperColumn的key是SuperColumn name,它的value部分可以存儲多個Columns,如下圖所示:
這樣就有以下三種嵌套:
- ColumnFamily: key - value(SuperColumn)
- SuperColumn: key(SuperColumn name) - value(Column)
- Column: key(Column name) - value
SuperColumn裏不能再存儲SuperColumn,因此cassandra的嵌套最多爲三層。
3.Column和SuperColumn的name部分都可以用來存儲實際數據
首先,它們的name部分可以用來當做屬性名。比如在一個存儲用戶郵箱的記錄裏,它是這樣的:
- Column name=Email,value="[email protected]";
這是我們在傳統關係型數據庫裏所習慣和使用的。但在cassandra裏,name部分也可以直接用來存儲實際數據。比如在一個只用來存儲用戶郵箱的記錄裏,我們可以這樣:
- Column name="[email protected]",value=null;
這得益於(a)cassandra的非結構化數據存儲,數據的存儲不需要固定的位置(b)name部分也是使用字節流存儲(關係型數據庫的字段名必須是字符),因此可以存儲任何類型的數據。
4.Column和SuperColumn按照name的順序存儲
需要注意的是,cassandra並不是按照value的順序存儲數據,而是按照name。關於這點下文會詳細說明。
二、cassandra數據模型的構成
在這一部分,將詳細講解cassandra數據模型的各個組成部分。
1.Column
Column是數據存儲的最小單位。它是一個包括name、value和timestamp(時間戳)的元祖。下面是一個Column的示例:
- {
- name:"age",
- value:24,
- timestamp:123456789
- }
方便書寫,後文將省略時間戳,我們將Column看成一個name-value對。name和value都是字節流,長度沒有限制。
2.SuperColumn
一個SuperColumn是一個真正的name-value對,它沒有時間戳。而且它的value部分可以包含無限個Column,並且用Column的name部分作爲關鍵字。下面是一個SuperColumn的示例:
- {
- name:"homeAddress"
- //value部分是多個Columns
- value:
- //這裏的key是Column的name部分
- street:{name:"street",value:"XiTuCheng road"},
- city:{name:"city",value:"BeiJing"},
- zip:{name:"zip",value:"410083"},
- }
在後面,不再寫出name和value,上文將簡寫爲:
- homeAddress:{
- street:"XiTuCheng road",
- city:"BeiJing",
- zip:"410083",
- }
3.Row
在介紹下文的ColumnFamily前,我們先熟悉一下Row。在cassandra裏,每個ColumnFamily都存在一個單獨的文件裏,這個文件以Row爲單位存儲並排序。因此,我們應儘量將相關的Column放在同一個ColumnFamily裏。
ColumnFamily的組成是一行行的Row,一個Row就是一個key-value對,key決定數據將被存在哪臺機器上(筆記二的token部分有解釋),value部分就是Columns或SuperColumns。
4.ColumnFamily
ColumnFamily是一個可以包含無數個Row的結構,又因爲Row的value部分是Columns或SuperColumns,因此ColumnFamily實際是Columns和SuperColumns的容器。ColumnFamily對應關係型數據庫裏的“表”。下面給出ColumnFamily和Row的一個簡單示例(使用Column):
- User={//這是一個ColumnFamily,名字是User
- zhangsan:{//這是一個Row,Row的key是zhangsan
- //下面的value可以有無限制的Columns,這裏有兩個
- username:"zhangsan",
- email:"[email protected]",
- },//這個Row結束了
- lisi:{//這是第二個Row,Row的key是lisi
- //value部分,依然是Columns,lisi有三個
- username:"lisi",
- email:"[email protected]",
- phone:"123456"
- },//Row結束
- }
又如下圖所示:
在這個層面沒有設計模式的要求,Row沒有預先定義它們應該包含的Columns列表,就如上面的示例,李四可以隨意的多一個phone的Column。一個Row可能有成千上萬個Columns而另一個Row可能只有一個Column。cassandra在這一點上有無法比擬的靈活性。
5.屬性爲Super的ColumnFamily
上面的示例是一個type爲標準的(Standard)ColumnFamily,另外也有Super的ColumnFamily,這取決於我們創建ColumnFamily時的定義。顧名思義,一個類型爲Super的ColumnFamily的Row存儲的不是Columns,而是SuperColumns。在這種情況下,一個Row的value部分有若干個SuperColumns,一個SuperColumns的value部分又有若干個Columns。如下圖所示:
爲什麼要有SuperColumn呢?SuperColumn提供了比普通Column多一級的一對多關係。Column只能讓一個key存儲一組相關聯的Columns,而這個能讓一個key存儲多組相關聯的Columns。
這裏給出一個應用:假設我們提供一種網上地址本的服務,用戶可以在這保存他的朋友們的地址,而地址又是由不同的屬性如郵編、街道、城市等組成。這時候我們可以採用SuperColumn。對於ColumnFamily,它的key使用的是用戶自己的名字,value部分是若干SuperColumns。每個SuperColumns的name部分是用戶某個朋友的名字,value部分是若干Columns,存儲地址的各個屬性。下面是示例:
- AddressBook={//這是一個SuperColumnFamily,名字是AddressBook
- zhangsan:{//這是一個Row,key是zhangsan,張三的地址本
- //下面是Row的Value部分,可以有任意個SuperColumns
- lisi:{//這是SuperColumn的name
- //下面是Columns,表示地址
- street:"XiTuCheng road",
- zip:"410083",
- city:"BeiJing"
- },
- wangwu:{//另一個SuperColumn
- street:"XiTuCheng road",
- zip:"410083",
- city:"BeiJing"
- },
- zhaoliu:{//SuperColumn
- street:"XiTuCheng road",
- zip:"410083",
- city:"BeiJing"
- },
- .......
- }//end the row of zhangsan
- lisi:{//這是另一個Row,key是lisi,李四的地址本
- wangwu:{//SuperColumn
- street:"XiTuCheng road",
- zip:"410083",
- city:"BeiJing"
- },
- zhangsan:{//SuperColumn
- street:"XiTuCheng road",
- zip:"410083",
- city:"BeiJing"
- },
- .......
- }
- }
6.KeySpace
KeySpace是最外層的容器,也是最大的容器,通常一個應用程序對應一個KeySpace。所有的ColumnFamily都位於一個KeySpace裏面,它相當於關係數據庫裏的DB。
三、cassandra的數據排序
前面所介紹的是cassandra裏各種數據容器的概念,現在來看看數據模型的另外一個關鍵地方即數據是如何排序的。cassandra和關係型數據庫不同,你無法在取出數據時指定一種排序(order by)。數據在你存儲到集羣,被寫入數據庫時已經按照預定的規則被排好序。當你取出數據時,它們的順序已經確定了。
如前問所說,cassandra是按照name而不是value進行排序。cassandra在寫入數據的時候,每個row中的所有Columns會按照name自動排好序。排序的規則由ColumnFamily的CompareWith選項確定,可選的有:BytesType,UTF8Type,LexicalUUIDType,TimeUUIDType,AsciiType和LongType。這些選項將Column Name看作不同的數據類型來排序,如LongType將它視爲64bit Long類型。如下面給出的例子:
- {name: 123, value: “hello there”},
- {name: 832416, value: “kjjkbcjkcbbd”},
- {name: 3, value: “101010101010″},
- {name: 976, value: “kjjkbcjkcbbd”}
採用LongType排序類型,結果是:
- {name: 3, value: “101010101010″},
- {name: 123, value: “hello there”},
- {name: 976, value: “kjjkbcjkcbbd”},
- {name: 832416, value: “kjjkbcjkcbbd”}
採用UTF8Type排序類型,結果是:
- {name: 123, value: “hello there”},
- {name: 3, value: “101010101010″},
- {name: 832416, value: “kjjkbcjkcbbd”},
- {name: 976, value: “kjjkbcjkcbbd”}
這些排序規則也適用於SuperColumns在Row內的排序,但對於SuperColumn內的Columns,用來定義排序規則的參數不再是Row和SuperColumn裏的CompareWith,而是CompareSubcolumnsWith。
我們可以自定義排序規則,實現接口org.apache.cassandra.db.marsha1.IType即可。