Apache Cassandra 數據存儲模型

Cassandra Apache HBase 數據庫 BigTable Google 阿里云云棲社區 2019-05-17

我們在《Apache Cassandra 簡介》文章中介紹了 Cassandra 的數據模型類似於 Google 的 Bigtable,對應的開源實現為 Apache HBase,而且我們在 《HBase基本知識介紹及典型案例分析》 文章中簡單介紹了 Apache HBase 的數據模型。按照這個思路,Apache Cassandra 的數據模型應該和 Apache HBase 的數據模型很類似,那麼這兩者的數據存儲模型是不是一樣的呢?本文將為大家解答這些問題。我們從 KeySpace -> Table -> Partition -> Row -> Cell 順序介紹。本文基於 Apache Cassandra 3.11.4 源碼進行介紹的,不同版本可能有些不一樣。

Table & KeySpace

Cassandra 中的 KeySpace 概念和 RDBMS 裡面的 DataBase 概念很類似,一個 KeySpace 包含多張表,一般將有關聯的數據表放到同一個 KeySpace 下面。KeySpace 創建的時候可以指定副本策略,副本因子以及是否啟用 CommitLog 機制(類似 HBase 中的 WAL)。

Cassandra 中表的概念和 RDBMS 很類似。不同的是在 Cassandra 中屬於同一張表的數據在物理上是分佈在不同節點上存儲的,同一張表由多個 Partition 組成。

Partitions

Cassandra 一般是由多臺節點組成的,每臺節點負責一定範圍的,如果使用 Murmur3hash 的時候,每個節點負責的 Token 類似於下面那樣:

Apache Cassandra 數據存儲模型

所以 Token 範圍為 -9223372036854775808 ~ -4611686018427387904 的數據存儲在 A 節點;同理,Token 範圍為 -4611686018427387903 ~ -1 之間的數據存儲在 B節點,其他類似;每個 Token 範圍由多個 Partition 構成,每個 Partition 由一行或多行數據組成,Partition 類似下面的:

Apache Cassandra 數據存儲模型

其中,username 為 Partition key;type 為 Clustering key。那麼在這種情況下,username = iteblog 的兩條數據構成一個 Partition;另外兩條構成分別構成兩個 Partitions。在底層存儲每個 Partition 格式如下:

Apache Cassandra 數據存儲模型

從上圖可以看出,一個 Partition 是由 PartitionHeader、零個或多個 Row (格式在後面介紹)以及 EndPartition 三部分組成的。EndPartition 這個用於標記 Partition 結束,只佔用一個字節,並使用 0x00000001 標記。PartitionHeader 的格式如下:

Apache Cassandra 數據存儲模型

  • Partition Key 就是我們建表的時候指定的,由於 Partition Key 長度使用兩字節表示,所以 Cassandra 中 Partition Key 長度必須小於等於 65535 字節。
  • Local Delete Time 是刪除發生時的服務器時間(以秒為單位),與 gc_grace_seconds 進行比較以確定何時可以清除它。當與 TTL 一起使用時,localDeletionTime 是數據到期的時間。共佔四個字節;
  • Marked For Delete At 記錄刪除的時間戳,時間戳小於此值的數據被視為已刪除,共佔用八字節。
  • Static Row:如果我們建表的時候有 Static 字段,那麼標記為 Static 的列會在這裡存儲。從這裡也可以看出,partition key 相同的數據 Static 列只會保存一份數據。

在底層存儲中,多個 Partition 組成一個 SSTable(Sorted-String Table)文件。那麼同一個 SSTable 文件中的數據數據是如何組織的呢?答案是按照 Partition Key 計算得到的 Token 升序排序的。

Row

上面看出,Partition 裡面包含了零個或多個 Row,這些 Row 對應的 Partition Key 是一樣的。非 Static 的 Row 在磁盤存儲的格式如下:

Apache Cassandra 數據存儲模型

上面除了 flags 、Row Body Size 、 Previous Row Body Size 以及 Primary Key Liveness Timestamp 這四個字段一定會存在,其他字段需要滿足條件才會存儲。下面對上面字段進行介紹:

  • flags:Row 的標記信息,主要用於標記當前 Row 是否存在時間戳、TTL、被刪除、是否包含所有的列等信息。flag 字段佔用一個字節,
  • hasExtendedFlags:當前 Row 是否含有 Static 列,存在才會有數據;
  • Clustering info:每個 Row 包含零個或多個 Clustering 相關的信息。Clustering 信息就是我們創建表的時候指定的 Clustering key 信息。每個 Clustering Info 在持久化的時候會先存儲頭部信息,標記當前 Clustering key 是否為空、是否為 null 以及是否有值等信息;然後根據數據類型將值存下來,如果當前 Clustering key 的值佔用字節非固定,還需要存儲當前 Clustering key 值的字節數。
  • Row Body Size:當前 Row Body 的大小,Row Body 包含 primary key 的 liveness 信息、Row 是否刪除等信息以及 Cell 的信息。
  • Previous Row Body Size:前一個 Row Body 的大小,這個主要用於加速反向查詢的,不過當前並沒有使用;
  • Primary Key Liveness Timestamp:primary key 的 Liveness 用於確定行是否還活著或已經死了(沒有 live cells 並且 liveness 為空)。這個字段主要用於存儲當前 Row 的 Liveness 時間戳。注意,持久化到磁盤的時間戳是相對於當前 Memtable 最小時間戳的值。
  • Primary Key Liveness TTL:這個字段主要用於存儲當前 Row 的 Liveness TTL 信息。也是相對於當前 Memtable 最小 TTL 的值
  • Primary Key Liveness LocalExpirationTime:當前 Liveness 的 ExpirationTime,也是相對時間;
  • Row Marked For Delete At:當前 Row 的刪除時間,也是相對時間,精確到毫秒;
  • Row Local Deletion Time:當前被標記為 tombstone 時服務器的時間,也是相對時間,精確到秒;
  • Columns Bitmap:從 Cassandra 3.x 開始,列的信息已經不保存到數據文件裡面了,列的信息是保存在對應 SSTable 的 md-X-big-Statistics.db 文件中。這個字段是用於標記當前行哪些列存在,哪些列不存在。如果列存在則標記為0;如果列不存在則標記為1;如果列全部存在,直接標記為0。當表的字段數小於64個的時候,直接使用一個 long 類型的數據來存儲這個 bitmap。如果大於等於64個,處理方案稍微複雜一些:
  • 先保存一個標記位,標記當前表擁有的字段個數大於等於64;

如果存在的列沒有佔總列數的一半,則按照全部列的順序保存存在的列在排序後列的索引位置;

如果存在的列佔總列數超過一半,則按照全部列的順序保存不存在的列在排序後列的索引位置。

可見,Cassandra 通過將列的信息(包括列的名稱、類型、表名、keySpace等信息)保存到對應 SSTable 的 md-X-big-Statistics.db 文件中,相應的行只保存列是否存在的標記信息,這個可以節省存儲空間的佔用。注意,HBase 存儲數據的時候每個 Cell 都需要保存列名稱和列族名稱的。

非 Static Row 的底層存儲格式已經在前面描述過,對於 Static Row 除了沒有上圖的 Clustering info 信息,其餘都一樣,所以這裡就不介紹了。

上圖中最後有 N 個 Cell,那多個 Cell 之間的順序是如何保證的呢?答案是按照列的名稱字典順序升序排序的。比如我們表的定義如下:

CREATE TABLE iteblog (
user_id text,
type text,
action text,
username text,
age text,
email text,
PRIMARY KEY(user_id)
);

那麼 Cell 的順序排列如下:

action -> age -> email -> type -> username

這個排序是通過 BTree 實現的,Row 的實現類為 BTreeRow。

Cell

Cell 就是每列數據的底層實現,Cell 裡面包含了列的定義信息,比如是否被刪除、是否過期、是否設置了時間戳等。在 Cassandra 裡面,Column 有 Simple 和 Complex(CASSANDRA-8099引入的) 之分。non-frozen collection 或 UDT(用戶自定義類型)的列是 ComplexColumn(Complex Cell)。

Simple Cell(Simple Column)的底層格式

我們正常使用的列就是屬於這種類型的,它的底層存儲格式如下:

Apache Cassandra 數據存儲模型

  • flags:這個 Cell 的 flag 標記,主要用於標記當前 Cell 是否有值、是否被刪除、是否過期、是否使用 Row 時間戳、是否使用 Row TTL 等信息。flag 字段佔用一個字節,每位的含義代表如下:
Apache Cassandra 數據存儲模型

  • timestamp:當前 Cell 的時間戳,Cassandra 中我們可以對每列設置時間戳;
  • deletion time:當前 Cell 的刪除時間;
  • ttl:當前 Cell 的 TTL,Cassandra 中我們可以對每列設置 TTL,代表這個 Cell 保留多長時間;
  • value:當前 Cell 的值;

Complex Cell(Complex Column)的底層格式

如果列屬於 non-frozen collection 或 UDT(用戶自定義類型),那麼這個屬於 Complex Cell,它的底層存儲格式如下:

Apache Cassandra 數據存儲模型

可以看出,Complex Cell 和 Simple Cell 大部分很類似,下面只介紹不一樣的地方:

  • Complex Cell Marked For Delete At & Complex Cell Local Deletion Time:這兩個屬性和前面的類似,只不過針對 Complex Cell 而言的。
  • Complex Cell Counts:Complex Cell 的個數;
  • path:當前 Cell 的路徑。

在 Cassandra 中, Complex Cell 的實現類是 ComplexColumnData。

作者:明惠

相關推薦

推薦中...