Cassandra学习笔记

准备

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

名称IP数据中心名称
node-01192.168.198.130datacenter1
node-02192.168.198.131datacenter1

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)

类型常量支持说明
asciistringASCII字符串
bigintinteger64位无符号整数
blobblob任意字节(无验证)
booleanbooleantruefalse
counterinteger计数器列(64位有符号值)
dateintegerstring日期(没有相应的时间值)
decimalintegerfloat十进制可变精度
doubleinteger float64位IEEE-754浮点
durationduration持续时间(纳秒精度)
floatintegerfloat32位IEEE-754浮点
inetstringIP地址,IPv4(4字节长)或IPv6(16字节长)
intinteger32位无符号整数
smallintinteger16位有符号整数
textstringUTF8编码的字符串
timeintegerstring具有纳秒精度的时间(没有相应的日期值)
timestampintegerstring时间戳(日期和时间),精度为毫秒
timeuuiduuidUUID(版本1),通常用作“无冲突”时间戳
tinyintinteger8位有符号整数
uuiduuid一个UUID(任何版本)
varcharstringUTF8编码的字符串
varintinteger任意精度整数

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

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