原始网页:https://www.cockroachlabs.com/docs/stable/partitioning.html


(New in v2.0) CockroachDB允许用户定义分区,能够获得行级别的控制数据存储位置的能力,通过分区功能也可以达到减少延迟、节约成本的效果,此外帮助用户满足在数据存储方面符合地方法律规定的需求。

NOTE: 该功能为企业版功能,需要企业版License

使用场景

分区能够减少延迟,节约成本:

工作原理

分区功能通过组合以下功能模块实现。

节点属性

配置地理分区或是规范分区,需要在节点启动的时候指定以下Flag:

企业版License

使用分区功能需要用户具备企业版License,关于获取并应用试用或是正式的企业版License,可查看企业版License

对于过期的企业版License,以下功能能够继续使用:

同时,用户不能再使用以下功能:

创建表

用户可以基于表其中一列或多列属性定义分区和子分区。在创建表期间,用户可以通过以下两种方式之一定义每个分区的值:

基于列表分区

用户可以使用PARTITION BY LIST语句在创建表的时候定义分区规则,对于不满足所有用户自定义分区规则的数据将映射到DEFAULT分区,详见Partition by List

基于范围分区

用户可以使用PARTITION BY RANGE语句在创建表的时候定义分区规则。使用该语句需要指定MINVALUEMAXVALUE参数,分别对应指定分区的可能值的最大值和最小值。

NOTE: 定义范围区间时包含最小值,不包含最大值。 与KEY编码排序和ORDER BY语句的逻辑一致,认为NULL值小于所有可能值。

分区范围的值可以使用SQL查询的查询结果值,但首次赋值后不再执行该SQL查询。例如用户在2017年1月30日使用< (now() - '1d')定义分区,对应着所有不晚于2017年1月29日的值,不会随着时间而改变分区范围。

基于主键建立分区

表分区使用的主键跟一般的主键有所不同。在定义表分区的联合主键的时候,需要将分区和子分区关联的主键提前,且分区主键前于子分区主键。 例如,考虑一个全球在线学习门户的数据库,该门户网站为世界各地所有课程的学生创建一张表。如果要根据学生所在国家对表数据进行地理分区,则需要将主键定义为:

CREATE TABLE students (
    id SERIAL,
    name STRING,
    email STRING,
    country STRING,
    expected_graduation_date DATE,   
    PRIMARY KEY (country, id));

用户须知

NOTE: v2.1版本后则允许修改主键。

基于二级索引建立分区

上述基于主键的分区有两个缺点:

解决以上问题的方式创建一个能够快速查找的唯一索引,并基于该二级索引建立分区。

值得注意的是,基于主键和基于该表的二级索引的所有分区的分区名不能重复。例如,以下分区定义的过程中,CREATE INDEX语句会因为重复分区名bar而执行失败:

CREATE TABLE foo (a STRING PRIMARY KEY, b STRING) PARTITION BY LIST (a) (
    bar VALUES IN ('bar'),
    default VALUES IN (DEFAULT)
);
CREATE INDEX foo_b_idx ON foo (b) PARTITION BY LIST (b) (
    baz VALUES IN ('baz'),
    default VALUES IN (DEFAULT)
);

用户需要定义一套命名规则避免命名重复,例如命名为primary_idx_barprimary_idx_defaultb_idx_bazb_idx_default

对交错表进行表分区

对于交错表的分区功能,目前只允许在父表定义分区,而子表与父表保持一致。

复制区域

对于满足分区规则的行将插入到对应的分区当中。用户可以针对分区创建和应用相应的复制区域。目前分区相较于表、索引或是数据库,是复制空间的最小粒度的配置单位。

示例

基于列表定义表分区

假设搭建一个全球在线学习门户网站RoachLearn,其数据库包含一张存储世界各地学生数据的表。用户将数据库部署在两个数据中心,一个在美国,另一个在澳大利亚。为了减少延迟,用户希望让学生的数据存储在地理上更接近他们的数据中心当中:

Step 1: 确定分区方式

此场景下,用户需要使用PARTITION BY LIST语句,基于country属性对学生表进行分区。

Step 2: 启动节点

需要在节点启动的时候使用--localityFlag指定节点对应的数据中心。

# Start the node in the US datacenter:
cockroach start --insecure --locality=datacenter=us1  --store=node1 --host=<node1 hostname> --port=26257 --http-port=8080 --join=<node1 hostname>:26257,<node2 hostname>:26258

# Start the node in the AUS datacenter:
cockroach start --insecure --locality=datacenter=au1 --store=node2 --host=<node2 hostname> --port=26258 --http-port=8081 --join=<node1 hostname>:26257,<node2 hostname>:26258

Step 3: 配置企业版License

详见配置企业版License

Step 4: 创建表

用户需要在创建表的同时定义分区:

CREATE TABLE students_by_list (
    id SERIAL,
    name STRING,
    email STRING,
    country STRING,
    expected_graduation_date DATE,   
    PRIMARY KEY (country, id))
    PARTITION BY LIST (country)
      (PARTITION north_america VALUES IN ('CA','US'),
      PARTITION australia VALUES IN ('AU','NZ'),
      PARTITION DEFAULT VALUES IN (default));

Step 5: 创建并应用区间配置

cat > north_america.zone.yml
constraints: [+datacenter=us1]
cat > australia.zone.yml
constraints: [+datacenter=au1]
cockroach zone set roachlearn.students_by_list.north_america --insecure  -f north_america.zone.yml

cockroach zone set roachlearn.students_by_list.australia --insecure  -f australia.zone.yml

Step 6: 验证表分区

SHOW EXPERIMENTAL_RANGES FROM TABLE students_by_list;

+-----------------+-----------------+----------+----------+--------------+
|    Start Key    |     End Key     | Range ID | Replicas | Lease Holder |
+-----------------+-----------------+----------+----------+--------------+
| NULL            | /"AU"           |      251 | {1,2,3}  |            1 |
| /"AU"           | /"AU"/PrefixEnd |      257 | {1,2,3}  |            1 |
| /"AU"/PrefixEnd | /"CA"           |      258 | {1,2,3}  |            1 |
| /"CA"           | /"CA"/PrefixEnd |      252 | {1,2,3}  |            1 |
| /"CA"/PrefixEnd | /"NZ"           |      253 | {1,2,3}  |            1 |
| /"NZ"           | /"NZ"/PrefixEnd |      256 | {1,2,3}  |            1 |
| /"NZ"/PrefixEnd | /"US"           |      259 | {1,2,3}  |            1 |
| /"US"           | /"US"/PrefixEnd |      254 | {1,2,3}  |            1 |
| /"US"/PrefixEnd | NULL            |      255 | {1,2,3}  |            1 |
+-----------------+-----------------+----------+----------+--------------+
(9 rows)

Time: 7.209032ms

基于范围定义表分区

该场景中,假定用户需要将当期学生的数据存储在快速且昂贵的存储设备(例如SSD)上,而将毕业学生的数据存储在较慢且较便宜的存储设备(例如HDD)上。

Step 1: 确定分区方式

用户需要使用PARTITION BY RANGE语句基于时间进行表分区。

Step 2: 配置企业版License

详见配置企业版License

Step 3: 启动节点

需要在节点启动的时候使用--storeFlag指定节点对应的存储设备。

cockroach start --insecure --store=path=/mnt/1,attrs=ssd --host=<node1 hostname> --port=26257 --http-port=8080 --join=<node1 hostname>:26257,<node2 hostname>:26258

cockroach start --insecure --store=path=/mnt/2,attrs=hdd --host=<node2 hostname> --port=26258 --http-port=8081 --join=<node1 hostname>:26257,<node2 hostname>:26258

Step 4: 创建表

用户需要在创建表的同时定义分区:

CREATE TABLE students_by_range (
   id SERIAL,
   name STRING,
   email STRING,                                                                                           
   country STRING,
   expected_graduation_date DATE,                                                                                      
   PRIMARY KEY (expected_graduation_date, id))
   PARTITION BY RANGE (expected_graduation_date)
      (PARTITION graduated VALUES FROM (MINVALUE) TO ('2017-08-15'),
      PARTITION current VALUES FROM ('2017-08-15') TO (MAXVALUE));

Step 5: 创建并应用区间配置

cat > current.zone.yml
constraints: [+ssd]
cat > graduated.zone.yml
constraints: [+hdd]
cockroach zone set roachlearn.students_by_range.current --insecure  -f current.zone.yml

cockroach zone set roachlearn.students_by_range.graduated --insecure  -f graduated.zone.yml

Step 6: 验证表分区

SHOW EXPERIMENTAL_RANGES FROM TABLE students_by_range;

+-----------+---------+----------+----------+--------------+
| Start Key | End Key | Range ID | Replicas | Lease Holder |
+-----------+---------+----------+----------+--------------+
| NULL      | /17393  |      244 | {1,2,3}  |            1 |
| /17393    | NULL    |      242 | {1,2,3}  |            1 |
+-----------+---------+----------+----------+--------------+
(2 rows)

Time: 5.850903ms

定义表的子分区

分区能被再分区,形成子分区,子分区的层次数量没有限制。

NOTE: 基于范围的分区也能创建子分区

在RoachLearn的场景中,假定用户需要实现:

Step 1: 确定分区方式

用户需要先进行地理分区,在依据时间进行子分区。

Step 2: 启动节点

使用--storeFlag指定节点使用的存储设备

cockroach start --insecure --host=<node1 hostname> --locality=datacenter=us1 --store=path=/mnt/1,attrs=ssd --store=path=/mnt/2,attrs=hdd \
cockroach start --insecure --host=<node2 hostname> --locality=datacenter=au1 --store=path=/mnt/3,attrs=ssd --store=path=/mnt/4,attrs=hdd --join=<node1 hostname>:26257
cockroach init --insecure --host=<node1 hostname>

Step 3: 配置企业版License

详见配置企业版License

Step 4: 创建表

用户需要在创建表的同时定义分区:

CREATE TABLE students (
    id SERIAL,
    name STRING,
    email STRING,
    country STRING,
    expected_graduation_date DATE,
    PRIMARY KEY (country, expected_graduation_date, id))
    PARTITION BY LIST (country)(
        PARTITION australia VALUES IN ('AU','NZ') PARTITION BY RANGE (expected_graduation_date)(PARTITION graduated_au VALUES FROM (MINVALUE) TO ('2017-08-15'), PARTITION current_au VALUES FROM ('2017-08-15') TO (MAXVALUE)),
        PARTITION north_america VALUES IN ('US','CA') PARTITION BY RANGE (expected_graduation_date)(PARTITION graduated_us VALUES FROM (MINVALUE) TO ('2017-08-15'), PARTITION current_us VALUES FROM ('2017-08-15') TO (MAXVALUE))
    );

分区和子分区的分区名不允许重复,因此推荐用户定义一套命名规则命名分区,例如使用graduated_augraduated_usand current_aucurrent_us

Step 5: 创建并应用区间配置

cat > current_us.zone.yml
constraints: [+ssd,+datacenter=us1]
cat > graduated_us.zone.yml
constraints: [+hdd,+datacenter=us1]
cat > current_au.zone.yml
constraints: [+ssd,+datacenter=au1]
cat > graduated_au.zone.yml
constraints: [+hdd,+datacenter=au1]
cockroach zone set roachlearn.students.current_us --insecure -f current_us.zone.yml

cockroach zone set roachlearn.students.graduated_us --insecure -f graduated_us.zone.yml

cockroach zone set roachlearn.students.current_au --insecure -f current_au.zone.yml

cockroach zone set roachlearn.students.graduated_au --insecure -f graduated_au.zone.yml

Step 6: 验证表分区

SHOW EXPERIMENTAL_RANGES FROM TABLE students;

+-----------------+-----------------+----------+----------+--------------+
|    Start Key    |     End Key     | Range ID | Replicas | Lease Holder |
+-----------------+-----------------+----------+----------+--------------+
| NULL            | /"AU"           |      260 | {1,2,3}  |            1 |
| /"AU"           | /"AU"/17393     |      268 | {1,2,3}  |            1 |
| /"AU"/17393     | /"AU"/PrefixEnd |      266 | {1,2,3}  |            1 |
| /"AU"/PrefixEnd | /"CA"           |      267 | {1,2,3}  |            1 |
| /"CA"           | /"CA"/17393     |      265 | {1,2,3}  |            1 |
| /"CA"/17393     | /"CA"/PrefixEnd |      261 | {1,2,3}  |            1 |
| /"CA"/PrefixEnd | /"NZ"           |      262 | {1,2,3}  |            3 |
| /"NZ"           | /"NZ"/17393     |      284 | {1,2,3}  |            3 |
| /"NZ"/17393     | /"NZ"/PrefixEnd |      282 | {1,2,3}  |            3 |
| /"NZ"/PrefixEnd | /"US"           |      283 | {1,2,3}  |            3 |
| /"US"           | /"US"/17393     |      281 | {1,2,3}  |            3 |
| /"US"/17393     | /"US"/PrefixEnd |      263 | {1,2,3}  |            1 |
| /"US"/PrefixEnd | NULL            |      264 | {1,2,3}  |            1 |
+-----------------+-----------------+----------+----------+--------------+
(13 rows)

Time: 11.586626ms

修改表分区

在RoachLearn场景中,假定学生表已经基于时间范围进行分区,将当期学生存储在快速且昂贵的存储设备(例如:SSD)上,将毕业学生的数据存储在速度较慢且低廉的存储设备上(例如:HDD)。现在用户需要变更时间范围,定义当期学生为2018-08-15之后,则需要使用ALTER TABLEPARTITION BY语句修改分区规则。

ALTER TABLE students_by_range PARTITION BY RANGE (expected_graduation_date) (
    PARTITION graduated VALUES FROM (MINVALUE) TO ('2018-08-15'),
    PARTITION current VALUES FROM ('2018-08-15') TO (MAXVALUE));

取消分区

使用PARTITION BY NOTHING取消指定表的分区。

ALTER TABLE students PARTITION BY NOTHING;

性能与可靠性之间的权衡

在提高读/写性能和集群可靠性上存在权衡,假设澳大利亚学生的数据在数据冗余上配置了3份副本。

对比其他数据库

其他数据库的分区功能相较于CockroachDB,拥有3个额外的使用场景: