指定分布方式
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)




