1.Base基础/3.Icon图标/操作/search备份
1.Base基础/3.Icon图标/操作/search备份
EN
文档
关于AntDB
部署与升级
快速入门
使用教程
SQL语言
Oracle兼容
驱动使用说明
运维
调优
工具和插件
高级服务
数据安全
参考
  • 文档首页 /
  • 使用教程 /
  • 分布式功能 /
  • 表分布

表分布

更新时间:2024-07-01 14:39:47

简介

AntDB 集群的数据可以分布在多个 datanode 上,创建表的时候可以同时指定分布方式和分布节点。默认是 hash 分布方式,分布到所有节点。

CREATE TABLE table_name ( [
  { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
    | table_constraint
    | LIKE source_table [ like_option ... ] }
    [, ... ]
] )
[DISTRIBUTE BY {replication | hash(column_name) | random | modulo(column_name)}]
[to node (datanode_name1, datanode_name2 [, ... ]);

通过 DISTRIBUTE BY 指定的分片方式以及 to node 指定的分布节点,将一行一行表数据按照一定的计算方法分散在多个数据存储节点中。

指定分布节点

创建表的时候,可以指定分布的节点,这里的节点指的是 datanode。例如,集群有 3 个 datanode,则创建表的时候,可以显式指定表分布到 3 个或者 2 个 datanode 上,不支持指定到 1 个 datanode 上。默认是分布到所有的 datanode 上。

示例,集群有 3 个 datanode,创建表的时候指定数据分布到节点 dn1 和 dn2 上,采用默认的分布方式 hash,关于分布方式将在下一个小节介绍:

CREATE TABLE t1(id int, num int, des text) to node(dn1, dn2);
INSERT INTO t1 VALUES(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8);

查看数据在 dn1 和 dn2 上的分布情况,发现其中 1、2、5、6、8 存放在 dn1 上,3、4、5 存放在 dn2 上:

antdb=# EXECUTE DIRECT ON  (dn1) 'SELECT * FROM  t1';
 id | num | des 
----+-----+-----
  1 |   1 | 
  2 |   2 | 
  5 |   5 | 
  6 |   6 | 
  8 |   8 | 
(5 rows)

antdb=# EXECUTE DIRECT ON  (dn2) 'SELECT * FROM  t1';
 id | num | des 
----+-----+-----
  3 |   3 | 
  4 |   4 | 
  7 |   7 | 
(3 rows)

指定分布方式

AntDB 支持四种分布方式:

  • hash(列名):哈希分布,根据列的值做哈希计算,来决定当前 tuple 分布到哪个节点上,默认的分布方式;
  • replicaiton:复制分布,每一个节点都存一个副本;
  • modulo:取模分布;
  • random:随机分布,随机分布到某一个节点上;

用户可以在建表语句 CREATE TABLE 中使用 DISTRIBUTE BY 关键字来指定分片方式。除复制表外,其他三种数据分片方式都是基于键的分片。比如 Hash 分片的原理是使用新写入数据的某个键值 —— 比如客户 ID、客户端 IP、ZIP 码等等 —— 通过哈希函数决定保存的数据节点(也就是分片位置)。

哈希分布

哈希分布是指对分片键的数据计算hash值,根据计算出的 hash 值来决定数据分布到哪个存储节点,即 datanode 节点。对于哈希表,要选取一个分片键。一张分布式表只能有一个分片键,分片键是用于划分和定位表的列,不能修改,默认的分布列是第一列。

以下两种写法是等效的:

CREATE TABLE t1(id int, num int, des text) DISTRIBUTE BY hash(id);
CREATE TABLE t1(id int, num int, des text) ;

如果某一列上有 primary key 或者 unique 约束,则会把唯一列默认作为分片键。如下,在 num 列上有主键约束,则默认会发 num 列作为分片键。

CREATE TABLE t1(id int, num int primary key, des text);
\d+ t1
antdb=# \d+ t1
                                     Table "public.t1"
 Column |  Type   | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+---------+-----------+----------+---------+----------+--------------+-------------
 id     | integer |           |          |         | plain    |              | 
 num    | integer |           | not null |         | plain    |              | 
 des    | text    |           |          |         | extended |              | 
Indexes:
    "t1_pkey" PRIMARY KEY, btree (num)
DISTRIBUTE BY HASH(num) TO NODE(dm1, dm2, dm3, dm4, dm5, dm6)
Access method: heap

以下两种写法等效:

CREATE TABLE t1(id int, num int primary key, des text);
CREATE TABLE t1(id int, num int , des text) DISTRIBUTE BY hash(num);

注意: 在 AntDB 分布式版本上,分片列和唯一列必须为同一列,否则报错:

antdb=# CREATE TABLE t1(id int unique, num int , des text) DISTRIBUTE BY hash(num);
ERROR:  Distribution column is not in index column list, create index cannot be supported

合理选择分片键

一个表中有很多列,选择哪一列作为分片键呢?合理选择分片键能够提高查询效率。选择分片键的时候,要注意一下几个问题。

  • 分片字段尽量和 join 连接字段保持一致

    只要是进行切分,跨节点 join 的问题是不可避免的。但是良好的设计和切分可以减少此类情况的发生。解决这一问题,要求分片字段和 join 连接字段保持一致,当查询可以下推至节点运算时,可以有效解决此类问题。如果运算不允许下推至节点,可以将数据量较小的表修改为复制表后(数据量不超过 1000 万),也能实现本节点运算的能力。

  • 分片字段尽量和聚合字段保持一致

    跨节点 order by,group by,count 等聚合函数问题是一类问题,因为它们都需要基于全部数据集合进行计算。解决这一问题,要求分片字段和聚合字段保持一致,当聚合可以下推至节点运算时,可以有效解决此类问题。

  • 分片键选择时,尽量保证数据能够均匀分布。

  • 分片键选择时,尽量保证事务控制在分片范围内(本节点),避免出现跨分片(跨节点)。

复制分布

复制分布则会在每一个 datanode 上都保留一份数据的副本,这会造成数据冗余,仅适用于使用频率非常高的小表。例如,全国所有的省份信息表,这样的表本身数据量有限,但是会经常与其它大表进行 join 操作,创建为复制表不会占用较多空间,确能提高查询效率。

示例,创建一个复制表,插入一些数据。

CREATE TABLE t1(id int, num int, des text) DISTRIBUTE BY replication;
INSERT INTO t1 VALUES(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8);

可以看到,在每一个 datanode 上都有全部的数据。

antdb=# EXECUTE DIRECT ON  (dn1) 'SELECT * FROM  t1';
 id | num | des 
----+-----+-----
  1 |   1 | 
  2 |   2 | 
  3 |   3 | 
  4 |   4 | 
  5 |   5 | 
  6 |   6 | 
  7 |   7 | 
  8 |   8 | 
(8 rows)

antdb=# EXECUTE DIRECT ON  (dn2) 'SELECT * FROM  t1';
 id | num | des 
----+-----+-----
  1 |   1 | 
  2 |   2 | 
  3 |   3 | 
  4 |   4 | 
  5 |   5 | 
  6 |   6 | 
  7 |   7 | 
  8 |   8 | 
(8 rows)

取模分布

取模分布与哈希分布类似,只是内部计算方式不同,类似于除以节点数取余数来决定分布的节点。

示例,创建表 t1 分布方式是取模分布,分布节点是 dn1 和 dn2。

CREATE TABLE t1(id int, num int, des text) DISTRIBUTE BY modulo(id) to node(dn1, dn2, dn3);
INSERT INTO t1 VALUES(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8);

查看表在 dn1、dn2 和 dn3 上数据分布如下:

antdb=# EXECUTE DIRECT ON  (dn1) 'SELECT * FROM  t1';
 id | num | des 
----+-----+-----
  3 |   3 | 
  6 |   6 | 
(2 rows)

antdb=# EXECUTE DIRECT ON  (dn2) 'SELECT * FROM  t1';
 id | num | des 
----+-----+-----
  1 |   1 | 
  4 |   4 | 
  7 |   7 | 
(3 rows)

antdb=# EXECUTE DIRECT ON  (dn3) 'SELECT * FROM  t1';
 id | num | des 
----+-----+-----
  2 |   2 | 
  5 |   5 | 
  8 |   8 | 
(3 rows)

随机分布

随机分布,顾名思义,是指将数据随机分布在某一个 datanode 上。此种分布方式的优点是数据会非常均匀分布在所有的节点上。前面介绍的哈希分布和取模分布都有可能造成数据倾斜(即根据计算得出,某一个 datanode 上存放的数据很多,而其它 datanode 上的数据较少的情况),复制分布又会造成空间浪费,只有随机分布能解决此问题。

由于数据完全随机分布,没有一个固定的算法来决定数据分布在哪个节点上,所以当查询数据时,无法根据某一列的值来定位到某一个 datanode,因此需要在所有的 datanode 上,查找数据,从而造成查询效率的低下。

示例,第一次创建表 t1,查看 dn1 节点上存放的数据如下:

CREATE TABLE t1(id int, num int, des text) DISTRIBUTE BY random to node(dn1, dn2, dn3);
INSERT INTO t1 VALUES(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8);
EXECUTE DIRECT ON  (dn1) 'SELECT * FROM  t1';
antdb=#  EXECUTE DIRECT ON  (dn1) 'SELECT * FROM  t1';
 id | num | des 
----+-----+-----
  2 |   2 | 
  5 |   5 | 
  6 |   6 | 
  7 |   7 | 
(4 rows)

删掉表 t1,再次创建表 t1,插入同样的数据,查看 dn1 节点上存放的数据,发现数据分布与第一次不同,因为每次插入数据都是随机的,所以两次随机分布的结果不同:

DROP TABLE t1;
CREATE TABLE t1(id int, num int, des text) DISTRIBUTE BY random to node(dn1, dn2, dn3);
INSERT INTO t1 VALUES(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8);
EXECUTE DIRECT ON  (dm1) 'SELECT * FROM  t1';

antdb=#  EXECUTE DIRECT ON  (dn1) 'SELECT * FROM  t1';
 id | num | des 
----+-----+-----
  7 |   7 | 
(1 row)

修改表分布(表数据重分布)

可以通过 ALTER TABLE 命令修改表的分布方式和分布节点。

如果表中有数据,修改表的分布会导致表中的数据按新的分布进行存储。

需要注意点的是,因为修改表分布会导致数据迁移,数据迁移期间会锁表,所以修改数据量特别大的表的分布可能会导致此表在一段时间内不可被访问。

修改分片键

哈希分布式方式下修改分片键:

antdb=# CREATE TABLE T36CE5 (id int,name varchar(20)) DISTRIBUTE BY hash(id);
CREATE TABLE
antdb=# INSERT INTO T36CE5 SELECT i,'t'||i FROM generate_series(0, 1000) i;
INSERT 0 1001
antdb=#  EXECUTE DIRECT ON  (dn1) 'SELECT count(1) FROM T36CE5;';
 count
-------
   186
(1 row)

antdb=# ALTER TABLE T36CE5  DISTRIBUTE BY hash(name);
ALTER TABLE
antdb=#  EXECUTE DIRECT ON  (dn1) 'SELECT count(1) FROM T36CE5;';
 count
-------
   212
(1 row)

注意:哈希分布方式仅支持整型和字符类型的列为分片键。

取模分布式方式下修改分片键:

antdb=#  DROP TABLE T36CE5;
DROP TABLE
antdb=#  CREATE TABLE T36CE5 (id int2,iid int) DISTRIBUTE BY modulo(id);
CREATE TABLE
antdb=#  INSERT INTO T36CE5 SELECT i,i*i FROM generate_series(0, 100) i;
INSERT 0 101
antdb=#  EXECUTE DIRECT ON  (dn1) 'SELECT count(1) FROM T36CE5;';
 count
-------
    20
(1 row)

antdb=# ALTER TABLE T36CE5  DISTRIBUTE BY modulo (iid);
ALTER TABLE
antdb=# EXECUTE DIRECT ON  (dn1) 'SELECT count(1) FROM T36CE5;';
 count
-------
    40
(1 row)

注意:取模分布方式仅支持 INT2 和 INT 的列为分片键。

修改分布方式

AntDB 分布式支持的四种分布式(哈希,取模,复制和随机)是可以互相转换的。示例:

antdb=# DROP TABLE T36CE5;
DROP TABLE
antdb=#CREATE TABLE T36CE5 (id int,name varchar(20)) DISTRIBUTE BY hash(id);
CREATE TABLE
antdb=#INSERT INTO T36CE5 SELECT i,'t'||i FROM generate_series(0, 1000) i;
INSERT 0 1001
antdb=#EXECUTE DIRECT ON  (dn1) 'SELECT count(1) FROM T36CE5;';
 count
-------
   186
(1 row)

antdb=# ALTER TABLE T36CE5  DISTRIBUTE BY modulo (id);
ALTER TABLE
antdb=# EXECUTE DIRECT ON  (dn1) 'SELECT count(1) FROM T36CE5;';
 count
-------
   200
(1 row)

antdb=# ALTER TABLE T36CE5  DISTRIBUTE BY replication;
ALTER TABLE
antdb=# EXECUTE DIRECT ON  (dn1) 'SELECT count(1) FROM T36CE5;';
 count
-------
  1001
(1 row)

antdb=#ALTER TABLE T36CE5  DISTRIBUTE BY random;
ALTER TABLE
antdb=# EXECUTE DIRECT ON  (dn1) 'SELECT count(1) FROM T36CE5;';
 count
-------
   199
(1 row)

修改分布节点

通过 ALTER TABLE 命令也可以修改表的分布节点。示例:

antdb=# CREATE TABLE T36CE5 (id int,name varchar(20)) DISTRIBUTE BY hash(id) to node (dn1,dn3,dn4);
CREATE TABLE
antdb=#INSERT INTO T36CE5 SELECT i,'t'||i FROM generate_series(0, 1000) i;
INSERT 0 1001
antdb=#\d+ T36CE5
                                          Table "public.t36ce5"
 Column |         Type          | Collation | Nullable | Default | Storage  | Stats target | Description
--------+-----------------------+-----------+----------+---------+----------+--------------+-------------
 id     | integer               |           |          |         | plain    |              |
 name   | character varying(20) |           |          |         | extended |              |
DISTRIBUTE BY HASH(id) TO NODE(dn1, dn3, dn4)
Access method: heap

antdb=#ALTER TABLE T36CE5 to node (dn0,dn2);
ALTER TABLE
antdb=#\d+ T36CE5
                                          Table "public.t36ce5"
 Column |         Type          | Collation | Nullable | Default | Storage  | Stats target | Description
--------+-----------------------+-----------+----------+---------+----------+--------------+-------------
 id     | integer               |           |          |         | plain    |              |
 name   | character varying(20) |           |          |         | extended |              |
DISTRIBUTE BY HASH(id) TO NODE(dn0, dn2)
Access method: heap

antdb=#ALTER TABLE T36CE5 DISTRIBUTE BY random ;
ALTER TABLE
antdb=# \d+ T36CE5
                                          Table "public.t36ce5"
 Column |         Type          | Collation | Nullable | Default | Storage  | Stats target | Description
--------+-----------------------+-----------+----------+---------+----------+--------------+-------------
 id     | integer               |           |          |         | plain    |              |
 name   | character varying(20) |           |          |         | extended |              |
DISTRIBUTE BY RANDOM TO NODE(dn0, dn2)
Access method: heap

注意:ALTER TABLE 时不能同时改变分布方式和分布节点,也就是 ALTER TABLE 时 DISTRIBUTE BY 和 to node不能一起使用,但是 CREATE TABLE 可以。

antdb=#ALTER TABLE T36CE5 DISTRIBUTE BY replication to node (dn0,dn1,dn2);
ERROR:  syntax error at or near "to"
LINE 1: ALTER TABLE T36CE5 DISTRIBUTE BY replication to node (dn0,dn...
                                                     ^

修改分片键的值

通过前面的章节已知,AntDB 的分布方式有四中:hash、modulo、replication、random。其中 hash(哈希)和 modulo(取模)分片方式是有分片键的,如下,分片键是 id 列:

create table t_hash(id int, num int) distribute by hash(id);
create table t_modulo(id int, num int) distribute by modulo(id);

当用户向表中 insert 一条数据时,对分片键的列值经过哈希或者取模计算,来确定数据存储到哪个 datanode 上。因此当 update 分片键时,列值的改变会导致数据分布节点的改变。

目前,AntDB 不支持 update 导致某行数据分布节点的改变,因此,只支持指定情况下修改分片键。

下面以 hash 分布为例,说明支持的两种特定情况:

  • 表分布到一个 datanode 上时,用户可以随意改变分片键的值;
create table t_hash(id int, num int) distribute by hash(id) to node(dm1);
insert into t_hash select i,i from generate_series(1,10) i;
update t_hash set id = 5 where id = 1; --支持
update t_hash set id = id + 1 where id > 6; --支持
  • 当表分布到多个 datanode 上(默认)时,只支持通过预备语句 update 分片键,且分片键的值前后必须一样;
create table t_hash(id int, num int) distribute by hash(id) to node(dm1, dm2, dm3);
insert into t_hash select i,i from generate_series(1,10) i;
update t_hash set id = 5 where id = 1;--不支持
update t_hash set id = id + 1 where id > 6;--不支持
update t_hash set id = 1 where id = 1; --不支持
prepare p_hash(int, int) as update t_hash set id = $1 where id = $2;
execute p_hash(1,1); --支持,$1=$2
execute p_hash(2,2); --支持,$1=$2
execute p_hash(2,1); --不支持,$1不等于$2

由上面的例子可知,目前只支持下面一种 update,创建预备语句,且执行预备语句时,必须满足 $1= $2

--1、创建预备语句
prepare p_hash(int, int) as update t_hash set id = $1 where id = $2;
--2、执行预备语句,且$1必须等于$2
execute p_hash(1,1); 
问题反馈