准备

按照Cassandra集群部署搭建两台测试机,环境信息如下:

名称 IP 数据中心名称
node-01 192.168.198.130 datacenter1
node-02 192.168.198.131 datacenter1

Keyspace

创建Keyspace

1
create_keyspace_statement ::=  CREATE KEYSPACE [ IF NOT EXISTS ] keyspace_name WITH options

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 使用SimpleStrategy复制策略
CREATE KEYSPACE excelsior
WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 3};

## 使用NetworkTopologyStrategy复制策略
# 1. 确认分区名称
$ nodetool status
Datacenter: datacenter1
...

# 2. 使用NetworkTopologyStrategy复制策略创建keyspace
CREATE KEYSPACE excalibur
WITH replication = {'class': 'NetworkTopologyStrategy', 'DC1' : 1, 'DC2' : 3}
AND durable_writes = false;

使用Keyspace

1
use_statement ::=  USE keyspace_name

修改Keyspace(replication factor)

1
alter_keyspace_statement ::=  ALTER KEYSPACE keyspace_name WITH options

示例:

1
2
ALTER KEYSPACE excelsior 
WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 4};

查看Keyspace

1
DESCRIBE KEYSPACE <keyspace name>;

使用该语句查看创建的键空间是否正确:

1
2
3
DESCRIBE KEYSPACE excelsior;

CREATE KEYSPACE excelsior WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 3} AND durable_writes = true;

删除Keyspace

1
drop_keyspace_statement ::=  DROP KEYSPACE [ IF EXISTS ] keyspace_name
1
2
3
4
DROP KEYSPACE excelsior;
DESCRIBE excelsior;

'excelsior' not found in keyspaces

Table

创建Table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
create_table_statement ::=  CREATE TABLE [ IF NOT EXISTS ] table_name
'('
column_definition
( ',' column_definition )*
[ ',' PRIMARY KEY '(' primary_key ')' ]
')' [ WITH table_options ]
column_definition ::= column_name cql_type [ STATIC ] [ PRIMARY KEY]
primary_key ::= partition_key [ ',' clustering_columns ]
partition_key ::= column_name
| '(' column_name ( ',' column_name )* ')'
clustering_columns ::= column_name ( ',' column_name )*
table_options ::= COMPACT STORAGE [ AND table_options ]
| CLUSTERING ORDER BY '(' clustering_order ')' [ AND table_options ]
| options
clustering_order ::= column_name (ASC | DESC) ( ',' column_name (ASC | DESC) )*

创建Table必须指定主键,主键是用于在表中唯一标识某一行,可以是一列或多列。

示例,在excelsior键空间创建一张名为excelsior_alt_stats 的表:

1
2
3
4
5
6
7
8
CREATE TABLE excelsior.excelsior_alt_stats (
id UUID PRIMARY KEY,
lastname text,
birthday timestamp,
nationality text,
weight text,
height text
);

cassandra还支持collection(map, set, 或者 list)类型作为列:

1
2
3
4
5
6
7
CREATE TABLE excelsior.whimsey ( 
id UUID PRIMARY KEY,
lastname text,
excelsior_teams set<text>,
events list<text>,
teams map<int,text>
);

甚至是嵌套的元组类型(tuple):

1
2
3
4
5
6
7
CREATE TABLE excelsior.route (
race_id int,
race_name text,
point_id int,
lat_long tuple<text, tuple<float,float>>,
PRIMARY KEY (race_id, point_id)
);

更多数据类型请参阅下一节Cassandra数据结构

静态列

某些列可以在表定义中声明为STATIC。静态的列将由属于同一分区(具有相同分区键)的所有行“共享”。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE TABLE t (
pk int,
t int,
v text,
s text static,
PRIMARY KEY (pk, t)
);

INSERT INTO t (pk, t, v, s) VALUES (0, 0, 'val0', 'static0');
INSERT INTO t (pk, t, v, s) VALUES (0, 1, 'val1', 'static1');

SELECT * FROM t;
pk | t | v | s
----+---+--------+-----------
0 | 0 | 'val0' | 'static1'
0 | 1 | 'val1' | 'static1'

## 所有记录中的静态列将永远展示最后一次更新的值

修改Table

1
2
3
4
alter_table_statement   ::=  ALTER TABLE table_name alter_table_instruction
alter_table_instruction ::= ADD column_name cql_type ( ',' column_name cql_type )*
| DROP column_name ( column_name )*
| WITH options

示例:

1
2
3
4
ALTER TABLE addamsFamily ADD gravesite varchar;

ALTER TABLE addamsFamily
WITH comment = 'A most excellent and useful table';

修改Table可以:

  1. 向表中添加新列(通过ADD指令)。请注意,无法更改表的主键,因此新添加的列将不会成为主键的一部分。
  2. 从表中删除列。这会丢弃列及其所有内容。
  3. 更改一些表选项(通过WITH指令)。支持的选项与创建表时相同(在创建后无法更改的COMPACT STORAGE和CLUSTERING ORDER之外)。

删除Table

1
drop_table_statement ::=  DROP TABLE [ IF EXISTS ] table_name

截断Table(清空表数据)

1
truncate_statement ::=  TRUNCATE [ TABLE ] table_name

由于表是唯一可以在当前截断的对象,因此可以省略TABLE关键字。

截断表会永久删除表中的所有现有数据,但不会删除表本身。

Cassandra数据结构

CQL是一种类型化语言,支持丰富的数据类型集,包括本地类型,集合类型,用户定义类型,元组类型和自定义类型:

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

本地类型(Native Types)

类型 常量支持 说明
ascii string ASCII字符串
bigint integer 64位无符号整数
blob blob 任意字节(无验证)
boolean boolean truefalse
counter integer 计数器列(64位有符号值)
date integerstring 日期(没有相应的时间值)
decimal integerfloat 十进制可变精度
double integer float 64位IEEE-754浮点
duration duration 持续时间(纳秒精度)
float integerfloat 32位IEEE-754浮点
inet string IP地址,IPv4(4字节长)或IPv6(16字节长)
int integer 32位无符号整数
smallint integer 16位有符号整数
text string UTF8编码的字符串
time integerstring 具有纳秒精度的时间(没有相应的日期值)
timestamp integerstring 时间戳(日期和时间),精度为毫秒
timeuuid uuid UUID(版本1),通常用作“无冲突”时间戳
tinyint integer 8位有符号整数
uuid uuid 一个UUID(任何版本)
varchar string UTF8编码的字符串
varint integer 任意精度整数

其中需要注意的是时间类型:

timestamps

时间戳类型的值被编码为64位有符号整数,表示自标准基准时间(称为纪元:1970年1月1日格林威治标准时间00:00:00)以来的毫秒数。

  • 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'

例如:

1
2
3
SELECT *
FROM point
WHERE ts = '2018-11-15 00:00:30.557+0000';

或者

1
2
3
SELECT *
FROM point
WHERE ts = 1542211230557;

其中,+0000是RFC 822 4-digit时区规范,+0000指GMT。美国太平洋标准时间为-0800,中国北京标准时间为+8000,官方建议每次插入查询都带上时区,不加的话,默认是使用Cassandra节点配置的时区,可能会出现时区不一致导致的查询失败问题。

dates

日期类型的值被编码为32位无符号整数,表示在该范围的中心处具有“纪元”的天数(2^31)。大纪元是1970年1月1日。

至于时间戳,日期可以作为整数或使用日期字符串输入。在后一种情况下,格式应为yyyy-mm-dd(例如’2011-02-03’)。

times

时间类型的值被编码为64位有符号整数,表示自午夜以来的纳秒数。

对于时间戳,可以以整数或表示时间的字符串的形式输入时间。在后一种情况下,格式应为hh:mm:ss [.fffffffff](其中亚秒精度是可选的,如果提供,则可以小于纳秒)。例如,以下是一段时间内的有效输入:

  • '08:12:54'
  • '08:12:54.123'
  • '08:12:54.123456'
  • '08:12:54.123456789'

durations

持续时间类型的值被编码为3个有符号整数的可变长度。这是因为一个月的天数可以改变,一天可以有23或25小时,具体取决于夏令时。

  • 第一个整数表示月数(32位整数)

  • 第二个表示天数(32位整数)

  • 第三个表示纳秒数(64位整数)

  1. 支持的单位:
  • 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 or µs : 微妙(1000 纳)
  • ns: 纳秒(1 纳)
  1. ISO 8601格式:P[n]Y[n]M[n]DT[n]H[n]M[n]S or P[n]W
  2. ISO 8601替代格式:P[YYYY]-[MM]-[DD]T[hh]:[mm]:[ss]

插入示例:

1
2
3
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);

持续时间列不能作为主键。这是由于无法精确确认持续时间。如果没有日期上下文,实际上不可能知道1个月是否大于29天。

1天的持续时间也不等于24h,因为持续时间类型需要支持夏令时。

集合类型(Collections)

cassandra支持三种类型的集合:Maps, Sets and Lists

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

可以这样输入集合类型的数据:

1
2
3
4
collection_literal ::=  map_literal | set_literal | list_literal
map_literal ::= '{' [ term ':' term (',' term : term)* ] '}'
set_literal ::= '{' [ term (',' term)* ] '}'
list_literal ::= '[' [ term (',' term)* ] ']'

Maps

Maps是一组(有序)键值对,其中键是唯一的,并且按其键排序。

1
2
3
4
5
6
7
8
9
10
11
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';

另外,Maps还具有一些高级特性:

  • 更新或插入一个或多个元素
1
2
UPDATE users SET favs['author'] = 'Ed Poe' WHERE id = 'jsmith';
UPDATE users SET favs = favs + { 'movie' : 'Cassablanca', 'band' : 'ZZ Top' } WHERE id = 'jsmith';
  • 删除一个或多个元素(如果一个元素不存在,删除它是一个无效操作但不会抛出错误)
1
2
DELETE favs['author'] FROM users WHERE id = 'jsmith';
UPDATE users SET favs = favs - { 'movie', 'band'} WHERE id = 'jsmith';

Sets

Sets是唯一值的(已排序)集合。

1
2
3
4
5
6
7
8
9
10
11
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';

另外,Sets也具有一些高级特性:

  • 添加一个或多个元素(因为这是一个集合,插入一个已存在的元素是一个无效操作)
1
UPDATE images SET tags = tags + { 'gray', 'cuddly' } WHERE name = 'cat.jpg';
  • 删除一个或多个元素(如果一个元素不存在,删除它是一个无效操作但不会抛出错误)
1
UPDATE images SET tags = tags - { 'cat' } WHERE name = 'cat.jpg';

Lists

Lists是非唯一值的(已排序)集合,其中元素按列表中的位置排序。它与Sets的区别就在于是否是唯一值。

1
2
3
4
5
6
7
8
9
10
11
12
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';

另外,Lists同样也具有一些高级特性:

  • 在列表头或尾添加元素
1
2
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';

💡该操作不是幂等的,特别是在其中一个操作超时时,重试操作是不安全的,可能会导致同一数据插入两次。

  • 在列表中指定下标处设置值。该列表必须长度大于此下标,否则将抛出列表太小的错误
1
UPDATE plays SET scores[1] = 7 WHERE id = '123-afde';
  • 通过列表指定下标删除元素。该列表必须长度大于此下标,否则将抛出列表太小的错误。此外,当操作从列表中删除元素时,列表大小将减1,从而改变此下标之后所有元素的位置
1
DELETE scores[1] FROM plays WHERE id = '123-afde';
  • 删除列表中指定下标之间的所有元素
1
UPDATE plays SET scores = scores - [ 12, 21 ] WHERE id = '123-afde';

💡以上2,3,4操作会出现内部的 read-before-write,会比通常的更新消耗更多的资源,所以尽量使用Sets代替Lists。

用户自定义类型(User-Defined Types)

CQL支持用户定义类型(以下简称UDT)。可以使用下面create_type_statementalter_type_statementdrop_type_statement创建,修改和删除此类型。

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

创建

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

UDT有一个名称(用于声明该类型的列),是一组命名和类型字段。字段名称可以是任何类型,包括集合或其他UDT。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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>>
)

💡注意

  • 尝试创建现有类型时请使用IF NOT EXISTS选项,否则将会抛出错误。
  • UDT本质上绑定到创建它的键空间,并且只能在该键空间中使用。在创建时,如果类型名称以键空间名称为前缀,则在该键空间中创建它。否则,它将在当前键空间中创建。
  • 从Cassandra 4.0开始,在大多数情况下必须冻结UDT,因此在上面的表定义中冻结了<address>。有关详细信息,请参阅冻结部分。

修改

1
2
3
alter_type_statement    ::=  ALTER TYPE udt_name alter_type_modification
alter_type_modification ::= ADD field_definition
| RENAME identifier TO identifier ( identifier TO identifier )*

修改一个UDT,可以:

  1. 在类型中添加一个新字段
1
ALTER TYPE address ADD country text

请注意:新添加的字段在之前的记录中,都将被置为NULL。

  1. 重命名该类型的字段
1
ALTER TYPE address RENAME zip TO zipcode

删除

1
drop_type_statement ::=  DROP TYPE [ IF EXISTS ] udt_name

使用

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

使用UDT有点像Maps,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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: '...' } }
}
})

元组(Tuples)

CQL还支持元组和元组类型(元素可以是不同类型),类似于匿名的UDT或者是Scala的Tuple类型。

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

例如:

1
2
3
4
5
6
CREATE TABLE durations (
event text,
duration tuple<int, text>,
)

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

自定义类型(Custom Types)

自定义类型主要是为了兼容老项目,不建议使用。使用已有的类型加上用户自定义类型(UDT)就够了。

1
custom_type ::=  string

数据增删改查(CRUD)

SELECT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
select_statement ::=  SELECT [ JSON | DISTINCT ] ( select_clause | '*' )
FROM table_name
[ WHERE where_clause ]
[ GROUP BY group_by_clause ]
[ ORDER BY ordering_clause ]
[ PER PARTITION LIMIT (integer | bind_marker) ]
[ LIMIT (integer | bind_marker) ]
[ ALLOW FILTERING ]
select_clause ::= selector [ AS identifier ] ( ',' selector [ AS identifier ] )
selector ::= column_name
| term
| CAST '(' selector AS cql_type ')'
| function_name '(' [ selector ( ',' selector )* ] ')'
| COUNT '(' '*' ')'
where_clause ::= relation ( AND relation )*
relation ::= column_name operator term
'(' column_name ( ',' column_name )* ')' operator tuple_literal
TOKEN '(' column_name ( ',' column_name )* ')' operator term
operator ::= '=' | '<' | '>' | '<=' | '>=' | '!=' | IN | CONTAINS | CONTAINS KEY
group_by_clause ::= column_name ( ',' column_name )*
ordering_clause ::= column_name [ ASC | DESC ] ( ',' column_name [ ASC | DESC ] )*

示例:

1
2
3
4
5
6
7
8
9
10
11
SELECT name, occupation FROM users WHERE userid IN (199, 200, 207);
SELECT JSON name, occupation FROM users WHERE userid = 199;
SELECT name AS user_name, occupation AS user_occupation FROM users;

SELECT time, value
FROM events
WHERE event_type = 'myEvent'
AND time > '2011-02-03'
AND time <= '2012-01-01'

SELECT COUNT (*) AS user_count FROM users;

Allowing filtering

默认情况下,CQL仅允许不涉及“过滤”服务器端的选择查询,原因是那些“非过滤”查询具有可预测的性能,因为它们的查询性能与Limit成比例。

举个例子:

1
2
3
4
5
6
7
8
9
CREATE TABLE users (
username text PRIMARY KEY,
firstname text,
lastname text,
birth_year int,
country text
)

CREATE INDEX ON users(birth_year);

以下两种查询是不需要添加ALLOW FILTERING的:

1
2
SELECT * FROM users;
SELECT * FROM users WHERE birth_year = 1981;

因为在这两种情况下,Cassandra都保证这些查询性能与返回的数据量成正比。

而下面的这个查询,则需要强制添加:

1
SELECT * FROM users WHERE birth_year = 1981 AND country = 'FR' ALLOW FILTERING;

👉🏼 关于如何定义可预测的列,可参考Cassandra中的索引

INSERT

1
2
3
4
5
6
insert_statement ::=  INSERT INTO table_name ( names_values | json_clause )
[ IF NOT EXISTS ]
[ USING update_parameter ( AND update_parameter )* ]
names_values ::= names VALUES tuple_literal
json_clause ::= JSON string [ DEFAULT ( NULL | UNSET ) ]
names ::= '(' column_name ( ',' column_name )* ')'

示例:

1
2
3
4
5
6
7
INSERT INTO NerdMovies (movie, director, main_actor, year)
VALUES ('Serenity', 'Joss Whedon', 'Nathan Fillion', 2005)
USING TTL 86400;

INSERT INTO NerdMovies JSON '{"movie": "Serenity",
"director": "Joss Whedon",
"year": 2005}';

💡请注意

  1. 与SQL不同,INSERT默认情况下不检查行的先前存在:如果之前不存在,则创建行,否则更新。此外,没有办法知道发生了哪些创建或更新。

  2. 如果要做到存在则不更新,可以使用IF NOT EXISTS条件。但请注意,使用IF NOT EXISTS将导致不可忽略的性能成本(内部使用Paxos),因此应谨慎使用。

  3. INSERT的所有更新都以原子方式单独应用。

UPDATE

1
2
3
4
5
6
7
8
9
10
11
12
13
update_statement ::=  UPDATE table_name
[ USING update_parameter ( AND update_parameter )* ]
SET assignment ( ',' assignment )*
WHERE where_clause
[ IF ( EXISTS | condition ( AND condition )*) ]
update_parameter ::= ( TIMESTAMP | TTL ) ( integer | bind_marker )
assignment ::= simple_selection '=' term
| column_name '=' column_name ( '+' | '-' ) term
| column_name '=' list_literal '+' column_name
simple_selection ::= column_name
| column_name '[' term ']'
| column_name '.' `field_name
condition ::= simple_selection operator term

示例:

1
2
3
4
5
6
7
8
9
10
UPDATE NerdMovies USING TTL 400
SET director = 'Joss Whedon',
main_actor = 'Nathan Fillion',
year = 2005
WHERE movie = 'Serenity';

UPDATE UserActions
SET total = total + 2
WHERE user = B70DE1D0-9908-4AE3-BE34-5573E5B09F14
AND action = 'click';

💡请注意

  1. 与SQL不同,UPDATE默认情况下不检查行的先前存在(除非通过IF):如果之前不存在,则创建行,否则更新。此外,没有办法知道是否发生了创建或更新。

  2. 可以通过IF在某些列上使用条件,在这种情况下,除非满足条件,否则不会更新行。但请注意,使用IF条件会产生不可忽视的性能成本(内部使用Paxos),因此应谨慎使用。

  3. 在UPDATE语句中,同一分区键中的所有更新都以原子方式单独应用。

此外,UPDATE操作针对某些数据类型有强制性要求:

  1. c = c + 3

用于递增/递减计数器。

‘=’符号后面的列名称必须与’=’符号前面的列名相同。请注意,仅在计数器上允许递增/递减,并且是计数器上允许的唯一更新操作

  1. id = id + <some-collection>id[value1] = value2

用于集合。

  1. id.field = 3

在非冻结的用户定义类型上设置字段的值。

DELETE

1
2
3
4
5
delete_statement ::=  DELETE [ simple_selection ( ',' simple_selection ) ]
FROM table_name
[ USING update_parameter ( AND update_parameter )* ]
WHERE where_clause
[ IF ( EXISTS | condition ( AND condition )*) ]

示例:

1
2
3
4
5
DELETE FROM NerdMovies USING TIMESTAMP 1240003134
WHERE movie = 'Serenity';

DELETE phone FROM Users
WHERE userid IN (C73DE1D3-AF08-40F3-B124-3FF3E5109F22, B70DE1D0-9908-4AE3-BE34-5573E5B09F14);

💡请注意

  1. WHERE子句指定要删除的行。使用IN运算符可以使用一个语句删除多行。可以使用不等运算符(例如>=)删除一系列行。

  2. 在DELETE语句中,同一分区键中的所有删除都以原子方式单独应用。

  3. DELETE操作可以通过使用IF子句来条件化,类似于UPDATE和INSERT语句。但是,与INSERT和UPDATE语句一样,这将导致不可忽略的性能成本(内部,将使用Paxos),因此应谨慎使用。

批处理

批处理只允许包含UPDATE,INSERT和DELETE语句。

批处理节省客户端和服务器之间的网络资源消耗。

1
2
3
4
5
batch_statement        ::=  BEGIN [ UNLOGGED | COUNTER ] BATCH
[ USING update_parameter ( AND update_parameter )* ]
modification_statement ( ';' modification_statement )*
APPLY BATCH
modification_statement ::= insert_statement | update_statement | delete_statement

示例

1
2
3
4
5
6
BEGIN BATCH
INSERT INTO users (userid, password, name) VALUES ('user2', 'ch@ngem3b', 'second user');
UPDATE users SET password = 'ps22dhds' WHERE userid = 'user3';
INSERT INTO users (userid, password) VALUES ('user4', 'ch@ngem3c');
DELETE name FROM users WHERE userid = 'user1';
APPLY BATCH;

💡请注意

  1. 属于给定分区键的BATCH中的所有更新都是单独执行的。
  2. 默认情况下,批处理中的所有操作都按记录执行,以确保所有变更都最终完成(或不执行任何操作)。类似于SQL事务,但不完全等同于SQL事务。

UNLOGGED batches

默认情况下,Cassandra使用批处理日志来确保所有变更都最终完成(或不执行任何操作)【请注意,操作仅在单个分区中隔离】。

批处理跨越多个分区时,批处理在性能上会有所损失。可以使用UNLOGGED选项来跳过批处理日志,不过,如果批处理失败,可能会造成批处理中的任务部分成功部分失败,请谨慎选择。

COUNTER batches

使用COUNTER选项进行批量计数器更新。

与Cassandra中的其他更新不同,计数器更新不是幂等的。

参考资源

Apache Cassandra Documentation