怎麼去給一個Row的Column建立二級索引是Cassandra中一個常見擬題。下面的這個文章來講一個實現方式,當然不是只有這一種才能實現。對於有經驗的Cassandra使用者來講,這個文章應該會提起興趣哦,這裡描述的實現方式根本不用Super Column,也就不會有使用Super Column帶來的複雜度和約束了。此外,應該指出的是,Cassandra0.7版本以上都會實現原生的二級索引,它使下面講述的東西更加簡單了,但是這個思路對於考慮Cassandra的二級索引任然是非常有效,還是可以在很多情境得到應用。
首先,我們假設如下一個情境。有一個Container(比如:一個部門),該Container中包含眾多的Items(比如:部門中的使用者),每一個使用者(user)有任意的屬性集合,也可以根據Container中的內容相關的值來搜尋。Items也可以是別的Container的成員,但是在這裡先不考慮這種情況。
在Cassandra中,一種建模方式是使用2個ColumnFamilies(下面將簡稱為CF)。第一個CF會描述Item的屬性,名稱為Item_Properties,它用Cassandra的最簡答的資料模型,在Item_Properties的Row能通過一個Key來找到,在這裡例子中將使用UUID來描述這個Key,在Item_properties中的列是Item的屬性名稱,列的值是對應的屬性的值。
CF: Item_Properties |
Key: item_id |
Compare with: BytesType |
Name |
Value |
property_name |
property_value |
... |
... |
第二個CF是均對Container的,它包括Items的集合,名稱為Container_Items。Container_Items中的列為Item_properties中的行的Key。在Cassandra中,這是一個讓人難以理解的地方。當你把Column Family當做一個簡單的關聯式資料庫的表,把CF中的行也作為一個關聯式資料庫中的一條記錄來使用的時候,每一個行可以作為一個簡單的表,甚至是一個串連的表。在Container_Items中,每一個列名用Item_Properties的行Key,列值則填充插入時候的目前時間戳。Container_Items的行可以增長的相當大,由於每一個列大概有42個位元組(UUID+timestamp),在Cassandra0.7以下的版本,最大能允許4000萬條Items,對於一個部門中的User來講這個也許是一個合理的限制,但是如果你用這個方式去存放Status的對應資訊(比如Tweets),那就是一個不可以接受的了,對於一個狀態的Tweets肯定會超過這個限制。不過,在Cassandra0.7及其以後的版本中,就沒有這個限制了,一行最多能夠存20億列。
CF: Container_Items |
Key: container_id |
Compare with: TimeUUIDType |
Name |
Value |
item_id |
insertion_timestamp |
... |
... |
到目前為止,這些都是相當基礎的Cassandra資料模型。當一個人想從Container中根據指定屬性值來尋找Items的時候,事情就會變複雜了。為了實現這個目標,你需要管理你自己的索引,大大超過了Cassandra的最簡設計了。需要建立另外兩個ColumnFamily來實現這個目標。第一個CF存放實際的索引,用Container_ID和想去索引的Item_Properties中的屬性名稱作為行Key。結構如下表:
CF: Container_Items_Property_Index |
Key: container_id + property_name |
Compare with: compositecomparer.CompositeType |
Name |
Value |
composite(property_value, item_id, entry_timestamp) |
item_id |
... |
... |
這裡描述的索引技術與其它地方有點不同的是索引中每一列是怎麼構成的。Cassandra提供了一套用於對行中的列進行排序的Column Type。你能在CF在被建立的時候指定一種排序類型,Cassandra也允許自訂Column Typesz,正如上面所使用到的組合類別型。組合類別型的列可以讓我們將幾個不同的成分進行組合為一個列,並且還可以按照該列進行排序。這使得可以讓我們建立唯一的列,就算是那些列原本是存在不唯一的值也沒有問題,不過需要添加一些額外的值去加以區別。
最後一個問題要處理的是屬性值需要改變並且索引值必須被更新的時候會發生什麼。答案很簡單,你在Container_Items_Property_Index列族的把新值作為一個列插入,並刪除舊值列。然而,Cassandra的最終一致性模型和事務缺乏相關的原因,簡單地從Item_Properties取以前的值,然後再更新,然後刪除Container_Items_Property_Index索引條目中的老得值將無法可靠地工作。To dothis we maintain a list of previous values
for the specific property of a givenitem and use that to remove these values from the index before adding the newvalue to the index. These are stored in the following CF:
CF: Container_Item_Property_Index_Entries |
Key: container_id + item_id + property_name |
Compare with: LongType |
Name |
Value |
entry_timestamp |
property_value |
... |
在取出這些列之後就刪除掉,所以這些行絕不會變得太大,在大多數情況下,絕不會超過1到2列,如果修改得比較頻繁則會大一些。通過這個方法, it's areally good idea to make sure you understand why this CF is necessary becauseyou can use variations of it to solve a lot of problems with "eventualconsistency" datastores.
所以,整體看來,主要有兩個基本的操作:(1)給Container中的一個Item設定屬性值 (2)從Container中取得匹配特定Value的Items列表資訊。這些看起來像這樣:
給Container中的Item的屬性(property_name)設定值(property_value)的過程如下:
1、取得新增加的實體(entry)的timestamp作為當前的時間戳記的值;
2、以Container_ID+item_ID+property_name為Key用get_Slice方法去Container_item_Property_index_entries中尋找合格列資訊;
3、調用batch_mutate方法去批量完成如下步驟:
從Container_Items_Property_index中刪除掉那些從Container_item_Property_index_entries中找到的先前步驟中的列資訊;
從Container_item_Property_index_entries中刪除掉先前尋找出來的列資訊;
向Item_properties中插入列(列名為Property_name,值為property_value);
向Container_Items_Property_Index中插入新的索引記錄資訊;
向Container_Item_Property_Index_Entries插入新的值資訊,供後續修改時使用;
按照Property_value來查詢Container中的Items過程如下:
1、以container_id + property_name為Key從Container_Items_Property_Index列簇中調用方法Get_Slice來找到匹配的Property_Value
看起來有很多步驟,但實際上,所有的步驟都被中介軟體封裝了,外面都不可見。你能從CassandraCompositeTypeon GitHub 中找到Composite column比較的具體實現,能從 CassandraIndexedCollectionson
GitHub..中找到上述索引技術的簡單實現
Update: Mike Malone pointsout that, since Cassandra already stores a timestamp along with the columnvalue, that it's redundant to store in the column value as well and can beomitted in the Container_Items
and Container_Item_Property_Index_Entries columnfamilies, which would reduce storage space by about 20%.
翻譯的很醜陋,自己都覺得不爽了,只是還是想堅持下去!
大家可以參考原文:http://www.anuff.com/2010/07/secondary-indexes-in-cassandra.html