数据类型
原生类型
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 字符串 |
|
|
64 位有符号长整型 |
|
|
任意字节(无验证) |
|
|
|
|
|
计数器列(64 位有符号值)。有关详细信息,请参阅 |
|
|
日期(没有对应的时间值)。有关详细信息,请参阅下面的 |
|
|
可变精度十进制 |
|
|
64 位 IEEE-754 浮点数 |
|
|
具有纳秒精度的持续时间。有关详细信息,请参阅下面的 |
|
|
32 位 IEEE-754 浮点数 |
|
|
IP 地址,可以是 IPv4(4 个字节长)或 IPv6(16 个字节长)。请注意,没有 |
|
|
32 位有符号整型 |
|
|
16 位有符号整型 |
|
|
UTF8 编码字符串 |
|
|
时间(没有对应的日期值),具有纳秒精度。有关详细信息,请参阅下面的 |
|
|
时间戳(日期和时间),具有毫秒精度。有关详细信息,请参阅下面的 |
|
|
版本 1 UUID,通常用作“无冲突”时间戳。另请参阅 |
|
|
8 位有符号整型 |
|
|
UUID(任何版本) |
|
|
UTF8 编码字符串 |
|
|
任意精度整数 |
|
|
固定长度非空,扁平化的浮点值数组 CASSANDRA-18504 将此数据类型添加到 Cassandra 5.0。 |
计数器
counter
类型用于定义计数器列。计数器列是其值为 64 位有符号整数的列,并且支持两种操作:递增和递减(有关语法,请参阅 UPDATE 语句)。请注意,无法设置计数器的值:计数器在第一次递增/递减之前不存在,并且第一次递增/递减被视为先前值为 0。
计数器有一些重要的限制
使用时间戳
timestamp
类型的值被编码为 64 位有符号整数,表示自标准基准时间(称为 纪元)以来的毫秒数:格林威治标准时间 1970 年 1 月 1 日 00:00:00。
时间戳可以在 CQL 中使用其值作为 integer
输入,也可以使用表示 ISO 8601 日期 的 string
输入。例如,以下所有值都是格林威治标准时间 2011 年 3 月 2 日上午 04:05:00 的有效 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)。纪元是 1970 年 1 月 1 日。
对于 时间戳,日期可以作为 integer
输入,也可以使用日期 string
输入。在后一种情况下,格式应为 yyyy-mm-dd
(例如 '2011-02-03'
)。
时间类型
time
类型的值被编码为 64 位有符号整数,表示自午夜以来的纳秒数。
对于 时间戳,时间可以作为 integer
输入,也可以使用表示时间的 string
输入。在后一种情况下,格式应为 hh:mm:ss[.fffffffff]
(其中亚秒精度是可选的,如果提供,可以小于纳秒)。因此,例如,以下时间是有效的输入
-
'08:12:54'
-
'08:12:54.123'
-
'08:12:54.123456'
-
'08:12:54.123456789'
持续时间类型
duration
类型的值被编码为 3 个可变长度的有符号整数。第一个整数表示月份数,第二个表示天数,第三个表示纳秒数。这是因为一个月的日数可能会变化,并且一天可能有 23 或 25 个小时,具体取决于夏令时。在内部,月份数和天数被解码为 32 位整数,而纳秒数被解码为 64 位整数。
持续时间可以作为以下内容输入:
-
(quantity unit)+
,例如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 支持三种类型的集合:maps
、sets
和 lists
。这些集合的类型由
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_marker
或 NULL
。
值得注意的特征
集合用于存储/反规范化相对少量的数据。它们适用于诸如“给定用户的电话号码”、“应用于电子邮件的标签”等情况。但是,当项目预计会无限制地增长(“用户发送的所有消息”、“传感器注册的事件”……)时,集合就不合适了,应该使用特定的表(带有聚类列)。具体来说,(非冻结)集合具有以下值得注意的特征和限制
-
单个集合在内部没有索引。这意味着即使要访问集合中的单个元素,也必须读取整个集合(并且内部不会对读取进行分页)。
-
虽然集合和映射上的插入操作在内部永远不会产生读前写,但列表上的某些操作会产生读前写。此外,某些列表操作本质上不是幂等的(有关详细信息,请参见下面关于列表的部分),这使得在超时的情况下重试它们成为问题。因此,建议尽可能地优先使用集合而不是列表。
请注意,虽然这些限制中的一些可能在将来被移除/改进,但使用(单个)集合来存储大量数据是一种反模式。
映射
一个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
的键。
最后,TTL 允许用于INSERT
和UPDATE
,但在两种情况下,设置的 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 仅适用于新插入的值。
列表
如上所述,并在本节末尾进一步讨论,列表具有限制和特定的性能注意事项,您在使用它们之前应该考虑这些因素。一般来说,如果您可以使用集合而不是列表,始终优先使用集合。 |
一个list
是一个(排序的)非唯一值的集合,其中元素按它们在列表中的位置排序。您可以使用以下方法定义和插入列表
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_statement
、alter_type_statement
和drop_type_statement
创建、修改和删除此类类型。但是,一旦创建,UDT 只通过其名称引用
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;
元组
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
关键字),并且无法仅更新元组的某些元素(无需更新整个元组)。此外,元组文字应始终具有与它所属的类型中声明的相同数量的值(其中一些值可以为空,但需要显式声明为这样)。
自定义类型
自定义类型主要用于向后兼容目的,不鼓励使用它们。它们的用法很复杂,不友好,其他提供的类型,特别是用户定义类型,几乎总是足够了。 |
自定义类型由以下内容定义
custom_type::= string
自定义类型是一个string
,它包含扩展服务器端AbstractType
类的 Java 类的名称,并且可以由 Cassandra 加载(因此它应该位于运行 Cassandra 的每个节点的CLASSPATH
中)。该类将定义哪些值为类型有效以及在用作聚类列时时间如何排序。对于任何其他目的,自定义类型的值与blob
的值相同,并且可以特别地使用blob
文字语法输入。