Greenplum或DeepGreen数据库对象的使用和管理

1. 创建文件空间
[gpadmin@cdha ~]$ gpfilespace -o gpfilespace_config   #当前目录下生成gpfilespace_config文件
Enter a name for this filespace> zhangyun_fs          #手工输入
primary location 1> /dbfast_zhangyun_tbs/primary      #手工输入
primary location 2> /dbfast_zhangyun_tbs/primary      #手工输入
mirror location 1> /dbfast_zhangyun_tbs/mirror        #手工输入
mirror location 2> /dbfast_zhangyun_tbs/mirror        #手工输入
master location> /dbfast_zhangyun_tbs/master          #手工输入


[gpadmin@cdha ~]$ gpfilespace -c gpfilespace_config   #基于gpfilespace_config配置文件创建文件空间




2. 创建表空间
CREATE TABLESPACE zhangyun_tbs FILESPACE zhangyun_fs;
允许普通的DB User来使用该表空间, 可以将CREATE权限授予相应的用户。
GRANT CREATE ON TABLESPACE zhangyun_tbs TO zhangyun;


或创建表空间时直接授权给用户
CREATE TABLESPACE zhangyun_tbs OWNER zhangyun FILESPACE zhangyun_fs;


3. 创建数据库
表空间创建好之后,我们就可以基于表空间来创建数据库
create database zhangyun_db with OWNER zhangyun template template0 encoding 'utf8' tablespace zhangyun_tbs ;


4. 使用表空间存储 DB 对象 
表、索引、甚至整个DB都可以指定在特定的表空间。


若要如此,拥有给定表空间CREATE权限的Role必须通过表空间的名称作为相关命令的参数来实现,下
面是创建一个zhangyun_tbs表空间上的表:
CREATE TABLE test(id int) TABLESPACE zhangyun_tbs;


或者使用缺省表空间参数default_tablespace来设定:
SET default_tablespace = zhangyun_tbs;
CREATE TABLE test(id int);


5. 查看现有的表空间和文件空间
每个GPDB系统都有两个缺省的表空间: pg_global(用以存储系统日志信息)和pg_default(用以存储template1和template0模版DB的缺省表空间)。 
这些表空间使用系统缺省的文件空间pg_system(系统初始化时使用的数据目录data directory)。


要获取文件空间的信息,可以查看系统日志表pg_filespace和pg_filespace_entry。
可通过与pg_tablespace关联查看表空间的完整定义:
SELECT spcname as tblspc, fsname as filespc, fsedbid as seg_dbid, fselocation as datadir
FROM pg_tablespace pgts, pg_filespace pgfs, pg_filespace_entry pgfse
WHERE pgts.spcfsoid=pgfse.fsefsoid AND pgfse.fsefsoid=pgfs.oid ORDER BY tblspc, seg_dbid;


    tblspc    |   filespc   | seg_dbid |             datadir              
--------------+-------------+----------+----------------------------------
 pg_default   | pg_system   |        1 | /dbfast0/master/dg-1
 pg_default   | pg_system   |        2 | /dbfast1/primary/dg0
 pg_default   | pg_system   |        3 | /dbfast2/primary/dg1
 pg_default   | pg_system   |        4 | /dbfast1/primary/dg2
 pg_default   | pg_system   |        5 | /dbfast2/primary/dg3
 pg_default   | pg_system   |        6 | /dbfast1/mirror/dg0
 pg_default   | pg_system   |        7 | /dbfast2/mirror/dg1
 pg_default   | pg_system   |        8 | /dbfast1/mirror/dg2
 pg_default   | pg_system   |        9 | /dbfast2/mirror/dg3
 pg_global    | pg_system   |        1 | /dbfast0/master/dg-1
 pg_global    | pg_system   |        2 | /dbfast1/primary/dg0
 pg_global    | pg_system   |        3 | /dbfast2/primary/dg1
 pg_global    | pg_system   |        4 | /dbfast1/primary/dg2
 pg_global    | pg_system   |        5 | /dbfast2/primary/dg3
 pg_global    | pg_system   |        6 | /dbfast1/mirror/dg0
 pg_global    | pg_system   |        7 | /dbfast2/mirror/dg1
 pg_global    | pg_system   |        8 | /dbfast1/mirror/dg2
 pg_global    | pg_system   |        9 | /dbfast2/mirror/dg3
 zhangyun_tbs | zhangyun_fs |        1 | /dbfast_zhangyun_tbs/master/dg-1
 zhangyun_tbs | zhangyun_fs |        2 | /dbfast_zhangyun_tbs/primary/dg0
 zhangyun_tbs | zhangyun_fs |        3 | /dbfast_zhangyun_tbs/primary/dg1
 zhangyun_tbs | zhangyun_fs |        4 | /dbfast_zhangyun_tbs/primary/dg2
 zhangyun_tbs | zhangyun_fs |        5 | /dbfast_zhangyun_tbs/primary/dg3
 zhangyun_tbs | zhangyun_fs |        6 | /dbfast_zhangyun_tbs/mirror/dg0
 zhangyun_tbs | zhangyun_fs |        7 | /dbfast_zhangyun_tbs/mirror/dg1
 zhangyun_tbs | zhangyun_fs |        8 | /dbfast_zhangyun_tbs/mirror/dg2
 zhangyun_tbs | zhangyun_fs |        9 | /dbfast_zhangyun_tbs/mirror/dg3
(27 rows)




6. 创建与管理模式
模式(Schema)是在DB内组织对象的一种逻辑结构。 模式可以允许用户在一个DB内不同的模式之间使用相同Name的对象(比如Table,View等)。


缺省"Public"模式
每个新创建的DB都有一个缺省的模式public。如果没有创建其他的模式,在创建DB对象时将缺省使用public模式。 缺省情况下所有的ROLE(User)都有public
模式下的CREATE和USAGE权限。而在创建其他模式时,需要将该模式授权给相关的ROLE(User)。


创建模式
CREATE SCHEMA zhangyun_schema;
CREATE SCHEMA zhangyun_schema AUTHORIZATION zhangyun;


模式搜索路径
要知道在DB的哪个模式下搜索需要的对象,可以通过明确指定模式名的方式来实现。
SELECT * FROM zhangyun_schema.mybigtable;
若不想通过指定模式名称的方式来实现,可以通过设置search_path参数来完成。
1) 设置模式搜索路径
该参数可以通过ALTER DATABAST命令修改DB的模式搜索路径
ALTER DATABASE zhangyun_db SET search_path TO zhangyun_schema, public, pg_catalog;


还可以通过ALTER ROLE命令修改特定ROLE(User)的模式搜索路径。例如:
ALTER ROLE zhangyun SET search_path TO zhangyun_schema, public, pg_catalog;


2) 查看当前的模式
SELECT current_schema();
SHOW search_path;


系统模式
下面的这些系统级别的模式在所有的DB中都存在:
1) pg_catalog模式
存储着系统日志表(System Catalog Table)、 内置类型(Type)、函数(Function)和运算符(Operator)。
该模式无论是否在search_path中指明,都存在search_path中。


2) information_schema模式
由一个标准化视图构成,其包含DB中对象的信息。该视图用于以标准化的方法从系统日志表中查看系统信息。


3) pg_toast模式
一个储存大对象的地方(那些超过页面尺寸(page size)的记录)。 该模式仅供GPDB系统内部使用,通常不建议管理员或者任何用户访问。


4) pg_bitmapindex模式
一个储存bitmap index对象的地方(值列表等)。该模式仅供GPDB系统内部使用,通常不建议管理员或者任何用户访问。


5) pg_aoseg模式
一个储存append-only表的地方。该模式仅供GPDB系统内部使用,通常不建议管理员或者任何用户访问。


6) gp_toolkit模式
一个管理用的模式,可以查看和检索系统日志文件和其他的系统信息。 gp_toolkit视图包含一些外部表、视图、函数, 
可以通过SQL的方式访问它们。 gp_toolkit视图对于所有DB User都是可以访问的。




7. 创建与管理表
创建表
1) 选择Column的数据类型
Column的数据类型决定了其可以储存什么类型的数据值。通常都希望用最小的空间储存数据。
对于字符串,在多数情况下,应该选择使用TEXT或者VARCHAR而不是CHAR。
对于Numeric类型来说,应该尽量选择更小的数据类型来适应数据。比如,选择BIGINT类型来存储SMALLINT类型范围内的数值,会造成空间的大量浪费。
对于打算用来做表关联的Column来说,应该考虑选择相同的数据类型。


2) 设置Table和Column的约束
检查约束
CREATE TABLE products ( product_no integer, name text, price numeric CHECK (price > 0) );


非空约束
CREATE TABLE products (product_no integer NOT NULL, name text NOT NULL, price numeric );


唯一约束
在GPDB中使用唯一约束存在强制条件, Table必须是HASH分布的(而不是DISTRIBUTED RANDOMLY),并且唯一约束的Column集合必须完整包含所有的DK Column。
CREATE TABLE products (product_no integer UNIQUE, name text, price numeric)
DISTRIBUTED BY (product_no);


主键约束
在GPDB中使用主键约束存在强制条件, Table必须是HASH分布的(而不是DISTRIBUTED RANDOMLY),并且主键约束的Column集合必须完整包含所有的DK Column。
CREATE TABLE products (product_no integer PRIMARY KEY, name text, price numeric)
DISTRIBUTED BY (product_no);


3) 声明分布键
在创建Table时有一个额外的子句用以指明分布策略。 如果在创建Table时没有指明DISTRIBUTED BY或者DISTRIBUTED RANDOMLY子句, 
GPDB将会依次考虑使用主键(假如该Table有的话)或者第一个字段作为HASH分布的DK。
CREATE TABLE products (name varchar(40), prod_id integer, supplier_id integer)
DISTRIBUTED BY (prod_id);


CREATE TABLE random_stuff (things text, doodads text, etc text)
DISTRIBUTED RANDOMLY;


4) 选择表的存储模式


######选择堆存储或者只追加存储(Heap Storage or Append-Optimized Storage)######
Heap Storage:
缺省情况下GPDB使用与PostgreSQL相同的存储模式为堆存储。堆存储模式在OLTP类型工作负载的DB中很常用,常用于数据在初始装载后经常变化。 
UPDATE和DELETE操作需要对ROW级别做版本控制从而确保DB事务处理的可靠性。堆表更适合一些小表,比如维表,这种表可能会在初始化装载后经常更新数据。
因此,对于经常update,delete,或单行insert操作,或者并行的update,delete和insert操作,都适合堆表存储。


创建堆表
行存堆表是缺省的存储模式:
CREATE TABLE test (id int, name text) DISTRIBUTED BY (id);




Append-Optimized Storage:
GPDB还提供了一种称之为只追加存储模式的表。AO表更适合数据仓库中非规范化事实表,这些表通常都是系统中最大的表。
AO表达到了更精简和优化的页面存储结构。该存储模式强化了批量数据装载的性能。不推荐一行一行的使用INSERT语句来装载数据。
目前版本AO表已经支持DELETE和UPDATE操作。
因此,对于初始数据导入,以及随后的批量insert,而且不频繁update的表适合使用AO表。不要在AO表上执行单条的insert,update或delete操作。
并行的批量insert操作也是可以的,但是不要执行并行批量的update或delte操作。不适合主要的原因是AO表的update或delete后的row占用的空间不能有效地
回收和重用。因此AO只适合数据一次导入的大表,update不频繁,并且多次查询的情况。


** 创建只追加表
CREATE TABLE test (id int, name text) WITH (appendonly=true);
演示delete和update对ao表的操作:
zhangyun_db=# insert into test values(1,'hello');
INSERT 0 1
zhangyun_db=# insert into test values(2,'spark');
INSERT 0 1
zhangyun_db=# update test set name = 'scala' where id = 1;
UPDATE 1
zhangyun_db=# select * from test;
 id | name  
----+-------
  1 | scala
  2 | spark
(2 rows)
zhangyun_db=# delete from test where id = 1;
DELETE 1
zhangyun_db=# select * from test;
 id | name  
----+-------
  2 | spark
(1 row)




######选择行存储或者列存储(Row or Column Orientation)######
考虑行存的情况:
(1) 表数据的更新
如果一张表在装载完之后一定有更新操作,那么就选择行存表。


(2) 经常做INSERT操作
如果经常有数据被INSERT,考虑选择行存表。列存表对于写操作不是最优的,
因为每条数据都需要被写到磁盘的多个位置(列存表的每列存储于不同的磁盘文件,而行存表是存储在同一个磁盘文件)。


(3) 查询涉及的COLUMN数量
若通常在SELECT或者WHERE中涉及TABLE的全部或大部COLUMN,考虑选择行存表。行存适合在WHERE或HAVING中对单列做聚合操作:
SELECT SUM(salary)...
SELECT AVG(salary)... WHERE salary > 10000


或者在WHERE条件中使用单个COLUMN条件且返回相对少量的ROW:
SELECT salary, dept ... WHERE state='CA'


创建行存表
在CREATE TABLE时使用WITH子句来指明TABLE的存储模式。如果没有指明,该表将会是缺省的行存堆表。




考虑列存的情况:
列式存储是对读操作进行优化的,而对写操作没有优化。对于row的列值被放在磁盘的不同位置。列存表在只访问宽表的很少COLUMN的查询中可以表现出更好的性能。
另外一个性能提升的地方是,列式存储的每列都是相同格式的数据值,压缩效率高,占用磁盘空间少,减少磁盘IO。


创建列存表
使用列存的TABLE必须是AO表。
CREATE TABLE test (id int, name text) WITH (appendonly=true, orientation=column)
DISTRIBUTED BY (id);


5) 使用压缩(只可以是AO表)
在GPDB中, AO表有两种库内压缩可选,一种是表级的压缩,另外一种是COLUMN级别的压缩,前者应用到整个TABLE,后者应用到指定的COLUMN。
在选择COLUMN级别压缩时,可以为不同的COLUMN选择不同的压缩算法。下表是可用的压缩算法:


表导向  |  可用压缩类型    |    支持压缩算法
--------|------------------|-------------------------------
行      |  表级别          |    ZLIB 和 QUICKLZ
--------|------------------|---------------------------------
列      | 列级别 和 表级别 |    RLE_TYPE、 ZLIB 和 QUICKLZ


使用库内压缩要求Segment系统具备强劲的CPU来压缩和解压缩数据。不要在压缩文件系统使用压缩AO表。如果Instance数据目录是压缩文件系统,不要压缩使用AO表。


QUICKLZ压缩通常适用于CPU能力一般的情况,其压缩速度比ZLIB快,但压缩率不如ZLIB。 
相反的, ZLIB提供更高的压缩率,但压缩速度较低。 在压缩级别为1时, QUICKLZ与ZLIB可能获得差不多的压缩率(但压缩速度ZLIB可能差一些)。
但在6级以上的ZLIB在压缩率方面的优势显著高于QUICKLZ(但压缩速度也因此显著的低于QUICKLZ)。


QUICKLZ压缩模式只有一种压缩级别,没有级别选项可以选择。而ZLIB压缩模式有1 – 9个压缩级别可选。




创建压缩表
CREATE TABLE foo (a int, b text) WITH (appendonly=true, compresstype=zlib, compresslevel=5);


检查AO表的压缩与分布情况
------------------------------------------------------------------------------------------------------------------------
select get_ao_distribution(name);      |   Set of (dbid, tuplecount) rows | 展示 AO 表的分布情况,每ROW 对应 
select get_ao_distribution(oid);       |                                  | Segment Instance 的dbid 与储存的数据行数。
------------------------------------------------------------------------------------------------------------------------
select get_ao_compression_ratio(name); |   float8                         | 计算出 AO 表的压缩率。
select get_ao_compression_ratio(oid);  |                                  | 如果该信息未得到,将返回-1 值
------------------------------------------------------------------------------------------------------------------------


示例:
select get_ao_distribution('foo'); #每个Instance存储foo表的ROW数量
 get_ao_distribution 
---------------------
 (3,2500037)
 (0,2499970)
 (2,2499974)
 (1,2500033)


select get_ao_compression_ratio('foo');
 get_ao_compression_ratio 
--------------------------
                     4.76 #意味着foo表未压缩状态下的储存尺寸是压缩下的储存尺寸的4倍多
(1 row)


下面演示通过oid查看:
select oid from pg_class where relname = 'foo';
  oid  
-------
 25626


select get_ao_distribution(25626);
 get_ao_distribution 
---------------------
 (0,2499970)
 (1,2500033)
 (3,2500037)
 (2,2499974)


select get_ao_compression_ratio(25626);
 get_ao_compression_ratio 
--------------------------
                     4.76




支持运行长度编码
GPDB已支持COLUMN级别的运行长度编码(Run-length Encoding /RLE)压缩算法。 RLE是一种将连续重复的数据作为一种计数方式存储的压缩算法。
RLE对于重复元素是很有效的。比如,在一个表中有两个COLUMN,一个日期COLUMN和一个描述COLUMN,其中包含200000个date1和400000个data2,
RLE压缩处理这种数据为类似data1 200000 data2 400000这样的效果。对于那些没有很多重复值的数据RLE是不适合的,而且还可能会显著的增加存储文件的尺寸。
RLE压缩有4种级别。级别越高,压缩效率越高,但压缩速度也会越低。
               
使用列级压缩
在CREATE TABLE、 ALTER TABLE和CREATE TYPE命令中包含对COLUMN设置压缩类型、压缩级别和块尺寸(Block Size)的选项。 这些参数统称为存储参数。
存储参数可用于行导向和列导向的AO表。 下面列举这3种存储参数及每种参数的可选值。


名称                |          解释            |      可选值
COMPRESSTYPE        |        使用的压缩类型    |      ZLIB(更高压缩)
                    |                          |      QUICKLZ(更快压缩)
                    |                          |      RLE_TYPE(运行长度编码)
                    |                          |      none(无压缩、缺省值)
----------------------------------------------------------------------------------------------------------
COMPRESSLEVEL       |        压缩级别          |      ZLIB 为 1-9 级可选
                    |                          |      1 级压缩较快但压缩率较低,9 级压缩较慢但压缩率较高
                    |                          |      QUICKLZ 仅 1 个级别可选(缺省不需指定)
                    |                          |      RLE_TYPE 为 1-4 级可选
                    |                          |      1 级压缩较快但压缩率较低,4 级压缩较慢但压缩率较高
-----------------------------------------------------------------------------------------------------------
BLOCKSIZE           |        表的存储块大小    |      8192 – 209715(8K – 2M)该值必须是 8192 的倍数
-----------------------------------------------------------------------------------------------------------
使用存储参数的格式如下:
[ ENCODING ( storage_directive [,…] ) ]
这里ENCODING关键字是必须的, 存储参数包含3个部分:参数名称、等于号、参数值。
如下面的CREATE TABLE语句所示:
一般用法:
column_name data_type ENCODING ( storage_directive [, … ] ), …
COLUMN column_name ENCODING ( storage_directive [, … ] ), …
DEFAULT COLUMN ENCODING ( storage_directive [, … ] )
例如:
C1 char ENCODING (compresstype=quicklz, blocksize=65536)
COLUMN C1 ENCODING (compresstype=quicklz, blocksize=65536)
DEFAULT COLUMN ENCODING (compresstype=quicklz)


示例:
CREATE TABLE T1 (
    c1 int ENCODING (compresstype=zlib),
    c2 char ENCODING (compresstype=quicklz, blocksize=65536),
    c3 char)
WITH (appendonly=true, orientation=column);


CREATE TABLE T2 (
    c1 int ENCODING (compresstype=zlib),
    c2 char ENCODING (compresstype=quicklz, blocksize=65536),
    c3 char, COLUMN c3 ENCODING (RLE_TYPE) )
WITH (appendonly=true, orientation=column);


CREATE TABLE T3 (
    c1 int ENCODING (compresstype=zlib),
    c2 char ENCODING (compresstype=quicklz, blocksize=65536),
    c3 char,
    COLUMN c3 ENCODING (compresstype=RLE_TYPE))
WITH (appendonly=true, orientation=column)
PARTITION BY RANGE (c3) (
    START ('1900-01-01'::DATE) END ('2100-12-31'::DATE),
    COLUMN c3 ENCODING (zlib) #实际上c3使用的是ZLIB压缩而非RLE_TYPE压缩
);


CREATE TABLE T4 (
    c1 int ENCODING (compresstype=zlib),
    c2 char,  #将从DEFAULT COLUMN ENCODING子句继承压缩方式(QUICKLZ)和块尺寸(65536)
    c3 char,  #压缩方式(RLE_TYPE), 块尺寸(65536)从DEFAULT COLUMN ENCODING子句继承而来
    c4 smallint ENCODING (compresstype=none), #而块尺寸没有显式的复写设置,因此,其块尺寸为65536.
    DEFAULT COLUMN ENCODING (compresstype=quicklz, blocksize=65536),
    COLUMN c3 ENCODING (compresstype=RLE_TYPE) )
WITH (appendonly=true, orientation=column);




CREATE TABLE T5 (
    i int,
    j int,
    k date,
    DEFAULT COLUMN ENCODING (blocksize=1048576) --1MB
) WITH (appendonly = true, orientation=column)
PARTITION BY RANGE(k)
  SUBPARTITION BY LIST(j)
    SUBPARTITION TEMPLATE
    (
      SUBPARTITION one_two VALUES(1, 2) COLUMN j ENCODING (compresstype=RLE_TYPE),
      SUBPARTITION rest VALUES(3, 4, 5, 6, 7, 8, 9) COLUMN j ENCODING (compresstype=zlib, compresslevel=9),
      DEFAULT COLUMN ENCODING (compresstype=quicklz)
    )
(
    START (date '2011-01-01') END (date '2011-12-31')
    EVERY (interval '1 day')
);




通过TYPE命令的方式设置压缩配置
使用精简的方式创建压缩表:
CREATE TABLE t2 (c1 comptype) WITH (APPENDONLY=true, ORIENTATION=column);
这里的comptype的定义为:
CREATE TYPE comptype (
    internallength = 4,
    input = comptype_in,
    output = comptype_out,
    alignment = int4,
    default = 123,
    passedbyvalue,
    compresstype="quicklz",
    blocksize=65536,
    compresslevel=1
);


不建议使用这种不明显的方式,虽然在定义TABLE时看起来精简了不少,但对于别人来说,阅读和理解可能都存在障碍。另外替代原生TYPE的定义未必适应所有情况。建议慎用。




选择块尺寸
在一个TABLE中,每个块尺寸意味着相应数量byte的存储。块尺寸必须在8192到2097152之间,并且必须是8192的整数倍。缺省值为32768。 
需要注意的是,指定大的块大小会消耗大量的内存资源。块尺寸决定着存储层的尺寸, 在GP中,每个块作为一部分数据来维护,因此多分区表和列存储表都会消耗更多的内存。




6) 改变表的分布
下面的命令在所有Segment之间按照customer_id作为DK重分布sales表:
ALTER TABLE sales SET DISTRIBUTED BY (customer_id);


在修改TABLE的HASH分布时,表数据会自动重新分布。如果将分布策略改为随机分布时也会重新分布数据。例如:


测试:
template1=# \d+ foo
            Append-Only Table "public.foo"
 Column |  Type   | Modifiers | Storage  | Description 
--------+---------+-----------+----------+-------------
 a      | integer |           | plain    | 
 b      | text    |           | extended | 
Compression Type: zlib
Compression Level: 5
Block Size: 32768
Checksum: t
Has OIDs: no
Options: appendonly=true, compresstype=zlib, compresslevel=5
Distributed by: (a)


template1=# select get_ao_distribution('foo');
 get_ao_distribution 
---------------------
 (1,2500033)
 (0,2499970)
 (2,2499974)
 (3,2500037)
(4 rows)


template1=# ALTER TABLE foo SET DISTRIBUTED RANDOMLY; #重新设置分布策略
 
template1=# select get_ao_distribution('foo');
 get_ao_distribution 
---------------------
 (1,2500003)
 (3,2500004)
 (0,2500002)
 (2,2500005)
(4 rows)


template1=# ALTER TABLE foo SET WITH (REORGANIZE=TRUE); #重分布数据
template1=# select get_ao_distribution('foo');
 get_ao_distribution 
---------------------
 (3,2500004)
 (0,2500003)
 (1,2500004)
 (2,2500003)
(4 rows)




template1=# ALTER TABLE foo SET DISTRIBUTED BY (a);  #重新设置分布策略
ALTER TABLE
template1=# select get_ao_distribution('foo');
 get_ao_distribution 
---------------------
 (1,2500033)
 (2,2499974)
 (3,2500037)
 (0,2499970)
(4 rows)


重分布表数据
对于随机分布策略或者不改变分布策略的表,要重分布TABLE的数据,使用REORGANIZW=TRUE。
这在处理数据倾斜问题时可能是很必要的,在添加新的Segment节点资源时也是必要的。
ALTER TABLE sales SET WITH (REORGANIZE=TRUE);
该命令会在Instance之间按照现有的分布策略(包括随机分布策略)重新平衡表中数据。


7) 修改表的存储模式
在TABLE被创建之后,修改表的存储模式是不可能的。存储模式只能在CREATE TABLE时被指定。
如果要修改现有表的存储模式,必须使用正确的存储选项重建该表,重新加载数据到新的表,删除旧的表,修改新表为旧的表名。另外还必须重新授权表的权限。


CREATE TABLE sales2 (LIKE sales)
WITH (appendonly=true, compresstype=quicklz, compresslevel=1, orientation=column);
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;




8) 在现有表上添加压缩列
可以使用ALTER TABLE命令来添加一个压缩列。
ALTER TABLE T1 ADD COLUMN c4 int DEFAULT 0 ENCODING (COMPRESSTYPE=zlib);


9) 继承压缩设置
创建一个带子分区设置的表,然后增加一个分区:
CREATE TABLE ccddl (i int, j int, k int, l int)
WITH (APPENDONLY = TRUE, ORIENTATION=COLUMN)
PARTITION BY range(j)
  SUBPARTITION BY list (k)
    SUBPARTITION template(
      SUBPARTITION sp1 values(1, 2, 3, 4, 5),
      COLUMN i ENCODING(COMPRESSTYPE=ZLIB),
      COLUMN j ENCODING(COMPRESSTYPE=lz4), #Vitesse DeepGreen does not support quicklz.  Please use lz4 or set vitesse.lz4_replace_quicklz. 
      COLUMN k ENCODING(COMPRESSTYPE=ZLIB),
      COLUMN l ENCODING(COMPRESSTYPE=ZLIB))
( PARTITION p1 START(1) END(10), 
  PARTITION p2 START(10) END(20)
);


ALTER TABLE ccddl ADD PARTITION p3 START(20) END(30);






10) 分区大表
表分区用以解决特别大的表的问题,比如事实表,解决办法就是将表分成很多小且更容易管理的部分。
在CREATE TABLE时使用PARTITION BY(以及可选的SUBPARTITION BY)子句来做分区。在GPDB中对一张表做分区,实际上是创建了一张顶层(父级)表和多个低层
(子级)表。 在内部, GPDB在顶级表与低级表之间创建了继承关系(类似于PostgreSQL中的继承/INHERIT功能)。


GPDB支持范围(根据数值型的范围分割数据,比如日期或价格)分区和列表(根据值列表分区,比如区域或生产线)分区,或者两种类型的结合。


表分区是一种大表逻辑切分和数据仓库任务的工具。分区本身不会改变Instance间物理上的数据分布规律。


决定表分区的策略:
表是否足够大? 
对目前的性能不满意?
查询条件是否能匹配分区条件? 
数据仓库是否需要滚动历史数据? 
按照某个规则数据是否可以被均匀的分拆? 


定义日期范围分区表
日期范围分区表使用单个date或者timestamp字段作为分区键。 
可以通过使用START值、 END值和EVERY子句定义分区增量让GPDB自动产生分区。
缺省情况下, START值总是被包含而END值总是被排除。
CREATE TABLE sales (id int, date date, amt decimal(10,2)) 
DISTRIBUTED BY (id)
PARTITION BY RANGE (date)
( START (date '2008-01-01') INCLUSIVE
END (date '2009-01-01') EXCLUSIVE
EVERY (INTERVAL '1 day') );




不过也可以为每个分区单独指定名称。比如:
CREATE TABLE sales (id int, date date, amt decimal(10,2))
DISTRIBUTED BY (id)
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 
);


上面的分区的范围都是连续的,如果不连续需要指定end值。


定义数字范围分区表
数字范围分区表使用单个数字列作为分区键。例如:
CREATE TABLE rank (id int, rank int, year int, gender char(1), count int)
DISTRIBUTED BY (id)
PARTITION BY RANGE (year)
( START (2001) END (2008) EVERY (1),
  DEFAULT PARTITION extra 
);




定义列表分区表
列表分区表可以使用任何数据类型的列作为分区键,分区规则使用等值比较。
列表分区可以使用多个COLUMN(组合起来)作为分区键,而范围分区只允许使用单独COLUMN作为分区键。对于列表分区,必须为每个分区指定相应的值。


CREATE TABLE rank (id int, rank int, year int, gender char(1), count int )
DISTRIBUTED BY (id)
PARTITION BY LIST (gender)
( PARTITION girls VALUES ('F'),
PARTITION boys VALUES ('M'),
DEFAULT PARTITION other );




定义多级分区表
使用subpartition template来确保每个分区具有相同的子分区结构,尤其是对那些后增加的分区来说。
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 '2008-01-01') INCLUSIVE END (date '2009-01-01') EXCLUSIVE
  EVERY (INTERVAL '1 month'), 
  DEFAULT PARTITION outlying_dates 
);




下面是一个3级分区表的例子,这里表sales被分区为年、月、区域。 SUBPARTITION TEMPLATE子句确保每个年分区有相同的子分区结构。
另外,每个级别的分区都有一个默认分区:
CREATE TABLE sales (id int, year int, month int, day int, region text) DISTRIBUTED BY (id)
PARTITION BY RANGE (year)
SUBPARTITION BY RANGE (month)
  SUBPARTITION TEMPLATE 
  (
    START (1) END (13) EVERY (1),
    DEFAULT SUBPARTITION other_months 
  )
SUBPARTITION BY LIST (region)
  SUBPARTITION TEMPLATE 
  (
    SUBPARTITION usa VALUES ('usa'),
    SUBPARTITION europe VALUES ('europe'),
    SUBPARTITION asia VALUES ('asia'),
    DEFAULT SUBPARTITION other_regions 
  )
( START (2002) END (2010) EVERY (1), 
  DEFAULT PARTITION outlying_years 
);




将现有表分区
对已经创建的表是不能分区的。只能在CREATE TABLE的时候做分区。要想对现有的表做分区,只能重新创建一个分区表、重新装载数据到新的分区表中、删
掉旧表然后把新的分区表改为旧表的名称。还必须重新对TABLE做授权。
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;


分区表的限制
主键或者唯一约束必须包含表上的所有分区键。而唯一索引可以不包含分区键,
但是,其只对一个分区强制有效,而不是对整个分区表有效。




装载分区表
一旦创建了分区表,顶级表总是空的。数据值储存在最低层的表中。在多级分区表中,仅仅在层级最低的子分区中有数据。
在运行期间,查询规划器会扫描整个TABLE的层级结构并使用CHECK约束适配查询条件来决定哪些子表需要被扫描。
默认分区(只要该层级中存在)总是会被扫描。如果默认分区中包含数据,其会拖慢整体的扫表时间。


如果有必要,还可以直接把数据装载到子表中。还可以先创建一个中间表、装载数据、然后与分区表进行分区交换。这种分区交换的性能高于直接的COPY和INSERT。




查看分区设计
要查看分区表的设计情况,通过pg_partitions视图查看。
SELECT partitionboundary, partitiontablename, partitionname, partitionlevel, partitionrank
FROM pg_partitions WHERE tablename='sales2';


pg_partition_templates - 用以创建SUBPARTITION的SUBPARTITION template
pg_partition_columns – 用于分区的分区键




维护分区表
必须使用ALTER TABLE命令从顶级表来维护分区。 最常见的场景是根据日期范围的设计来维护数据时,删除旧分区并添加一个新的分区。
还有一种可能就是把旧的分区交换为压缩AO表以节省空间。 若在父表中存在默认分区,添加分区的操作只能是从默认分区拆分出一个新的分区。


由于分区不要求有名称,若分区没有名称,下面的表达式仍可以指定一个分区:
PARTITION FOR (value) or PARTITION FOR(RANK(number))
(1) 添加新分区
如果原有的分区表包含了subpartition template设计,新增的分区将根据该模版创建子分区。
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 '2008-01-01') INCLUSIVE END (date '2009-01-01') EXCLUSIVE
  EVERY (INTERVAL '1 month'), 
  DEFAULT PARTITION outlying_dates 
);


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




如果在创建TABLE时没有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') );


子表的名称格式如下:
<父表名称>_<分区层级>_prt_<分区名称>


子表的名称不能通过直接执行ALTER表名来实现。但修改顶级表的名称,该改变将会影响所有相关的分区表。


添加缺省分区
ALTER TABLE sales ADD DEFAULT PARTITION other;
如果是多级分区表, 同一层次中的每个分区都需要一个默认分区。
ALTER TABLE sales ALTER PARTITION FOR (RANK(1)) ADD DEFAULT PARTITION other;
ALTER TABLE sales ALTER PARTITION FOR (RANK(2)) ADD DEFAULT PARTITION other;
ALTER TABLE sales ALTER PARTITION FOR (RANK(3)) ADD DEFAULT PARTITION other;
RANK(partitionrank)指的是范围分区同一层级中的顺序。partitionrank可参见pg_partition表。


(2)删除分区
ALTER TABLE sales DROP PARTITION FOR (RANK(1));


注意: 在将RANK(1)的分区删除后,其余分区的partitionrank值仍然是从1开始的连续编号。 编号的顺序按照分区字段的值由小到大从1开始排序。
不管分区是否连续(中间有值不匹配分区),或者随意的修改分区定义。


(3)清空分区数据
在清空一个包含子分区的分区时,其所有相关子分区的数据都自动被清空。
ALTER TABLE sales TRUNCATE PARTITION FOR (RANK(1));


(4)交换分区:
CREATE TABLE jan08 (LIKE sales) WITH (appendonly=true);
INSERT INTO jan08 SELECT * FROM sales_1_prt_1 ;
ALTER TABLE sales EXCHANGE PARTITION FOR (DATE '2008-01-01') WITH TABLE jan08




(5)拆分分区
拆分分区是将现有的一个分区分成两个分区。 使用ALTER TABLE命令来拆分分区。只能拆分最低层级的分区表(只有包含数据的分区可以拆分)。
指定的分割值对应的数据将进入后面一个分区(就是STAER为INCLUSIVE)。
ALTER TABLE sales SPLIT PARTITION FOR ('2008-01-01')
AT ('2008-01-16') INTO (PARTITION jan081to15, PARTITION jan0816to31);


如果分区表有默认分区,要添加新的分区只能从默认分区拆分。而且只能从最低层级分区的默认分区拆分(只有包含数据的分区可以拆分)。 
在使用INTO子句时,第2个分区名称必须是已经存在的默认分区。
ALTER TABLE sales SPLIT DEFAULT PARTITION
START ('2009-01-01') INCLUSIVE
END ('2009-02-01') EXCLUSIVE
INTO (PARTITION jan09, default partition);




(6)修改子分区模版
使用ALTER TABLE SET SUBPARTITION TEMPLATE命令来修改现有分区表的子分区模版。 在修改了子分区模版之后添加的分区,
其子分区将按照新的模版产生。已经存在的分区不会被修改。
ALTER TABLE sales SET SUBPARTITION TEMPLATE
(   SUBPARTITION usa VALUES ('usa'),
    SUBPARTITION asia VALUES ('asia'),
    SUBPARTITION europe VALUES ('europe'),
    SUBPARTITION africa VALUES ('africa')
    DEFAULT SUBPARTITION other 
);
ALTER TABLE sales ADD PARTITION sales_prt_3
START ('2009-03-01') INCLUSIVE END ('2009-04-01') EXCLUSIVE;


这个例子在一级分区有默认分区时是不能执行的,要查看效果,先删除默认分区。
要删除子分区模版,使用SET SUBPart TEMPLATE并使用空的参数来完成。
ALTER TABLE sales SET SUBPARTITION TEMPLATE ();




11) 创建与使用序列


(1)创建序列
CREATE SEQUENCE myserial START 101;


(2)使用序列
获取序列的下一个值并插入表中:
INSERT INTO vendors VALUES (nextval('myserial'), 'acme');


可以使用setval函数重置一个序列计数器的值。例如:
SELECT setval('myserial', 201);


注意,如果启用了镜像功能, nextval函数不允许在UPDATE和DELETE语句中被使用,另外currval和lastval函数目前未被GPDB支持。


检查序列当前的计数设置,可以直接查询该序列表:
SELECT * FROM myserial;


(3)修改序列
ALTER SEQUENCE myserial RESTART WITH 105;




12) 索引


在创建索引时需要综合考虑的问题:
查询工作负载
压缩表
避免在频繁更新的列上使用索引
创建选择性B-tree索引
低选择性列上使用位图索引
索引列用于关联
索引列经常用在查询条件中


(1)创建索引
CREATE INDEX title_idx ON films (title);
CREATE INDEX gender_bmp_idx ON employee USING bitmap (gender);


使用EXPLAIN ANALYZE命令对使用索引前后进行计时比较会很有用。


(2)管理索引
更新和删除操作不更新位图索引。因此在删除或者更新了位图索引列之后,可能需要使用REINDEX命令重建索引。
重建表上的全部索引
REINDEX my_table;
重建特定的索引
REINDEX my_index;


(3)删除索引
在装载数据时,通常先删除索引、再装载数据、然后在重新创建索引,这样比直接装载数据要快很多。

你可能感兴趣的:(Greenplum或DeepGreen数据库对象的使用和管理)