Cassandra 文件

版本

您正在檢視預發行版本的說明文件。

資料類型

CQL 是一種已輸入的語言,支援豐富的資料類型,包括 原生類型集合類型使用者定義類型元組類型自訂類型

cql_type::= native_type| collection_type| user_defined_type | tuple_type | custom_type

原生類型

CQL 支援的原生類型為

native_type::= ASCII | BIGINT | BLOB | BOOLEAN | COUNTER | DATE
| DECIMAL | DOUBLE | DURATION | FLOAT | INET | INT |
SMALLINT | TEXT | TIME | TIMESTAMP | TIMEUUID | TINYINT |
UUID | VARCHAR | VARINT | VECTOR

下表提供原生資料類型的其他資訊,以及每種類型支援的 常數 類型

類型 支援的常數 說明

ascii

字串

ASCII 字元字串

bigint

整數

64 位元有號長整數

blob

blob

任意位元組 (無驗證)

boolean

boolean

truefalse

counter

整數

計數器欄位 (64 位元有號值)。有關詳細資訊,請參閱 counters

date

integerstring

日期 (沒有對應的時間值)。有關詳細資訊,請參閱下方的 dates

decimal

integerfloat

可變精度的十進位數

double

integer float

64 位元 IEEE-754 浮點數

duration

duration,

具有奈秒精度的持續時間。有關詳細資訊,請參閱下方的 durations

float

integerfloat

32 位元 IEEE-754 浮點數

inet

字串

IP 位址,可能是 IPv4 (4 位元組長) 或 IPv6 (16 位元組長)。請注意,沒有 inet 常數,IP 位址應輸入為字串。

int

整數

32 位元有號整數

smallint

整數

16 位元有號整數

文字

字串

UTF8 編碼字串

時間

integerstring

時間(無對應日期值),精確到奈秒。詳情請參閱下方 times

時間戳記

integerstring

時間戳記(日期和時間),精確到毫秒。詳情請參閱下方 timestamps

timeuuid

uuid

版本 1 UUID,通常用作「無衝突」時間戳記。另請參閱 timeuuid-functions

tinyint

整數

8 位元有號整數

uuid

uuid

UUID(任何版本)

varchar

字串

UTF8 編碼字串

varint

整數

任意精度的整數

向量

float

定長非空陣列,包含浮點值 CASSANDRA-18504 將此資料類型新增至 Cassandra 5.0。

計數器

counter 類型用於定義計數器欄位。計數器欄位是一個欄位,其值為 64 位元有號整數,且支援兩個運算:遞增和遞減(語法請參閱 UPDATE 陳述式)。請注意,無法設定計數器的值:計數器在第一次遞增/遞減之前不存在,而第一次遞增/遞減會視為先前的值為 0。

計數器有許多重要的限制

  • 無法用於表格 PRIMARY KEY 的欄位。

  • 包含計數器的表格只能包含計數器。換句話說,表格中 PRIMARY KEY 以外的所有欄位都必須有 counter 類型,或都沒有。

  • 計數器不支援 過期

  • 支援刪除計數器,但僅保證在您第一次刪除計數器時有效。換句話說,您不應重新更新已刪除的計數器(如果您這樣做,無法保證正確的行為)。

  • 計數器更新本質上並非 冪等。一個重要的後果是,如果計數器更新意外失敗(逾時或與協調器節點失去連線),客戶端無法得知更新是否已套用。特別是,重新執行更新可能會導致過度計數。

使用時間戳記

timestamp 類型的值編碼為 64 位元有號整數,代表自稱為 Epoch 時間 的標準基準時間(1970 年 1 月 1 日格林威治標準時間 00:00:00)開始經過的毫秒數。

時間戳記可以在 CQL 中輸入,使用其值作為 integer,或使用代表 ISO 8601 日期格式的 string。例如,以下所有值都是 2011 年 3 月 2 日格林威治標準時間上午 04:05:00 AM 的有效 timestamp

  • 1299038700000

  • '2011-02-03 04:05+0000'

  • '2011-02-03 04:05:00+0000'

  • '2011-02-03 04:05:00.000+0000'

  • '2011-02-03T04:05+0000'

  • '2011-02-03T04:05:00+0000'

  • '2011-02-03T04:05:00.000+0000'

上述的 +0000 是 RFC 822 4 位元時區規格;+0000 指的是格林威治標準時間。美國太平洋標準時間是 -0800。時區可以省略('2011-02-03 04:05:00'),如果是這樣,日期會被解釋為協調的 Cassandra 節點設定的時區。然而,依賴於預期的時區設定是有其困難度的,因此建議在可行的情況下,總是為時間戳記指定時區。

也可以省略時間('2011-02-03''2011-02-03+0000'),這種情況下,時間會預設為指定或預設時區的 00:00:00。但是,如果只有日期部分相關,請考慮使用 date 類型。

日期類型

date 類型的值編碼為 32 位元無號整數,代表自範圍中心(2^31)的「Epoch 時間」開始經過的天數。Epoch 時間為 1970 年 1 月 1 日

對於 時間戳記,日期可以輸入為 integer 或使用日期 string。在後者的情況下,格式應為 yyyy-mm-dd(例如 '2011-02-03')。

時間類型

time 類型的值編碼為 64 位元有號整數,代表自午夜開始經過的奈秒數。

對於 時間戳記,時間可以輸入為 整數 或使用表示時間的 字串。在後面的情況中,格式應為 hh:mm:ss[.fffffffff](其中次秒精度是可選的,如果提供,可以小於納秒)。因此,例如,以下是時間的有效輸入

  • '08:12:54'

  • '08:12:54.123'

  • '08:12:54.123456'

  • '08:12:54.123456789'

持續時間類型

持續時間 類型的值編碼為 3 個可變長度的有號整數。第一個整數表示月份數,第二個整數表示天數,第三個整數表示納秒數。這是因為一個月中天數會改變,而一天可以有 23 或 25 小時,具體取決於夏令時。在內部,月份和天數被解碼為 32 位元組整數,而納秒數被解碼為 64 位元組整數。

持續時間可以輸入為

  • (數量單位)+ 例如 12h30m,其中單位可以是

    • y:年(12 個月)

    • mo:月(1 個月)

    • w:週(7 天)

    • d:天(1 天)

    • h:小時(3,600,000,000,000 納秒)

    • m:分鐘(60,000,000,000 納秒)

    • s:秒(1,000,000,000 納秒)

    • ms:毫秒(1,000,000 納秒)

    • usµs:微秒(1000 納秒)

    • ns:納秒(1 納秒)

  • ISO 8601 格式:P[n]Y[n]M[n]DT[n]H[n]M[n]S 或 P[n]W

  • ISO 8601 替代格式:P[YYYY]-[MM]-[DD]T[hh]:[mm]:[ss]

例如

INSERT INTO RiderResults (rider, race, result)
   VALUES ('Christopher Froome', 'Tour de France', 89h4m48s);
INSERT INTO RiderResults (rider, race, result)
   VALUES ('BARDET Romain', 'Tour de France', PT89H8M53S);
INSERT INTO RiderResults (rider, race, result)
   VALUES ('QUINTANA Nairo', 'Tour de France', P0000-00-00T89:09:09);

持續時間欄不能用於表格的 PRIMARY KEY。此限制是因為持續時間無法排序。在沒有日期上下文的情況下,實際上無法知道 1mo 是否大於 29d

1d 持續時間不等於 24h 持續時間,因為持續時間類型被創建為能夠支援夏令時。

集合

CQL 支援三種類型的集合:映射集合清單。這些集合的類型由

collection_type::= MAP '<' cql_type',' cql_type'>'
	| SET '<' cql_type '>'
	| LIST '<' cql_type'>'

定義,其值可以使用集合文字輸入

collection_literal::= map_literal | set_literal | list_literal
map_literal::= '\{' [ term ':' term (',' term : term)* ] '}'
set_literal::= '\{' [ term (',' term)* ] '}'
list_literal::= '[' [ term (',' term)* ] ']'

但請注意,集合文字中不支援 bind_markerNULL

值得注意的特徵

集合用於儲存/反正規化相對較少量的資料。它們適用於「給定使用者的電話號碼」、「應用於電子郵件的標籤」等事物。但是,當預期項目會無限增加(「使用者傳送的所有訊息」、「感測器註冊的事件」……)時,集合就不適用了,應該使用特定表格(含分群欄位)。具體來說,(未凍結)集合具有下列值得注意的特徵和限制

  • 個別集合在內部沒有索引。這表示即使要存取集合的單一元素,也必須讀取整個集合(而且在內部讀取一個集合不會分頁)。

  • 雖然對集合和映射的插入作業在內部從不發生寫入前讀取,但對清單的某些作業會發生。此外,某些清單作業本質上不是冪等(請參閱下方 清單 區段的詳細資料),這會讓它們在逾時時重試變得有問題。因此,建議在可能的情況下優先使用集合而非清單。

請注意,雖然有些限制未來可能會移除或改善,但使用(單一)集合來儲存大量資料是一種反模式。

映射

map 是(已排序)的鍵值對集合,其中鍵是唯一的,且映射依其鍵排序。您可以使用下列方式定義和插入映射

CREATE TABLE users (
   id text PRIMARY KEY,
   name text,
   favs map<text, text> // A map of text keys, and text values
);

INSERT INTO users (id, name, favs)
   VALUES ('jsmith', 'John Smith', { 'fruit' : 'Apple', 'band' : 'Beatles' });

// Replace the existing map entirely.
UPDATE users SET favs = { 'fruit' : 'Banana' } WHERE id = 'jsmith';

此外,映射支援

  • 更新或插入一個或多個元素

    UPDATE users SET favs['author'] = 'Ed Poe' WHERE id = 'jsmith';
    UPDATE users SET favs = favs + { 'movie' : 'Cassablanca', 'band' : 'ZZ Top' } WHERE id = 'jsmith';
  • 移除一個或多個元素(如果元素不存在,移除它是不執行任何作業,但不會擲回錯誤)

    DELETE favs['author'] FROM users WHERE id = 'jsmith';
    UPDATE users SET favs = favs - { 'movie', 'band'} WHERE id = 'jsmith';

    請注意,對於移除 map 中的多個元素,您會從中移除一個鍵的 set

最後,INSERTUPDATE 都允許 TTL,但在兩種情況下,設定的 TTL 僅適用於新插入/更新的元素。換句話說

UPDATE users USING TTL 10 SET favs['color'] = 'green' WHERE id = 'jsmith';

只會將 TTL 套用於 { 'color' : 'green' } 記錄,映射的其餘部分不受影響。

集合

set 是(已排序)的唯一值集合。您可以使用下列方式定義和插入集合

CREATE TABLE images (
   name text PRIMARY KEY,
   owner text,
   tags set<text> // A set of text values
);

INSERT INTO images (name, owner, tags)
   VALUES ('cat.jpg', 'jsmith', { 'pet', 'cute' });

// Replace the existing set entirely
UPDATE images SET tags = { 'kitten', 'cat', 'lol' } WHERE name = 'cat.jpg';

此外,集合支援

  • 新增一個或多個元素(因為這是集合,所以插入已存在的元素是不執行任何作業)

    UPDATE images SET tags = tags + { 'gray', 'cuddly' } WHERE name = 'cat.jpg';
  • 移除一個或多個元素(如果元素不存在,移除它是一個空操作,但不會擲回錯誤)

    UPDATE images SET tags = tags - { 'cat' } WHERE name = 'cat.jpg';

最後,對於 集合,TTL 僅套用於新插入的值。

串列

如上所述,並在本節的最後進一步討論,串列有其限制和特定的效能考量,在使用它們之前,您應該將這些考量納入考量。一般來說,如果您能使用 集合 代替串列,請務必優先選擇集合。

串列 是非唯一值的(已排序)集合,其中元素依其在串列中的位置排序。您可以使用下列方式定義和插入串列

CREATE TABLE plays (
    id text PRIMARY KEY,
    game text,
    players int,
    scores list<int> // A list of integers
)

INSERT INTO plays (id, game, players, scores)
           VALUES ('123-afde', 'quake', 3, [17, 4, 2]);

// Replace the existing list entirely
UPDATE plays SET scores = [ 3, 9, 4] WHERE id = '123-afde';

此外,串列支援

  • 將值附加到串列的尾端和前端

    UPDATE plays SET players = 5, scores = scores + [ 14, 21 ] WHERE id = '123-afde';
    UPDATE plays SET players = 6, scores = [ 3 ] + scores WHERE id = '123-afde';
警告

附加和預先附加操作本質上並非冪等。因此,特別是如果其中一個操作逾時,則重試該操作並不安全,而且它可能會(或可能不會)導致附加/預先附加值兩次。

  • 設定串列中特定位置的值,該位置有預先存在的元素。如果串列沒有該位置,將擲回錯誤

    UPDATE plays SET scores[1] = 7 WHERE id = '123-afde';
  • 移除串列中特定位置的元素,該位置有預先存在的元素。如果串列沒有該位置,將擲回錯誤。此外,由於操作會從串列中移除元素,因此串列大小將減少一個元素,將所有後續元素的位置向前移動一個

    DELETE scores[1] FROM plays WHERE id = '123-afde';
  • 刪除串列中特定值所有的出現(如果特定元素根本沒有出現在串列中,它只會被忽略,而且不會擲回錯誤)

    UPDATE plays SET scores = scores - [ 12, 21 ] WHERE id = '123-afde';
警告

依位置設定和移除元素,以及移除特定值的出現,會產生內部讀取再寫入。這些操作執行速度會很慢,而且會比一般的更新使用更多資源(排除條件式寫入,條件式寫入有其自己的成本)。

最後,對於 串列,TTL 僅套用於新插入的值。

使用向量

向量是特定資料類型非空值的固定大小序列。它們使用與串列相同的字面值。

您可以使用下列方式定義、插入和更新向量

CREATE TABLE plays (
    id text PRIMARY KEY,
    game text,
    players int,
    scores vector<int, 3> // A vector of 3 integers
)

INSERT INTO plays (id, game, players, scores)
           VALUES ('123-afde', 'quake', 3, [17, 4, 2]);

// Replace the existing vector entirely
UPDATE plays SET scores = [ 3, 9, 4] WHERE id = '123-afde';

請注意,無法變更向量的個別值,而且無法選取向量的個別元素。

使用者定義類型 (UDT)

CQL 支援使用者定義類型 (UDT) 的定義。此類型的建立、修改和移除可以使用下面所述的 create_type_statementalter_type_statementdrop_type_statement

user_defined_type::= udt_name
udt_name::= [ keyspace_name '.' ] identifier

建立 UDT

建立新的使用者定義類型是使用 CREATE TYPE 陳述式,定義如下

create_type_statement::= CREATE TYPE [ IF NOT EXISTS ] udt_name
        '(' field_definition ( ',' field_definition)* ')'
field_definition::= identifier cql_type

UDT 有名稱(用於宣告該類型的欄位)和一組已命名且已鍵入的欄位。欄位名稱可以是任何類型,包括集合或其他 UDT。例如

CREATE TYPE phone (
    country_code int,
    number text,
);

CREATE TYPE address (
    street text,
    city text,
    zip text,
    phones map<text, phone>
);

CREATE TABLE user (
    name text PRIMARY KEY,
    addresses map<text, frozen<address>>
);

關於 UDT 應注意的事項

  • 嘗試建立已存在的類型會導致錯誤,除非使用 IF NOT EXISTS 選項。如果使用,如果類型已存在,陳述式將不會執行任何操作。

  • 類型本質上與其建立的鍵集空間繫結,而且只能在該鍵集空間中使用。建立時,如果類型名稱加上鍵集空間名稱的前置詞,則會在該鍵集空間中建立。否則,會在目前的鍵集空間中建立。

  • 自 Cassandra 起,UDT 在大多數情況下必須凍結,因此在上面的表格定義中有 frozen<address>

UDT 文字

建立使用者定義類型後,可以使用 UDT 文字輸入值

udt_literal::= '{' identifier ':' term ( ',' identifier ':' term)* '}'

換句話說,UDT 文字就像 映射` 文字,但其金鑰是類型的欄位名稱。例如,可以使用以下方式插入到前一節中定義的表格中

INSERT INTO user (name, addresses)
   VALUES ('z3 Pr3z1den7', {
     'home' : {
        street: '1600 Pennsylvania Ave NW',
        city: 'Washington',
        zip: '20500',
        phones: { 'cell' : { country_code: 1, number: '202 456-1111' },
                  'landline' : { country_code: 1, number: '...' } }
     },
     'work' : {
        street: '1600 Pennsylvania Ave NW',
        city: 'Washington',
        zip: '20500',
        phones: { 'fax' : { country_code: 1, number: '...' } }
     }
  }
);

要有效,UDT 文字只能包含其文字所屬類型的定義欄位,但可以省略一些欄位(這些欄位將設為 NULL)。

變更 UDT

可以使用 ALTER TYPE 陳述式修改現有的使用者定義類型

alter_type_statement::= ALTER TYPE [ IF EXISTS ] udt_name alter_type_modification
alter_type_modification::= ADD [ IF NOT EXISTS ] field_definition
        | RENAME [ IF EXISTS ] identifier TO identifier (AND identifier TO identifier )*

如果類型不存在,陳述式將傳回錯誤,除非使用 IF EXISTS,這種情況下,操作不會執行任何操作。您可以

  • 新增新欄位到類型(ALTER TYPE address ADD country text)。對於在新增之前建立的任何類型值,該新欄位將為 NULL。如果新欄位存在,陳述式將傳回錯誤,除非使用 IF NOT EXISTS,這種情況下,操作不會執行任何操作。

  • 重新命名類型的欄位。如果欄位不存在,陳述式將傳回錯誤,除非使用 IF EXISTS,這種情況下,操作不會執行任何操作。

ALTER TYPE address RENAME zip TO zipcode;

刪除 UDT

你可以使用 DROP TYPE 陳述式刪除現有的使用者定義類型

drop_type_statement::= DROP TYPE [ IF EXISTS ] udt_name

刪除類型會立即且不可逆地移除該類型。但是,嘗試刪除仍由其他類型、表格或函式使用的類型會導致錯誤。

如果刪除的類型不存在,系統會傳回錯誤,除非使用 IF EXISTS,這種情況下操作不會執行任何操作。

CQL 也支援組和組類型(其中元素可以是不同類型)。在功能上,組可以視為具有匿名欄位的匿名 UDT。組類型和組文字定義如下

tuple_type::= TUPLE '<' cql_type( ',' cql_type)* '>'
tuple_literal::= '(' term( ',' term )* ')'

,並且可以建立如下

CREATE TABLE durations (
  event text,
  duration tuple<int, text>,
);

INSERT INTO durations (event, duration) VALUES ('ev1', (3, 'hours'));

與其他複合類型(例如集合和 UDT)不同,組總是 frozen <frozen>(不需要 frozen 關鍵字),而且不可能只更新組的某些元素(而不更新整個組)。此外,組文字的數值應該總是與其所屬類型的宣告數值相同(其中一些數值可以為 null,但需要明確宣告為 null)。

自訂類型

自訂類型主要存在於向後相容性的目的,不建議使用。它們的使用很複雜,對使用者不友善,而且其他提供的類型,特別是 使用者定義類型,幾乎總是足夠的。

自訂類型定義如下

custom_type::= string

自訂類型是一個 string,其中包含延伸伺服器端 AbstractType 類別的 Java 類別名稱,而且 Cassandra 可以載入(因此它應該在執行 Cassandra 的每個節點的 CLASSPATH 中)。該類別將定義哪些值對類型有效,以及在用於叢集欄位時時間如何排序。對於任何其他目的,自訂類型的值與 blob 的值相同,特別是可以使用 blob 文字語法輸入。