Greeplum 系列(三) 基本用法

Greeplum 系列(三) 基本用法

一、Greeplum 登陆与创建

1.1 登陆

psql -d test -h 127.0.0.1 -p 5432 -U gpadmin

注意:默认登陆的表名为 gpadmin

1.2 创建数据库

create database test;   # 需要登陆 psql
createdb test;          # 命令行模式

二、创建与管理模式

访问模式的对象:schema.table,缺省 Public 模式

1. 创建和删除模式

create schema sc01;         # 创建 schema    
create schema sc02 authorization dylan;     # 将 schema 权限赋予用户 dylan 
drop schema sc01;           # 删除 schema(空模式)    
drop schema sc01 cascade;   # 删除模式及相关的所有对象

2. 查看模式

查看所有的模式为 \dn

           List of schemas
        Name        |  Owner  
--------------------+---------
 gp_toolkit         | gpadmin
 information_schema | gpadmin
 pg_aoseg           | gpadmin
 pg_bitmapindex     | gpadmin
 pg_catalog         | gpadmin
 pg_toast           | gpadmin
 public             | gpadmin

3. 设置模式搜索路径

若不想通过指定模式名称的方式来搜索需要的对象,可以通过设置 search_path 的方式来实现,第一个模式为缺省。

# 查看当前的搜索路径
select current_schema();
show search_path;
# 设置搜索路径
# 通过ALTER DATABASE修改DB的模式搜索路径
alter database test set search_path to sc01,public,pg_catalog;
# 通过 alter role 修改 ROLE(User) 的模式搜索路径
alter role dylan set search_path to sc01,public,pg_catalog;

注意: 修改表的搜索路径后需要重新登陆才会生效,即不会在当前会话生效。

4. 系统模式

  1. pg_catalog 模式:存储系统日志表、内置类型、函数和运算符。
  2. Information_schema 模式:由一个标准化视图构成。其中包含DB中对象的信息。
  3. pg_toast 模式:是存储大对象(系统内部使用)
  4. pg_bitmapindex 模式:存储bitmap index对象(系统内部使用)
  5. pg_aoseg 模式:存储append-only表(系统内部使用)
  6. gp_toolkit 模式:是管理用的模式,可以查看和检索系统日志文件和其他系统信息。

三、创建与管理表

通过 CREATE TABLE 命令实现,在创建表时,通过需要定义以下几点:

  1. 哪些列及数据类型
  2. 约束限定可以存储什么样的数据
  3. 表的分布策略
  4. 存储方式。比如压缩、列存储等
  5. 大表的分区策略

3.1 数据类型

  1. 对于字符类型,多数选择 TEXT 或者 VARCHAR
  2. 对于数值类型,尽量选择更小的数据类型
  3. 对于打算用作连接的列,选择相同的数据类型
create table tb_products(
    -- 1. 主键约束,唯一约束和非空约束的综合体。默认成为 DK(Distributed Key)
    id integer integer PRIMARY KEY,
    -- 2. 唯一约束,确保字段的数据在表中唯一
    product_no integer UNIQUE, 
    -- 3. 非空约束,不可以存在空值
    name text  NOT NULL, 
    -- 4. 检查约束,过制定数据必须满足一个布尔表达式来约束
    price numeric CHECK(price>0)
    -- 5. 外键约束,GPDB 目前不支持。
);

注意: 主键约束与唯一约束只有出现一个。

3.2 分布策略

在 create table 和 alter table 的时候使用 DISTRIBUTED BY(HASH 分布)或 DISTRIBUTED RANDOMLY(随机分布)来决定数据如何分布。考虑要点:

  1. 均匀的数据分布:尽量确保每个 segment 实例存储了等量的数据;尽可能使用具有唯一性的 DK,比如主键、唯一键等。
  2. 本地操作与分布式操作:确保查询的处理(关联、排序、聚合等)尽可能在每个实例的本地完成,避免数据重分布;不同表尽量使用相同DK,避免使用随机分布。
  3. 均衡的查询处理:尽可能确保每个 segment 实例能处理等量的工作负载。

声明分布键:

  1. 在创建或者修改表定义的时候指定;
  2. 如果没有指定,系统会依次考虑使用主键或第一个字段作为HASH分布的DK;
  3. 几何类型或自定义类型的列不适合作为GP的DK。
  4. 如果没有合适类型的列可以保证数据平均分布,则使用随机分布。
create table tb_dk_01(a int, b int) distributed by(b);
create table tb_dk_02(a int, b int);
create table tb_dk_03(a int primary key, b int);
create table tb_dk_04(a int, b int) distributed randomly;

# 查看表分布键
\d tb_dk_01

3.3 存储模式

选择堆存储(Heap)或只追加(Append-Only)存储

  1. 堆存储适合数据经常变化的小表,比如维度表;
  2. 只追加存储适合仓库中事实大表,通常是批量装载数据并只进行只读查询操作,不支持 UPADTE 和 DELETE 操作。
# 创建堆表,缺省的存储模式
CREATE TABLE tb_heap_01(id int) DISTRIBUTED BY (id)
# 创建只追加表
CREATE TABLE tb_ao_01(id int) WITH(appendonly=true)

3.4 行存储与列存储

选择行存储(Row-Orientation)或列存储(Column-Orientation),考虑因素:

  1. 表数据的更新:只能选择行存储。
  2. 如果经常有数据被 INSERT:考虑选择行存储。
  3. 查询设计的列数量:

    如果在 SELECT 或 WHERE 中涉及表的全部或大部分列时,考虑行存储。列存储适用于在 WHERE 或 HAVING 中队单列作聚合操作:

    SELECT SUM(salary)
    SELECT AVG(salary)…WHERE salary>10000

    或在 WHERE 条件中使用单个列条件且返回少量的行使用压缩存储

    SELECT salary, dept…WHERE state=‘CA’

  4. 表的列数量:行存储对于列多或行尺寸相对小的表更高效;列存储在只访问宽表的少量列的查询中性能更高。
  5. 压缩:列存储表具有压缩优势。

-- 创建列存储时,只能为追加(Append-Only)存储
create table tb_col_01(a int, b text) with (appendonly=true, orientation=column) distributed by (a);

3.5 压缩存储(只支持Append-only表)

两种压缩方式:表级压缩和列级压缩。

(1) 选择压缩方式和级别的考虑因素:

  1. CPU性能
  2. 压缩比
  3. 压缩速度
  4. 解压速度或查询效率

应保证不会显著提高压缩时间和查询效率的前提下最有效的压缩减少数据尺寸。ZLIB 压缩率高于 QUICKLZ,但速度较低。

-- 注意:QUICKLZ 只有一种压缩级别,即没有 compresslevel 参数,而 ZLIB 有 1-9 可选。
create table tb_zlib_01(a int, b text) with (appendonly=true, compresstype=zlib, compresslevel=5);

(2) 检查 AO 表的压缩和分布情况:

# 查看表的分布情况,只能用于Append-Only表
select get_ao_distribution('表名');
# 查看表的压缩率,只能用于Append-Only表
select get_ao_compression_ratio('表名');
select gp_segment_id, count(1) from 表名 group by 1;

(3) 参数

  1. COMPRESSTYPE:ZLIB(更高压缩率)、QUICKLZ(更快压缩)、RLE_TYPE(运行长度编码)、none(无压缩、缺省)
  2. COMPRESSLEVEL:ZLIB为1-9级可选,1级最低,9级最高;QUICKLZ仅1级压缩可选;RLE_TYPE为1-4级可选,1级快但压缩率低,4级较慢但压缩率高
  3. BLOCKSIZE:8K~2M

(4) 压缩设置的优先级

在越低级别的设置具有越高的优先级:

  1. 子分区的列压缩设置将覆盖分区、列和表级的设置
  2. 分区的列压缩设置将覆盖列和表级的设置
  3. 列的压缩设置将覆盖整个表级的设置

注意: 存储设置不可以被继承

create table tb_t3 (
    c1 int encoding(compresstype=zlib),
    c2 text,
    c3 text encoding(compresstype=rle_type),
    c4 smallint encoding(compresstype=none),
    defalut column encoding(compresstype=quicklz, blocksize=65536)
)
with(appendonly=true, orientation=column)
partition by range(c3)
(
    start('2010-01-01'::date) end('2010-12-31'::date), 
    column c3 encoding(compresstype=zlib)
);

3.6 变更表

alter table 命令用于改变现有表的定义。例如:

alter table tb_01 alter column a set not null;

(1) 修改分布策略

-- 数据会重分布,并递归地应用于所有子分区
alter table tb_01 set distributed by(b);
-- 注意:将分布策略改为随机分布时不会重新分布数据
alter table tb_01 set distributed randomly;

(2) 重分布表数据

# 随机分布策略或者不改变分布策略的表,强行重分布
alter table tb_01 set with(reorganize=true);

(3) 修改表的存储模式

存储模式只能在CREATE TABLE时被指定。如果要修改,必须使用正确的存储选项重建该表。

CREATE TABLE tb_zlib_02 (like tb_zlip_01) WITH (appendonly=true, compresstype=quicklz, compresslevel=1, orientation=column);
INSERT INTO tb_zlib_02 SELECT * FROM tb_zlip_01;
DROP TABLE tb_zlip_01;
ALTER TABLE tb_zlib_02 RENAME TO tb_zlib_01;
GRANT ALL PRIVILEGES ON tb_zlib_01 TO admin;
GRANT SELECT ON tb_zlib_01 TO dylan;

(4) 添加压缩列

ALTER TABLE tb_zlib_01 ADD COLUMN c int DEFAULT 0 ENCODING (COMPRESSTYPE=zlib);

(5) 删除表

-- 删除与表相关的视图,必须使用CASCADE
DROP TABLE tb01;
DROP TABLE tb03 CASCADE;

-- 使用 DELETE 或 TRUNCATE 清空表记录
DELETE FROM tb02;
TRUNCATE tb02;

四、分区表

一张大表逻辑性地分成多个部分,如按照分区条件进行查询,将减少数据的扫描范围,提高系统性能。提高对于特定类型数据的查询速度和性能,更方便数据库的维护和更新。

决定表的分区策略:

  1. 表是否足够大?大的事实表适合做表分区。
  2. 对目前的性能不满意?查询性能低于预期时再考虑分区。
  3. 查询条件是否能匹配分区条件?查询语句的WHERE条件是否与考虑分区的列一致
  4. 数据仓库是否需要滚动历史数据?历史数据的滚动需求也是分区设计的考虑因素
  5. 按照某个规则数据是否可以被均匀的分拆?尽量把数据均匀分拆的规则

4.1 分区表类型

  • Range分区: (日期范围或数字范围)/如日期、价格等
  • List 分区: 例如地区、产品等

图3-4-1 分区表存储

4.2 创建分区

TABLE 只能在 CREATE TABLE 时被分区。第一步要选择分区类型(范围分区、列表分区)和分区字段

(1) 定义日期范围分区表(range分区)

使用单个 date 或者 timestamp 字段作为分区键。如果需要,还可以使用同样的字段做子分区。通过使用 START、END 和 EVERY 子句定义分区增量让 GP 自动产生分区。

CREATE TABLE tb_cp_01 (id int, date date, amt decimal(10, 2))
DISTRIBUTED BY (id)
PARTITION BY RANGE (date)
(
    -- (默认行为)INCLUSIVE:包含 2013-01-01;EXCLUSIVE:不包含 2014-01-01
    START (date '2013-01-01') INCLUSIVE
    END (date '2014-01-01') EXCLUSIVE 
    EVERY (INTERVAL '1 month') 
);

也可以为每个分区单独制定名称

CREATE TABLE tb_cp_02 (id int, date date, amt decimal(10, 2))
DISTRIBUTED BY (id)
PARTITION BY RANGE (date)
( 
    PARTITION Jan13 START (date '2013-01-01') INCLUSIVE, 
    PARTITION Feb13 START (date '2013-02-01') INCLUSIVE, 
    PARTITION Mar13 START (date '2013-03-01') INCLUSIVE, 
    PARTITION Apr13 START (date '2013-04-01') INCLUSIVE, 
    PARTITION May13 START (date '2013-05-01') INCLUSIVE, 
    PARTITION Jun13 START (date '2013-06-01') INCLUSIVE, 
    PARTITION Jul13 START (date '2013-07-01') INCLUSIVE, 
    PARTITION Aug13 START (date '2013-08-01') INCLUSIVE, 
    PARTITION Sep13 START (date '2013-09-01') INCLUSIVE, 
    PARTITION Oct13 START (date '2013-10-01') INCLUSIVE, 
    PARTITION Nov13 START (date '2013-11-01') INCLUSIVE, 
    PARTITION Dec13 START (date '2013-12-01') INCLUSIVE 
    END (date '2014-01-01') EXCLUSIVE 
);

(2) 定义数字范围分区表

CREATE TABLE tb_cp_03 (id int, rank int, year int, gender char(1), count int)
DISTRIBUTED BY (id)
PARTITION BY RANGE (year)
( 
    START (2010) END (2014) EVERY (1),
    DEFAULT PARTITION extra 
);

(3) 创建列表分区表(list分区)

可以使用任何数据类型的列作为分区键;可以使用多个列组合作为分区键。

CREATE TABLE tb_cp_04 (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 
);

(4) 定义多级分区表

当需要子分区时,可以使用多级分区的设计。

CREATE TABLE tb_cp_05 (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 europe VALUES ('europe'),
    DEFAULT SUBPARTITION other_regions
)
(
    -- 主分区 
    START (date '2013-09-01') INCLUSIVE
    END (date '2014-01-01') EXCLUSIVE
    EVERY (INTERVAL '1 month'),
    DEFAULT PARTITION outlying_dates 
);

创建3级子分区表,被分区为年、月、区域三层。

CREATE TABLE tb_cp_06 (id int, year int, month int, day int, region text)
DISTRIBUTED BY (id)
PARTITION BY RANGE (year)
SUBPARTITION BY RANGE (month)
SUBPARTITION TEMPLATE (
    -- 定义二级分区(2个 + default)  
    START (1) END (3) EVERY (1),
    DEFAULT SUBPARTITION other_months)
SUBPARTITION BY LIST (region)
SUBPARTITION TEMPLATE (
    -- 定义三级分区(2个 + default) 
    SUBPARTITION usa VALUES ('usa'),
    SUBPARTITION europe VALUES ('europe'),
    DEFAULT SUBPARTITION other_regions)
( 
    -- 定义一级分区(2个 + default) 
    START (2012) END (2014) EVERY (1),
    DEFAULT PARTITION outlying_years 
); 

4.3 查看分区设计

通过 pg_partitions 视图查看分区表设计情况。

SELECT partitionboundary, partitiontablename, partitionname, partitionlevel, partitionrank 
FROM pg_partitions WHERE tablename='tb_cp_05';

如下视图也可以查看分区表的信息:

  1. 查看创建 SUBPARTITION 的 pg_partition_templates
  2. 查看分区表的分区键 pg_partition_columns

4.4 维护分区表

必须使用 ALTER TABLE 命令从顶级表来维护分区。

(1) 添加新分区

原分区表包含 subpartition template 设计:

ALTER TABLE tb_cp_05 DROP DEFAULT PARTITION;
ALTER TABLE tb_cp_05 ADD PARTITION START (date '2014-01-01') INCLUSIVE END (date '2014-02-01') EXCLUSIVE;

原分区不包含 subpartition template 设计:

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

注意:如果存在默认分区,只能从默认分区中拆分新的分区

(2) 重命名分区

GP 中的对象长度限制为 63 个字符,并且受唯一性约束。子表的名称格式:

<父表名称>_<分区层级>_prt_<分区名称>

修改父表名称,将会影响所有分区表

# 对应分区表将会改为:tbcp05_1_prt_5
ALTER TABLE tb_cp_05 rename to tbcp05;

只修改分区名称:

# 对应分区表将会改为:tbcp05_1_prt_jun13
ALTER TABLE tbcp05 RENAME PARTITION FOR('2013-06-01') TO Jun13;

(3) 删除分区

# 删除指定的分区
ALTER TABLE tb_cp_04 DROP PARTITION other;
# 删除默认分区:
ALTER TABLE tb_cp_04 DROP DEFAULT PARTITION; 
# 对于多级分区表,为同一层每一个分区删除默认分区:
ALTER TABLE tb_cp_06 ALTER PARTITION FOR (RANK(1)) DROP DEFAULT PARTITION;
ALTER TABLE tb_cp_06 ALTER PARTITION FOR (RANK(2)) DROP DEFAULT PARTITION;

(4) 添加默认分区

# 使用ALTER TABLE命令添加默认分区:
ALTER TABLE tbcp05 ADD DEFAULT PARTITION other;
# 如果是多级分区表,同一层每个分区都需要默认分区:
ALTER TABLE tb_cp_06 ALTER PARTITION FOR (RANK(1)) ADD DEFAULT PARTITION other;
ALTER TABLE tb_cp_06 ALTER PARTITION FOR (RANK(2)) ADD DEFAULT PARTITION other;

(5) 清空分区数据

# 使用ALTER TABLE命令来清空分区。
ALTER TABLE tbcp05 TRUNCATE PARTITION FOR (RANK(1));

(6) 交换分区

交换分区是用一个普通的 TABLE 与现有的分区交换身份。使用 ALTER TABLE 命令来交换分区。只能交换最低层次的分区表。

CREATE TABLE jan13(LIKE tb_cp_02) WITH(appendonly=true);
INSERT INTO jan13 VALUES(1,'2013-01-15',123.45);
ALTER TABLE tb_cp_02 EXCHANGE PARTITION for(date '2013-01-01') WITH TABLE jan13; 

(7) 拆分分区

使用 ALTER TABLE 命令将现有的一个分区拆分成两个。例如:将一个月分区数据拆分到一个1-15日的分区和另一个16-31日的分区

ALTER TABLE tb_cp_02 SPLIT PARTITION FOR('2013-01-01') AT ('2013-01-16')
    INTO (PARTITION jan131to15, PARTITION jan0816to31);

如果分区表有默认分区,要添加新分区只能从默认分区拆分:

ALTER TABLE tb_cp_03 SPLIT DEFAULT PARTITION
START (2014) INCLUSIVE END (2015) EXCLUSIVE
INTO (PARTITION y2014, DEFAULT PARTITION);

(8) 修改子分区模板

使用 ALTER TABLE SET SUBPARTITION TEMPLATE 命令来修改现在分区表的子分区模板。例如:

ALTER TABLE tb_cp_05 SET SUBPARTITION TEMPLATE
(
    SUBPARTITION usa VALUES('usa'),
    SUBPARTITION africa VALUES('africa'),
    DEFAULT SUBPARTITION other
);

使用新模板后为表 tb_cp_05 添加一个分区,

ALTER TABLE tb_cp_05 ADD PARTITION Feb14 START ('2014-02-01') INCLUSIVE END('2014-03-01') EXCLUSIVE;

4.5 其它

(1) 装载分区表

  1. 分区表中顶级表是空的,数据存储在最底层的表中。
  2. 为避免数据装载失败,可选择定义默认分区。
  3. 查询分区表时,默认分区总是会被扫描,如果默认分区包含数据,会影响查询效率。
  4. 在使用 COPY 或者 INSERT 向父级表装载数据时,数据会自动路由到正确的分区。
  5. 可考虑交换分区的方法直接转载数据到子表,提高性能。

(2) 验证分区策略

# EXPLAIN查看查询计划是否扫描了相关分区
EXPLAIN SELECT * FROM tb_cp_05 WHERE date=‘2013-12-01’ AND region=‘usa’; 

(3) 分区选择性扫描的限制

如果查询计划显示分区表没有被选择性的扫描,可能和以下的限制有关:

  1. 查询计划仅可以对稳定的比较运算符,如:=, <, <=, >, >=, <>
  2. 查询计划不识别非稳定函数来执行选择性扫描。比如,WHERE 子句中使用如 date>CURRENT_DATE 会使查询计划执行分区扫描,而 time>TIMEOFDAY 不会。

五、序列

CREATE SEQUENCE myserial START 101;     -- 创建索引
SELECT * FROM myserial;                 -- 查询索引
SELECT setval(‘myserial’,201);          -- 设置索引
INSERT INTO tb02 VALUES(nextval(‘myserial’), ‘abc’);    -- 使用索引
ALTER SEQUENCE myserial RESTART WITH 105;   -- 修改索引
DROP SEQUENCE myserial;                 -- 删除索引        

注意:如果启用了镜像功能,nextval 不允许在 UPDATE 和 DELETE 语句中被使用

六、索引

在分布式数据库如 GP 中,应保守使用索引。GP会自动为主键建立主键索引,并确保索引在查询工作负载(如表关联、查询)中真正被使用到。

GP中常用的两种:B-tree 和 Bitmap 索引。

CREATE INDEX idx_01 ON tb_cp_02(id);                    -- 创建 B-tree 索引
CREATE INDEX bmidx_01 ON tb_cp_02 USING BITMAP(date);   -- 创建位图索引
REINDEX TABLE tb_cp_02;     -- 重建全部索引
REINDEX INDEX bmidx_01;     -- 重建特定索引
DROP INDEX bmidx_01;        -- 删除索引,在装载数据时,通常先删除索引,再装载数据

七、视图

CREATE VIEW vw_01 AS SELECT * FROM tb_cp_03 WHERE gender=‘F’;   -- 创建视图
DROP VIEW vw_01;    -- 删除视图

每天用心记录一点点。内容也许不重要,但习惯很重要!

猜你喜欢

转载自www.cnblogs.com/binarylei/p/9062415.html