1、表

OushuDB表由行(rows)和(columns)组成。每一个列有一个列名和一个数据类型,一个表的列数和列的程序是固定的。一个表的行数是可变的。SQL并不假如表中行的程序。当读一个表时,除非显示要求排序,返回的行会以任意程序呈现。另外,SQL并不给每一行一个惟一标志符,所以,一个表中具备同样几个同样的行是可能的。

创立一个表能够应用create table命令。在命令外面,须要指定表名,列名以及列的类型。例如:

create table my_first_table (    first_column text,    second_column integer);

下面的命令创立了一个两列的表,一列为文本类型,一列为整数类型。删除刚刚创立的表能够应用drop table命令。

drop table my_first_table;

2、表的存储格局

OushuDB当初反对多种存储格局:AO,Parquet,ORC,MagmaAP。AO是按行存储的格局,而Parquet,ORC,MagmaAP是按列存储的格局。 其中MagmaAP 是在4.0.0.0公布的全新的存储格局。MagmaAP,ORC都反对update/delete,反对事务, 且MagmaAP还反对index。
注:和GPDB相似,之前OushuDB版本反对CO格局,但CO格局不适宜集群大和分区多的状况,后续新版本去除了CO反对。

对于各种格局的表的建表语法,上面给出了几个例子。

# 默认创立的是AO表CREATE TABLE rank1 (id int, rank int, year smallint,gender char(1), count int );# 和下面的创立的表一样,显式指定存储格局类型CREATE TABLE rank2 (id int, rank int, year smallint,gender char(1), count int ) with (appendonly =true, orientation =row);# 创立一个snappy压缩的AO表CREATE TABLE rank3 (id int, rank int, year smallint,gender char(1), count int ) with (appendonly =true, orientation =row, compresstype = snappy);# 创立一个snappy压缩的Parquet表,如果不指定压缩类型的话,默认不压缩。CREATE TABLE rank3 (id int, rank int, year smallint,gender char(1), count int ) with (appendonly =true, orientation =parquet, compresstype = snappy);# 创立一个不压缩的ORC表,如果不指定压缩类型的话,默认不压缩。CREATE TABLE rank3 (id int, rank int, year smallint,gender char(1), count int ) with (appendonly =true, orientation =orc);# 创立一个带压缩的ORC表,需指定压缩类型。CREATE TABLE rank3 (id int, rank int, year smallint,gender char(1), count int ) with (appendonly =true, orientation =orc, compresstype = lz4);# 创立一个压缩的magma表, magma 外部主动实现了压缩。CREATE TABLE rank3 (id int, rank int, year smallint,gender char(1), count int ) format 'magmaap';# 创立一个有primary key的magma表, magma 外部主动实现了压缩。CREATE TABLE rank3 (id int, rank int, year smallint,gender char(1), count int,primary key(id) ) format 'magmaap';

3、表的散布

在OushuDB中,表能够两种形式散布形式:基于Hash的散布和Random散布。基于Hash的散布办法基于散布列的Hash值进行散布,Random散布采取随机分布模式。

创立表时用户不指定散布形式的时候非magmaap表默认应用Random散布,magmaap表目前不反对Random 散布。上面这个两个例子等价。

CREATE TABLE rank (id int, rank int, year smallint,gender char(1), count int );CREATE TABLE rank (id int, rank int, year smallint,gender char(1), count int );DISTRIBUTED RANDOMLY;

上面这个例子创立一个Hash散布的表,散布的Key应用三个列(rank, gender, year)的组合,数据分布到32个bucket外面。

如果不指定bucketnum的话,零碎默认应用default_hash_table_bucket_number零碎参数的值来做为bucketnum。

CREATE TABLE rank (id int, rank int, year smallint,gender char(1), count int )WITH (bucketnum = 32)DISTRIBUTED BY (rank, gender,year);

4、Hash散布和Random散布的选取

非Magma表: Random散布的表较灵便,在零碎扩容增加节点后无需从新散布数据。而Hash散布的表在零碎扩容后,为了利用新减少节点的计算能力,须要从新散布数据。另外,针对Hash散布的表资源管理器在分配资源的时候采取调配固定virtual segment数的形式,不如Random散布灵便。

Hash散布的表在某些查问上会有性能上的益处,因为有时能够防止从新散布某些表。
例如上面例子的查问,如果lineitem和orders两张表别离依照l_orderkey和o_orderkey散布,则这个查问在执行时不再须要从新散布任何一张表就能够并行在各个节点并行执行连贯操作。

SELECT l_orderkey, count(l_quantity)FROM lineitem, ordersWHERE l_orderkey = o_orderkey

针对绝大多数查问,试验表明都不是网络瓶颈,基于Hash散布和基于Random散布性能差异不大。所以咱们倡议用户默认采取Random散布, 只针对特定须要优化的场合应用Hash散布的表。

Magma表具备的Hash和random 表的劣势,通过 default_magma_hash_table_nvseg_per_node 来管制每个节点能启动的virtual segment数。 在零碎扩容后不须要从新散布数据。

5、Hash散布的表bucketnum的选取

针对Hash散布的表,bucketnum决定了一个查问的并行度。在一些常见的硬件配置中(128G内存和12块SAS盘),咱们倡议选取6 节点数或者8 节点数。 硬件更好的话能够减少bucketnum。在零碎初始化的时候,default_hash_table_bucket_number的初始化默认值为8 * 节点数。Magma table 应用default_magma_hash_table_nvseg_per_node, 示意每个节点是virtual segment 的个数。:

6、表分区

针对大的数据仓库事实表,往往咱们能够通过对表进行分区的形式来把一个很大的表拆分成多个子表。这样的话,有两个益处:
● 查问优化器能够针对分区表进行优化,如果查问只设计到某些分区,则查问打算只须要扫描这些分区,从而减速查问
● 如果咱们依照日期进行分区的话,咱们能够简略的退出分区和删除过期的分区。
OushuDB反对基于Range和List的两种分区形式。
● Range分区:根据数值范畴进行分区,比方日期,价格等
● List分区:根据一个值的列表进行分区,比方地区等
上面咱们通过例子阐明这两种分区的应用形式。

Range分区

# 创立一个sales表,依照date列Range分区,从2008年到2009年每月创立一个分区postgres=# CREATE TABLE sales (id int, date date, amt decimal(10,2))PARTITION BY RANGE (date)( START (date '2008-01-01') INCLUSIVE   END (date '2009-01-01') EXCLUSIVE   EVERY (INTERVAL '1 month') );

查看创立的表信息,d+给出该表的所有信息

postgres=# \d+ sales              Append-Only Table "public.sales" Column |     Type      | Modifiers | Storage | Description--------+---------------+-----------+---------+------------- id     | integer       |           | plain   | date   | date          |           | plain   | amt    | numeric(10,2) |           | main    |Compression Type: NoneCompression Level: 0Block Size: 32768Checksum: fChild tables: sales_1_prt_1,              sales_1_prt_10,              sales_1_prt_11,              sales_1_prt_12,              sales_1_prt_2,              sales_1_prt_3,              sales_1_prt_4,              sales_1_prt_5,              sales_1_prt_6,              sales_1_prt_7,              sales_1_prt_8,              sales_1_prt_9Has OIDs: noOptions: appendonly=trueDistributed randomlyPartition by: (date)

你也能够显式得申明子分区并指定子表名字。

CREATE TABLE sales_exp (id int, date date, amt decimal(10,2))PARTITION BY RANGE (date)( PARTITION Jan08 START (date '2008-01-01') INCLUSIVE ,  PARTITION Feb08 START (date '2008-02-01') INCLUSIVE ,  PARTITION Mar08 START (date '2008-03-01') INCLUSIVE ,  PARTITION Apr08 START (date '2008-04-01') INCLUSIVE ,  PARTITION May08 START (date '2008-05-01') INCLUSIVE ,  PARTITION Jun08 START (date '2008-06-01') INCLUSIVE ,  PARTITION Jul08 START (date '2008-07-01') INCLUSIVE ,  PARTITION Aug08 START (date '2008-08-01') INCLUSIVE ,  PARTITION Sep08 START (date '2008-09-01') INCLUSIVE ,  PARTITION Oct08 START (date '2008-10-01') INCLUSIVE ,  PARTITION Nov08 START (date '2008-11-01') INCLUSIVE ,  PARTITION Dec08 START (date '2008-12-01') INCLUSIVE                   END (date '2009-01-01') EXCLUSIVE );

查看创立的表信息

postgres=# \d+ sales_exp            Append-Only Table "public.sales_exp" Column |     Type      | Modifiers | Storage | Description--------+---------------+-----------+---------+------------- id     | integer       |           | plain   | date   | date          |           | plain   | amt    | numeric(10,2) |           | main    |Compression Type: NoneCompression Level: 0Block Size: 32768Checksum: fChild tables: sales_exp_1_prt_apr08,              sales_exp_1_prt_aug08,              sales_exp_1_prt_dec08,              sales_exp_1_prt_feb08,              sales_exp_1_prt_jan08,              sales_exp_1_prt_jul08,              sales_exp_1_prt_jun08,              sales_exp_1_prt_mar08,              sales_exp_1_prt_may08,              sales_exp_1_prt_nov08,              sales_exp_1_prt_oct08,              sales_exp_1_prt_sep08Has OIDs: noOptions: appendonly=trueDistributed randomlyPartition by: (date)

上面是另外一个依据Range分区的例子,这次应用的是整型列进行分区。这外面咱们增加了一个DEFAULT PARTITION, 在不满足其余分区的条件下,数据会被插入DEFAULT PARTITION。

CREATE TABLE rank (id int, rank int, year int, gender char(1), count int)PARTITION BY RANGE (year)( START (2001) END (2008) EVERY (1),  DEFAULT PARTITION extra );

7、List分区

上面的例子创立了一个基于List的分区表。List分区表能够基于任意反对等值比拟的数据类型。对与List分区,你须要 显式的指定所有子分区。

postgres=# CREATE TABLE rank (id int, rank int, year int, gender char(1), count int )PARTITION BY LIST (gender)( PARTITION girls VALUES ('F'),  PARTITION boys VALUES ('M'),  DEFAULT PARTITION other );
postgres=# \d+ rank              Append-Only Table "public.rank" Column |     Type     | Modifiers | Storage  | Description--------+--------------+-----------+----------+------------- id     | integer      |           | plain    | rank   | integer      |           | plain    | year   | integer      |           | plain    | gender | character(1) |           | extended | count  | integer      |           | plain    |Compression Type: NoneCompression Level: 0Block Size: 32768Checksum: fChild tables: rank_1_prt_boys,              rank_1_prt_girls,              rank_1_prt_otherHas OIDs: noOptions: appendonly=trueDistributed randomlyPartition by: (gender)

8、多级分区

你能够应用SUBPARTITION模版定义多级分区。上面的例子定义了一个两级分区表,第一级装置date列进行Range分区,第二级依照region列进行List分区。

CREATE TABLE sales (trans_id int, date date, amount decimal(9,2), region text)PARTITION BY RANGE (date)SUBPARTITION BY LIST (region)SUBPARTITION TEMPLATE( SUBPARTITION usa VALUES ('usa'),  SUBPARTITION asia VALUES ('asia'),  SUBPARTITION europe VALUES ('europe'),  DEFAULT SUBPARTITION other_regions)(START (date '2011-01-01') INCLUSIVE END (date '2012-01-01') EXCLUSIVE EVERY (INTERVAL '1 month'), DEFAULT PARTITION outlying_dates);

注:当你在应用多级分区的时候,零碎会产生大量的小表,有些表可能没有数据或蕴含很少数据,这样会对系统元数据管理产生过多压力。 倡议不要创立具备过多分区的表。个别限度分区数在100或以内比拟正当。

9、查看你的分区设计

你能够通过pg_partitions视图来查看你的分区表设计。例如通过上面的语句能够查看出sales表的分区设计。

postgres=# SELECT partitionboundary, partitiontablename, partitionname, partitionlevel, partitionrankpostgres-# FROM pg_partitionspostgres-# WHERE tablename='sales';                                           partitionboundary                                          | partitiontablename | partitionname | partitionlevel | partitionrank------------------------------------------------------------------------------------------------------+--------------------+---------------+----------------+--------------- START ('2008-01-01'::date) END ('2008-02-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_1      |               |              0 |             1 START ('2008-02-01'::date) END ('2008-03-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_2      |               |              0 |             2 START ('2008-03-01'::date) END ('2008-04-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_3      |               |              0 |             3 START ('2008-04-01'::date) END ('2008-05-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_4      |               |              0 |             4 START ('2008-05-01'::date) END ('2008-06-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_5      |               |              0 |             5 START ('2008-06-01'::date) END ('2008-07-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_6      |               |              0 |             6 START ('2008-07-01'::date) END ('2008-08-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_7      |               |              0 |             7 START ('2008-08-01'::date) END ('2008-09-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_8      |               |              0 |             8 START ('2008-09-01'::date) END ('2008-10-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_9      |               |              0 |             9 START ('2008-10-01'::date) END ('2008-11-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_10     |               |              0 |            10 START ('2008-11-01'::date) END ('2008-12-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_11     |               |              0 |            11 START ('2008-12-01'::date) END ('2009-01-01'::date) EVERY ('1 mon'::interval) WITH (appendonly=true) | sales_1_prt_12     |               |              0 |            12(12 rows)

10、增加一个分区

你能够通过上面的语句增加一个分区。

ALTER TABLE sales ADD PARTITION            START (date '2009-02-01') INCLUSIVE            END (date '2009-03-01') EXCLUSIVE;

如果你在创立表的时候没有应用subpartition template,你须要在增加分区的时候给出子分区定义,例如:

ALTER TABLE sales ADD PARTITION    START (date '2009-02-01') INCLUSIVE    END (date '2009-03-01') EXCLUSIVE      ( SUBPARTITION usa VALUES ('usa'),        SUBPARTITION asia VALUES ('asia'),        SUBPARTITION europe VALUES ('europe') );

你也能够独自批改一个二级分区:

CREATE TABLE sales_two_level (trans_id int, date date, amount decimal(9,2), region text)PARTITION BY RANGE (date)SUBPARTITION BY LIST (region)SUBPARTITION TEMPLATE( SUBPARTITION usa VALUES ('usa'),  SUBPARTITION asia VALUES ('asia'),  SUBPARTITION europe VALUES ('europe'))(START (date '2011-01-01') INCLUSIVE END (date '2012-01-01') EXCLUSIVE EVERY (INTERVAL '1 month'), DEFAULT PARTITION outlying_dates);ALTER TABLE sales_two_level ALTER PARTITION FOR (RANK(12))   ADD PARTITION africa VALUES ('africa');

其中RANK(12)示意第12个分区。
注:指定一个分区能够应用

PARTITION FOR (value) or PARTITION FOR(RANK(number))语法。

如果你的分区表有一个Default分区的话,你不能够向该分区表增加分区,你只能够通过决裂Default分区的办法来增加分区。

11、重命名分区

Partitioned tables use the following naming convention. Partitioned subtable names are subject to uniqueness requirements and length limitations.
分区表应用以下的命名规定。
<parentname>_<level>_prt_<partition_name>
例如:sales_1_prt_jan08指的是父表名字为sales,第一级分区名字为jan08的分区。在创立Range分区表时, 如果没有指定分区名字,分区的名字会主动生成为数字。
扭转父表的名字同时会扭转分区表的名字。例如:

postgres=# ALTER TABLE sales_two_level RENAME TO globalsales;postgres=# \d+ globalsales            Append-Only Table "public.globalsales"  Column  |     Type     | Modifiers | Storage  | Description----------+--------------+-----------+----------+------------- trans_id | integer      |           | plain    | date     | date         |           | plain    | amount   | numeric(9,2) |           | main     | region   | text         |           | extended |Compression Type: NoneCompression Level: 0Block Size: 32768Checksum: fChild tables: globalsales_1_prt_10,              globalsales_1_prt_11,              globalsales_1_prt_12,              globalsales_1_prt_13,              globalsales_1_prt_2,              globalsales_1_prt_3,              globalsales_1_prt_4,              globalsales_1_prt_5,              globalsales_1_prt_6,              globalsales_1_prt_7,              globalsales_1_prt_8,              globalsales_1_prt_9,              globalsales_1_prt_outlying_datesHas OIDs: noOptions: appendonly=trueDistributed randomlyPartition by: (date)

你能够扭转一个分区的名字,例如:

ALTER TABLE sales RENAME PARTITION FOR ('2008-01-01') TO jan08;

12、增加一个默认分区 (Default Partition)

你能够应用Alter命令增加一个默认分区。不满足任何分区条件的分区会进入默认分区。

ALTER TABLE sales ADD DEFAULT PARTITION other;ALTER TABLE sales ALTER PARTITION FOR (RANK(1)) ADD DEFAULT PARTITION other;

13、删除一个分区

你能够通过Alter命令删除一个分区。如果一个分区有子分区,在删除该分区的时候,它的子分区也会被删除。
对于一个分区的事实表,删除分区罕用来删除保留工夫窗口外的分区数据。

ALTER TABLE sales DROP PARTITION FOR (RANK(1));

14、Truncate分区

你能够通过Alter命令Truncate一个分区。在Truncate一个分区时,其子分区也会被Truncate。

ALTER TABLE sales TRUNCATE PARTITION FOR (RANK(1));

15、替换分区

你能够应用Alter Table命令来替换一个分区。替换分区操作把一个表和一个已存在分区进行替换(Swap)。你只能够替换叶子节点分区。
分区替换通常对数据加载很有用。例如,你能够首先加载数据到一个两头表,而后把该两头表替换到分区表外部。
你也能够利用分区替换扭转分区表的类型。例如:

CREATE TABLE sales (id int, date date, amt decimal(10,2))PARTITION BY RANGE (date)( START (date '2008-01-01') INCLUSIVEEND (date '2009-01-01') EXCLUSIVEEVERY (INTERVAL '1 month') );CREATE TABLE jan (LIKE sales) WITH (appendonly=true, orientation=parquet, compresstype = snappy);INSERT INTO jan SELECT * FROM sales_1_prt_1 ;ALTER TABLE sales EXCHANGE PARTITION FOR (RANK(1)) WITH TABLE jan;

16、分区决裂

你能够应用Alter决裂一个曾经存在的分区,例如上面的例子把sales_split分区表决裂成两个子分区:jan081to15和 jan0816to31。

CREATE TABLE sales_split (id int, date date, amt decimal(10,2))PARTITION BY RANGE (date)( START (date '2008-01-01') INCLUSIVEEND (date '2009-01-01') EXCLUSIVEEVERY (INTERVAL '1 month') );ALTER TABLE sales_split SPLIT PARTITION FOR ('2008-01-01')AT ('2008-01-16')INTO (PARTITION jan081to15, PARTITION jan0816to31);

如果你的分区表有Default分区的话,你只能够通过决裂Default分区的办法来增加子分区。例如,上面的例子通过决裂 Default分区的形式增加一个jan2009分区。

CREATE TABLE sales_split_default (id int, date date, amt decimal(10,2))PARTITION BY RANGE (date)( START (date '2008-01-01') INCLUSIVEEND (date '2009-01-01') EXCLUSIVEEVERY (INTERVAL '1 month'), DEFAULT PARTITION extra);ALTER TABLE sales_split_default SPLIT DEFAULT PARTITIONSTART ('2009-01-01') INCLUSIVEEND ('2009-02-01') EXCLUSIVEINTO (PARTITION jan2009, default partition);

17、批改子分区模版

你能够通过Alter命令批改子分区模版。先创立一个两级分区表。

CREATE TABLE sales (trans_id int, date date, amount decimal(9,2), region text)  DISTRIBUTED BY (trans_id)  PARTITION BY RANGE (date)  SUBPARTITION BY LIST (region)  SUBPARTITION TEMPLATE    ( SUBPARTITION usa VALUES ('usa'),      SUBPARTITION asia VALUES ('asia'),      SUBPARTITION europe VALUES ('europe'),      DEFAULT SUBPARTITION other_regions )  ( START (date '2014-01-01') INCLUSIVE    END (date '2014-04-01') EXCLUSIVE    EVERY (INTERVAL '1 month') );

上面这条命令批改子分区模版。

ALTER TABLE sales SET SUBPARTITION TEMPLATE( SUBPARTITION usa VALUES ('usa'),  SUBPARTITION asia VALUES ('asia'),  SUBPARTITION europe VALUES ('europe'),  SUBPARTITION africa VALUES ('africa'),  DEFAULT SUBPARTITION regions );

上面这条命令能够删除子分区模版。

ALTER TABLE sales SET SUBPARTITION TEMPLATE ();

对已存在非分区表进行分区
对已存在表进行分区,你须要创立一个新的分区表,并把须要分区的表的数据导入新的表。并把相干权限调配好。

CREATE TABLE sales2 (LIKE sales)PARTITION BY RANGE (date)( START (date '2008-01-01') INCLUSIVE   END (date '2009-01-01') EXCLUSIVE   EVERY (INTERVAL '1 month') );INSERT INTO sales2 SELECT * FROM sales;DROP TABLE sales;ALTER TABLE sales2 RENAME TO sales;GRANT ALL PRIVILEGES ON sales TO admin;GRANT SELECT ON sales TO guest;