HAWQ技术解析(六) —— 定义对象_数据仓库


欢迎关注大佬的博客,他出版的与greenplum相关书籍《HAWQ数据仓库与数据挖掘实战》、《SQL机器学习库——MADlib技术解析》深入浅出,欢迎大家购买阅读。

HAWQ本质上是一个数据库系统,所以这里所说的对象指的是数据库对象。和其它关系数据库类似,HAWQ中有数据库、表空间、表、视图、自定义数据类型、自定义函数、序列等对象。本篇将简述这些对象的创建与管理。对HAWQ中表的存储方式与分布策略等特性的选择,会对数据库性能产生极大的影响,同时这也是一个复杂的话题,将在下一篇单独讨论。

一、创建和管理数据库

HAWQ中数据库的概念与MySQL类似,一个HAWQ实例中通常会建立多个数据库,这和Oracle中数据库的概念不同。在Oracle体系结构中,数据库是一个“最大”的概念,大多数情况下一个Oracle数据库对应一个实例,RAC是一个数据库对应多个实例。尽管可以在一个HAWQ系统中创建很多数据库,但是客户端程序在某一时刻只能连接到一个数据库,这也决定了HAWQ不能执行跨库的查询。

  1. 模板数据库

HAWQ初始化后, 就有了template0和template1两个模板库,开始时这两个库的内容是一样的,并且template0库和template1都不可删除。两者最主要的区别是,缺省时可以连接template1并在其中创建对象,但不能连接template0。不能删除模板数据库:

gpadmin=# drop database template0;
ERROR: cannot drop a template database
gpadmin=# drop database template1;
ERROR: cannot drop a template database
gpadmin=# drop database postgres;
ERROR: cannot drop a template database

缺省时不能连接template0,但可以连接template1:

gpadmin=# \c template0
FATAL: database "template0" is not currently accepting connections
Previous connection kept
gpadmin=# \c template1
You are now connected to database "template1" as user "gpadmin".

每一个新创建的数据库都基于一个模板,建库时如果不指定 TEMPLATE属性,默认用的是template1模板库。除非希望某些对象在每一个新创建的数据库中都存在,不要在template1中创建任何对象。 template1是默认模板,并且其中的对象和数据会被克隆到每个以它为模板的新建数据库中:

template1=# create table t1 (a int);
CREATE TABLE
template1=# insert into t1 values (1);
INSERT 0 1
template1=# create database db1;
CREATE DATABASE
template1=# \dt
List of relations
Schema | Name | Type | Owner | Storage
--------+------+-------+---------+-------------
public | t1 | table | gpadmin | append only
(1 row)

template1=# select * from t1;
a
---
1
(1 row)

HAWQ还有一个模板库postgres。不要修改template0或postgres,HAWQ内部需要使用它们。以template0为模板可以创建一个完全干净的数据库,其中只包含HAWQ在初始化时预定义的标准对象。如果修改了template1,可能就需要这么做。指定以template0为模板创建数据库:

template1=# create database db2 with template template0;
CREATE DATABASE

通过配置,也可以连接template0:

template1=# set allow_system_table_mods='DML'; 
SET
template1=# update pg_database set datallowconn='t' where datname='template0';
UPDATE 1
template1=# \c template0
You are now connected to database "template0" as user "gpadmin".
template0=# update pg_database set datallowconn='f' where datname='template0';
ERROR: permission denied: "pg_database" is a system catalog
template0=# set allow_system_table_mods='DML';
SET
template0=# update pg_database set datallowconn='f' where datname='template0';
UPDATE 1
template0=# \q
[gpadmin@hdp3 ~]$ psql -d template0
psql: FATAL: database "template0" is not
  1. 创建数据库

创建数据库的用户必须要适当的权限,比如超级用户,或者被设置了createdb角色属性。除了象前面例子中,使用CREATE DATABASE命令创建数据库,还可以使用客户端程序createdb创建一个数据库。例如,运行下面的命令将连接HAWQ主机并创建名为db3的数据库,主机名和端口号必须与HAWQ的master节点相匹配。

[gpadmin@hdp4 ~]$ createdb -h hdp3 -p 5432 db3
[gpadmin@hdp4 ~]$ psql -h hdp3
psql (8.2.15)
Type "help" for help.

gpadmin=# \l
List of databases
Name | Owner | Encoding | Access privileges
-----------+---------+----------+-------------------
db1 | gpadmin | UTF8 |
db2 | gpadmin | UTF8 |
db3 | gpadmin | UTF8 |
gpadmin | gpadmin | UTF8 |
postgres | gpadmin | UTF8 |
template0 | gpadmin | UTF8 |
template1 | gpadmin | UTF8 |
(7 rows)

某些对象,如角色(用户),是被HAWQ中的所有数据库所共享的。而另外一些对象,如表,则只有它所在的数据库能感知它的存在。

  1. 查看数据库列表

psql客户端程序的\l元命令显示数据库列表。如果是数据库超级用户,也可以从pg_database系统目录表中查询数据库列表。

gpadmin=# \l
List of databases
Name | Owner | Encoding | Access privileges
-----------+---------+----------+-------------------
db1 | gpadmin | UTF8 |
db2 | gpadmin | UTF8 |
db3 | gpadmin | UTF8 |
gpadmin | gpadmin | UTF8 |
postgres | gpadmin | UTF8 |
template0 | gpadmin | UTF8 |
template1 | gpadmin | UTF8 |
(7 rows)

gpadmin=# select datname from pg_database;
datname
-----------
hcatalog
template1
postgres
gpadmin
template0
db1
db2
db3
(8 rows)

可以看到,从pg_database查询出的结果比\l命令多返回一个库名为hcatalog。此库仅HAWQ系统使用,并且不允许连接。

gpadmin=# \c hcatalog
FATAL: "hcatalog" database is only for system use
  1. 修改数据库
    ALTER DATABASE命令可以用于修改数据库的缺省配置,如下面的命令修改search_path服务器配置参数,改变数据库db1缺省的模式查找路径。
gpadmin=# alter database db1 set search_path to myschema, public, pg_catalog;
NOTICE: schema "myschema" does not exist
ALTER DATABASE

HAWQ不支持修改数据库改名。

gpadmin=# alter database db1 rename to db11;
ERROR: Cannot support rename database
  1. 删除数据库

DROP DATABASE命令删除一个数据库。它删除数据库在系统目录中的条目,并删除磁盘上的数据。只有数据库属主或超级用户才能删除数据库。并且,不能删除一个还有连接的数据库,包括不能删除自己当前会话连接的数据库。在删除一个数据库前,可先连接到template1或其它数据库。

gpadmin=# \c template1
You are now connected to database "template1" as user "gpadmin".
template1=# drop database db1;
DROP DATABASE

也可以使用客户端程序dropdb删除一个数据库。

[gpadmin@hdp4 ~]$ dropdb -h hdp3 -p 5432

一个数据库有连接时是不允许删除的,必须先终止所有连接,在没有连接之后再删除数据库。

gpadmin=# drop database db3;
ERROR: database "db3" is being accessed by other users
gpadmin=# select procpid,current_query from pg_stat_activity where datname='db3';
procpid | current_query
---------+---------------
790583 | <IDLE>
(1 row)

gpadmin=# select pg_terminate_backend(790583);
pg_terminate_backend
----------------------
t
(1 row)

gpadmin=# drop database db3;
DROP DATABASE

注意,删除数据库操作是不可回滚的。

二、创建和管理表空间

很多数据库系统,如Oracle和MySQL等,都有表空间的概念。HAWQ的表存储在HDFS上,其表空间管理有自己的特点。HAWQ在表空间之上有一个文件空间的概念,系统中所有组件的文件系统位置的集合构成一个文件空间。文件空间可以被一个或多个表空间所使用。实际上,一个文件空间物理上就是一个HDFS的目录及其子目录。在表空间定义中需要指定它所属文件空间。一个文件空间下的所有表空间文件都存储在该文件空间所对应的HDFS目录下。
表空间允许为经常使用和不经常使用的数据库对象赋予不同的存储,或控制特定数据库对象的I/O性能。例如,将经常使用的表放在高性能文件系统 (如SSD)上,而将其它表放在普通标准硬盘上。通过这种方式,DBA可以在HAWQ集群中使用多个HDFS目录,灵活规划数据库对象的物理存储。

  1. 创建文件空间

文件空间是一个符号存储标识符,映射为一组HAWQ主机文件系统的位置,指示HAWQ系统的存储空间。为了创建一个文件空间,需要在HAWQ集群上准备HDFS文件系统目录,然后使用hawq filespace应用程序定义文件空间。必须以数据库超级用户创建一个文件空间。注意,HAWQ并不直接感知底层的文件系统边界。它将文件存储在所指定的目录中,但不能人为控制逻辑文件系统中单个文件的磁盘位置。
(1)为文件空间准备HDFS目录

[root@hdp4 ~]# su - hdfs
[hdfs@hdp4 ~]$ hdfs dfs -mkdir /hawq_data1
[hdfs@hdp4 ~]$ hdfs dfs -chown -R gpadmin:gpadmin /hawq_data1

(2)用gpadmin用户登录HAWQ master

$ su -

(3)创建一个文件空间配置文件:

$ hawq filespace -o hawqfilespace_config

(4)在提示符下,输入文件空间的名字、master文件系统位置和segment文件系统位置。

[gpadmin@hdp3 ~]$ hawq filespace -o hawqfilespace_config
Enter a name for this filespace
> testfs
Enter replica num for filespace. If 0, default replica num is used (default=3)
>

Please specify the DFS location for the filespace (for example: localhost:9000/fs)
location> mycluster/hawq_data1
20170306:11:24:52:352152 hawqfilespace:hdp3:gpadmin-[INFO]:-[created]
20170306:11:24:52:352152 hawqfilespace:hdp3:gpadmin-[INFO]:-
To add this filespace to the database please run the command:
hawqfilespace --config /home/gpadmin/hawqfilespace_config

[gpadmin@hdp3 ~]$ more /home/gpadmin/hawqfilespace_config
filespace:testfs
fsreplica:3
dfs_url::mycluster/hawq_data1
[gpadmin@hdp3 ~]$ hawq filespace --config /home/gpadmin/hawqfilespace_config
Reading Configuration file: '/home/gpadmin/hawqfilespace_config'

CREATE FILESPACE testfs ON hdfs
('mycluster/hawq_data1/testfs') WITH (NUMREPLICA = 3);
20170306:11:25:50:352658 hawqfilespace:hdp3:gpadmin-[INFO]:-Connecting to database
20170306:11:25:50:352658 hawqfilespace:hdp3:gpadmin-[INFO]:-Filespace "testfs" successfully created
此时HDFS上会看到建立了/hawq_data1/testfs目录。
[hdfs@hdp2 ~]$ hdfs dfs -ls /hawq_data1
Found 1 items
drwx------ - gpadmin gpadmin 0 2017-03-07 14:32 /hawq_data1/testfs
  1. 创建表空间
    创建完文件空间,使用CREATE TABLESPACE命令定义一个使用该文件空间的表空间。
gpadmin=# create tablespace testts filespace testfs;
CREATE TABLESPACE

目前HAWQ只允许数据库超级用户定义表空间,并且不支持向其他用户GRANT/REVOKE表空间上的CREATION权限。

gpadmin=# create user wxy with superuser login password 'mypassword';
CREATE ROLE
gpadmin=# grant create on tablespace testts to wxy;
ERROR: Cannot support GRANT/REVOKE on TABLESPACE

相关信息参见 https://issues.apache.org/jira/browse/HAWQ-24。

  1. 使用表空间存储数据库对象

拥有表空间上CREATE权限的用户能够在此表空间中创建数据库对象,例如数据库、表等。使用default_tablespace参数,为没有指定表空间的CREATE TABLE语句指定缺省表空间。
与一个数据库关联的表空间存储数据库的系统目录、数据库的服务器进程创建的临时文件、数据库中创建时没有指定TABLESPACE的表。如果创建数据库时不指定表空间,数据库使用其模板数据库相同的表空间。如果有适当的权限,可以在任意数据库中使用一个表空间。

[gpadmin@hdp3 ~]$ psql -d template1 -U wxy -h hdp3
template1=# create database db1 tablespace testts;
CREATE DATABASE
template1=# \c db1
You are now connected to database "db1" as user "wxy".
db1=# create table t1 (a int);
CREATE TABLE
db1=# create table t2 (a int) tablespace testts;
CREATE TABLE
db1=# set default_tablespace = testts;
SET
db1=# create table t3 (a int);
CREATE TABLE
db1=# set default_tablespace = dfs_default;
SET
db1=# create table t4 (a int);
CREATE TABLE
db1=# select relname,reltablespace from pg_catalog.pg_class where relname in ('t1','t2','t3','t4');
relname | reltablespace
---------+---------------
t1 | 0
t2 | 0
t3 | 0
t4 | 16385
(4 rows)

pg_class.reltablespace为0,说明表保存在从数据库继承的缺省表空间testts里。特别要指出的是,所有非共享的系统表也都存放在这里。

  1. 查看表空间和文件空间
    每个HAWQ系统有以下缺省表空间:pg_global是共享系统目录的表空间。pg_default是缺省表空间,template1和template0数据库使用。这些表空间使用系统缺省的文件空间,pg_system,指示系统初始化时创建的数据目录位置。pg_filespace和pg_filespace_entry目录表存储文件空间信息。可以将这些表与pg_tablespace关联查看完整的表空间的定义,例如:
db1=# select spcname as tblspc, fsname as filespc,
db1-# fsedbid as seg_dbid, fselocation as datadir
db1-# from pg_tablespace pgts, pg_filespace pgfs,
db1-# pg_filespace_entry pgfse
db1-# where pgts.spcfsoid=pgfse.fsefsoid
db1-# and pgfse.fsefsoid=pgfs.oid
db1-# order by tblspc, seg_dbid;
tblspc | filespc | seg_dbid | datadir
-------------+------------+----------+-----------------------------------------------
dfs_default | dfs_system | 0 | hdfs://mycluster/hawq_data
testts | testfs | 0 | hdfs://{replica=3}mycluster/hawq_data1/testfs
(2 rows)
  1. 删除表空间和文件空间
    只有表空间的属主或超级用户可以删除表空间。直到表空间所有的数据库对象都被删除后,才能删除表空间。
postgres=# drop tablespace testts;
ERROR: tablespace "testts" is not empty: existing database.
postgres=# drop filespace testfs;
ERROR: filespace "testfs" is not empty
postgres=# drop database db1;
DROP DATABASE
postgres=# drop filespace testfs;
ERROR: filespace "testfs" is not empty
postgres=# drop tablespace testts;
DROP TABLESPACE
postgres=# drop filespace testfs;
DROP FILESPACE
postgres=#

此时HDFS上的/hawq_data1/testfs目录已经删除。

[hdfs@hdp2 ~]$ hdfs dfs -ls /hawq_data1/testfs
ls: `/hawq_data1/testfs': No such file or directory
[hdfs@hdp2 ~]$

三、创建和管理模式

模式(schema)是一个有趣的概念,不同数据库系统中的模式代表完全不同的东西。如Oracle中,默认在创建用户的时候,就创建了一个和用户名一样的模式,并且互相绑定,因此很多情况下Oracle的用户和模式可以通用。MySQL中的schema是database的同义词。而HAWQ中的模式是从PostgreSQL来的,其概念与SQLserver的模式更为类似,是数据库中的逻辑对象。
HAWQ的模式是数据库中对象和数据的逻辑组织。模式允许在一个数据库中有多个同名的对象,如表。如果对象属于不同的模式,同名对象之间不会冲突。使用schema有如下好处:

  • 方便管理多个用户共享一个数据库,但是又可以互相独立。
  • 方便管理众多对象,更有逻辑性。
  • 方便兼容某些第三方应用程序,如果创建对象时是带schema的。
    比如要设计一个复杂系统,由众多模块构成,有时候模块间又需要有独立性。各模块存放单独的数据库显然是不合适的。这时候使用schema来划分各模块间的对象,再对用户进行适当的权限控制,这样逻辑也非常清晰。
  1. 缺省的“Public”模式

每个数据库有一个缺省的名为public的模式。如果不建立任何模式,对象则被创建在public模式中。所有数据库角色(用户)都具有public模式中的CREATE和USAGE权限。当创建了一个模式,需要给用户授予访问模式的权限。

  1. 创建模式

使用CREATE SCHEMA命令创建一个新模式。为了在模式中创建和访问对象,完整的对象名称由模式名+对象名组成,对象名和模式名称用点号分隔。可以创建一个属于其他人的模式,语法是:CREATE SCHEMA AUTHORIZATION ;

  1. 模式查找路径

可以设置search_path配置参数指定数据库对象有效模式的查找顺序。查找路径列表中的第一个存在的模式为缺省模式。如果没有指定模式,对象在缺省模式中创建。
(1)设置模式查找路径
search_path配置参数设置模式查找顺序。ALTER DATABASE命令设置查找路径
ALTER DATABASE db1 SET search_path TO u1,public, pg_catalog;
(2)查看当前模式
使用current_schema()函数查看当前模式。
SELECT current_schema();
使用SHOW命令查看当前查找路径。
SHOW search_path;
set search_path to my_schema;只能改变当前session,如果需要长久生效可以为用户创建一个变量:
alter role etl set search_path=trade;
官方建议是这样的:在管理员创建一个具体数据库后,应该为所有可以连接到该数据库的用户分别创建一个与用户名相同的模式,然后,将search_path设置为"$user",即默认的模式是与用户名相同的模式。
4. 删除模式
使用DROP SCHEMA命令删除一个模式。DROP SCHEMA myschema;
缺省时,模式必须为空后才能删除它。为了删除一个非空的模式,可以使用:​​​DROP SCHEMA <schemaname> CASCADE​​;该命令将删除模式及该模式下的所有对象(表、数据、函数等等)。

  1. 系统模式
    使用psql的\dn元命令查看当前连接数据库的所有模式。
gpadmin=# \dn
List of schemas
Name | Owner
--------------------+---------
hawq_toolkit | gpadmin
information_schema | gpadmin
pg_aoseg | gpadmin
pg_bitmapindex | gpadmin
pg_catalog | gpadmin
pg_toast | gpadmin
public | gpadmin
(7 rows)

以下是每个数据库中系统级别的模式:
pg_catalog:包含系统目录表,内建数据类型、函数和操作符等。它总是模式查找路径的一部分,即使在查找路径中没有显式命名。
information_schema:由一系列标准视图构成的数据库对象信息。用\dv information_schema.*元命令列出该模式下的视图。这些视图以标准方式从系统目录表获取系统信息。
pg_toast:存储大小超过页尺寸的大对象。这个模式被HAWQ系统内部使用。
pg_bitmapindex:存储位图索引对象,如值列表。该模式被HAWQ系统内部使用。
hawq_toolkit:管理模式,包含可以从SQL命令访问的外部表、视图和函数。所有数据库用户可以访问hawq_toolkit查询系统日志文件或系统指标。
pg_aoseg:存储AO(Append-optimized)类型表对象的信息。该模式被HAWQ系统内部使用。
6. 模式示例

# 修改master的pg_hba.conf文件,增加三个用户u1、u2、u3的认证
[gpadmin@hdp3 ~]$ vi /data/hawq/master/pg_hba.conf
...
host all u1 172.16.1.0/24 md5
host all u2 172.16.1.0/24 md5
host all u3 172.16.1.0/24 md5

# 使认证文件生效
[gpadmin@hdp3 ~]$ more /data/hawq/master/pg_hba.conf

# 创建数据库db1
[gpadmin@hdp3 ~]$ createdb db1

# 使用gpadmin创建两个用户u1、u2,授予超级用户权限,
[gpadmin@hdp3 ~]$ psql -c "create role u1 with superuser password 'mypassword' login;create role u2 with superuser password 'mypassword' login;"

# 使用gpadmin在db1数据库中创建两个与用户u1、u2同名的schema,并指定对应的属主。此情况模拟Oracle的用户模式。
[gpadmin@hdp3 ~]$ psql -d db1 -c "create schema u1 authorization u1; create schema u2 authorization u2;"

# 用u1用户执行
[gpadmin@hdp3 ~]$ psql -d db1 -U u1 -h hdp3 -c "create table t1 (a int); insert into t1 values(1);"

# 用u2用户执行
[gpadmin@hdp3 ~]$ psql -d db1 -U u2 -h hdp3 -c "create table t1 (a int); insert into t1 values(2);"

# 用u1用户执行
[gpadmin@hdp3 ~]$ psql -d db1 -U u1 -h hdp3 -c "select *,current_schema() from t1;"
Password for user u1:
a | current_schema
---+----------------
1 | u1
(1 row)

# 用u2用户执行
[gpadmin@hdp3 ~]$ psql -d db1 -U u2 -h hdp3 -c "select *,current_schema() from t1;"
Password for user u2:
a | current_schema
---+----------------
2 | u2
(1 row)

# 用gpadmin用户执行
[gpadmin@hdp3 ~]$ psql -d db1 -h hdp3 -c "create table t1(a int);insert into t1 values(3);"
INSERT 0 1
[gpadmin@hdp3 ~]$ psql -d db1 -h hdp3 -c "select * from pg_tables where tablename='t1';"
schemaname | tablename | tableowner | tablespace | hasindexes | hasrules | hastriggers
------------+-----------+------------+------------+------------+----------+-------------
u1 | t1 | u1 | | f | f | f
u2 | t1 | u2 | | f | f | f
public | t1 | gpadmin | | f | f | f
(3 rows)

[gpadmin@hdp3 ~]$ psql -d db1
psql (8.2.15)
Type "help" for help.

db1=# show search_path;
search_path
----------------
"$user",public
(1 row)

db1=# select * from t1;
a
---
3
(1 row)

db1=# set search_path='u1';
SET
db1=# select * from t1;
a
---
1
(1 row)

db1=# set search_path='u2';
SET
db1=# select * from t1;
a
---
2
(1 row)

# 建立只有login权限的用户u3
[gpadmin@hdp3 ~]$ psql -c "create role u3 with password 'mypassword' login;"
NOTICE: resource queue required -- using default resource queue "pg_default"
CREATE ROLE
# 用u3用户执行
[gpadmin@hdp3 ~]$ psql -d db1 -U u3 -h hdp3
Password for user u3:
psql: FATAL: password authentication failed for user "u3"
[gpadmin@hdp3 ~]$ psql -d db1 -U u3 -h hdp3
Password for user u3:
psql (8.2.15)
Type "help" for help.

db1=> set search_path='u1';
SET
db1=> db1=> \dt
No relations found.
db1->

# 可以看到,u3看不到表u1.t1

# 赋予usage权限
[gpadmin@hdp3 ~]$ psql -d db1 -c "grant usage on schema u1 to u3;"
GRANT
# 用u3用户执行
[gpadmin@hdp3 ~]$ psql -d db1 -U u3 -h hdp3
Password for user u3:
psql (8.2.15)
Type "help" for help.

db1=> set search_path='u1';
SET
db1=> \dt
List of relations
Schema | Name | Type | Owner | Storage
--------+------+-------+-------+-------------
u1 | t1 | table | u1 | append only
(1 row)

db1=> select * from t1;
ERROR: permission denied for relation t1
db1=>

# 可以看到,u3可以看到表u1.t1,但不能查询。

# 赋予select权限
[gpadmin@hdp3 ~]$ psql -d db1 -c "grant select on u1.t1 to u3;"
GRANT
# 用u3用户执行
[gpadmin@hdp3 ~]$ psql -d db1 -U u3 -h hdp3 -c "set search_path='u1';select *,current_schema(),current_schemas(true) from t1;"
Password for user u3:
a | current_schema | current_schemas
---+----------------+-----------------
1 | u1 | {pg_catalog,u1}
(1 row)

# u3现在可以查询u1.t1

# 用u3用户执行
[gpadmin@hdp3 ~]$ psql -d db1 -U u3 -h hdp3 -c "create table t2(a int);"
Password for user u3:
CREATE TABLE

# 删除模式
[gpadmin@hdp4 ~]$ psql -h hdp3 -d db1
psql (8.2.15)
Type "help" for help.

db1=# drop schema u1;
NOTICE: append only table u1.t1 depends on schema u1
ERROR: cannot drop schema u1 because other objects depend on it
HINT: Use DROP ... CASCADE to drop the dependent objects too.
db1=# drop schema u1 cascade;
NOTICE: drop cascades to append only table u1.t1
DROP SCHEMA
db1=# drop schema u2 cascade;
NOTICE: drop cascades to append only table u2.t1
DROP SCHEMA

说明:搜索路径参数search_path控制查询表时所属schema的搜索顺序。创建的表存放哪个schema跟search_path有关。系统默认将PUBLIC模式的usage、create权限授权给所有用户。usage权限的含义是,可以“看到”模式中的对象,但是没有对象上的任何权限。pg_catalog存放了各系统表、内置函数等等。它总是在搜索路径中,需要通过current_schemas看到。

四、创建和管理表

这里所说的表是HAWQ数据库内部存储的表。除了表行是分布在系统中不同的segment上,HAWQ中的表与关系数据库中的表类似。关于外部表,将在后面“外部数据”中讨论。

  1. 创建表

CREATE TABLE命令创建表并定义表结构,当创建一个表时,可以定义:
表列及其数据类型。
表或列包含的限定数据的约束。
表的分布策略,决定HAWQ如何在segment中划分数据。
表在磁盘上的存储方式。
大表分区策略,指定数据如何划分。
(1)选择列的数据类型
列的数据类型决定了列中可以包含何种类型的数据。选择数据类型时应遵循以下通用原则:
选择可以容纳数据的最小可能空间,并能最好约束数据的数据类型。例如,如果可以使用INT或SMALLINT表示数据,那么就不要使用BIGINT,因为这会浪费存储空间。
在HAWQ中,字符类型CHAR、VARCHAR和TEXT除了使用空间不同,它们在性能上并无差异。在大多数情况下,应该使用TEXT或VARCHAR而不是CHAR。
考虑数据扩展。数据会随着时间的推移而不断扩展。在已经装载大量数据后,从小类型变为大类型的操作代价是很昂贵的。因此,如果当前的数据值可以用SMALLINT,但是考虑到数据扩展性,那么出于长期需要,INT是可能更好的选择。
为表连接的列使用相同的数据类型。如果数据类型不同,为了正确比较数据值,数据库必须进行数据类型转换,这将增加不必要的系统消耗。
HAWQ内建的数据类型参见 http://hawq.incubator.apache.org/docs/userguide/2.1.0.0-incubating/reference/HAWQDataTypes.html。

(2)设置约束

可以定义约束限制表中的数据。HAWQ支持与PostgreSQL相同的约束,但是有一些限制,包括:
CHECK约束只能引用它定义所属的表。
外键约束允许,但不起作用。
分区表上的约束作用于整个表。不能在一个表的单独部分上定义约束。
Check约束
Check约束允许指定特定列中存储的数据值必须满足一个布尔表达式。例如,产品价格必须为正值:

db1=# create table products
( product_no integer,
name text,
price numeric check (price > 0) );
db1=# insert into products values (1,'a',10);
INSERT 0 1
db1=# insert into products values (1,'a',10.5);
INSERT 0 1
db1=# insert into products values (1,'a',10.5111);
INSERT 0 1
db1=# insert into products values (1,'a',-10.5111);
ERROR: One or more assertions failed (seg0 hdp3:40000 pid=731975)
DETAIL: Check constraint products_price_check for table products was violated
db1=# insert into products values (1,'a',0);
ERROR: One or more assertions failed (seg0 hdp3:40000 pid=731988)
DETAIL: Check constraint products_price_check for table products was violated
db1=# select * from products;
product_no | name | price
------------+------+---------
1 | a | 10
1 | a | 10.5
1 | a | 10.5111
(3 rows)

非空约束
非空约束指定一个列不能有空值。非空约束总是一个列约束。

db1=# create table products
( product_no integer not null,
name text not null,
price numeric );
db1=# insert into products values(1,'a',10.51);
INSERT 0 1
db1=# insert into products (price) values(10.51);
ERROR: null value in column "product_no" violates not-null constraint (CTranslatorUtils.cpp:2726)
db1=#

主键与外键

HAWQ不支持主键与外键约束。因为主键是用唯一索引实现,而HAWQ不支持索引,因此不支持主键。根据外键的定义,既然没有主键,也就谈不上外键了。

db1=# create table t2(a int);
CREATE TABLE
db1=# create table t3(a int primary key);
ERROR: Cannot support create index
  1. 删除表

DROP TABLE命令从数据库中删除表。DROP TABLE总是删除表上的约束。指定CASCADE将删除引用表的视图。

db1=# create table t1 (a int);
CREATE TABLE
db1=# insert into t1 values (1);
INSERT 0 1
db1=# create view v1 as select * from t1;
CREATE VIEW
db1=# select * from v1;
a
---
1
(1 row)

db1=# drop table t1;
NOTICE: rule _RETURN on view v1 depends on append only table t1
NOTICE: view v1 depends on rule _RETURN on view v1
ERROR: cannot drop append only table t1 because other objects depend on it
HINT: Use DROP ... CASCADE to drop the dependent objects too.
db1=# drop table t1 cascade;
NOTICE: drop cascades to rule _RETURN on view v1
NOTICE: drop cascades to view v1
DROP TABLE

如果要清空表中的数据,但保留表定义,使用​​TRUNCATE <tablename>​​。

  1. 查看表对应的HDFS文件

假设在数据库db1中建立了表public.t2,使用以下步骤查看t2所在的HDFS文件。
(1)确定HAWQ在HDFS上的根目录

db1=# select * from pg_filespace_entry;
fsefsoid | fsedbid | fselocation
----------+---------+----------------------------
16384 | 0 | hdfs://mycluster/hawq_data
(1 row)

可以看到,HAWQ在HDFS上的根目录/hawq_data。我的Hadoop集群配置了HA,所以文件位置字段中的值使用Nameservice ID(mycluster)代替了NameNode FQDN(Fully Qualified Domain Name)。

(2)检查HAWQ系统目录表中t1的相关信息

db1=# select d.dat2tablespace tablespace_id, d.oid database_id, c.relfilenode table_id
db1-# from pg_database d, pg_class c, pg_namespace n
db1-# where c.relnamespace = n.oid
db1-# and d.datname = current_database()
db1-# and n.nspname = 'public'
db1-# and c.relname = 't2';
tablespace_id | database_id | table_id
---------------+-------------+----------
16385 | 25270 | 156634
(1 row)

一个数据库中不同schema下的表可能重名,但对应的表ID不同,因此需要关联pg_namespace系统表。d.oid是一个系统的隐藏列,表示行的对象标识符(对象ID)。该列只有在创建表的时候使用了WITH OIDS ,或者是设置了default_with_oids配置参数时出现。用\d pg_database命令是看不到oid列的。系统表pg_class的relhasoids列是布尔类型,true表示对象具有OID。为了简化对表的管理,每个表中的数据都被保存在一个HDFS目录中。HAWQ数据库表在HDFS上的目录结构为“文件空间根目录/表空间ID/数据库ID/表对象(分区表对象)ID”,例如表public.t2所对用的HDFS目录为/hawq_data/16385/25270/156634,该目录下是实际存储表数据的HDFS文件。

(3)查看表对应的HDFS文件

[gpadmin@hdp3 ~]$ hdfs dfs -ls /hawq_data/16385/25270/156634
Found 1 items
-rw------- 3 gpadmin gpadmin 0 2017-03-30 11:05 /hawq_data/16385/25270/156634/1

五、创建和管理视图

视图使能够保存经常使用的或者复杂的查询,然后将它们看做表,在SELECT语句中访问它们。视图并不物化到磁盘,当访问视图时,查询作为一个子查询运行。HAWQ不支持WITH子句的内嵌视图和物化视图。

  1. 创建视图
db1=# create table t1 (a int);
CREATE TABLE
db1=# insert into t1 values (10);
INSERT 0 1
db1=# insert into t1 values (1);
INSERT 0 1
db1=# select * from t1;
a
----
10
1
(2 rows)

db1=# create view v1 as select * from t1 order by a;
CREATE VIEW
db1=# select * from v1;
a
----
1
10
(2 rows)

db1=# drop view v1;
DROP VIEW
db1=# create view v1 as select * from t1 order by a desc;
CREATE VIEW
db1=# select * from v1;
a
----
10
1
(2 rows)
db1=# select * from v1;
a
----
10
1
(2 rows)

db1=# select * from v1 order by a;
a
----
1
10
(2 rows)
  1. 查看视图定义
db1=# \d v1
View "public.v1"
Column | Type | Modifiers
--------+---------+-----------
a | integer |
View definition:
SELECT t1.a
FROM t1
ORDER BY t1.a DESC;
  1. 删除视图
db1=# drop view v1;

六、其它对象

HAWQ还支持自定义数据类型、自定义函数、序列等对象。如果用过Oracle,对这些对象一定不会陌生。自定义数据类型的例子:

gpadmin=# \c db1
You are now connected to database "db1" as user "gpadmin".
db1=# create type compfoo as (f1 int, f2 text);
CREATE TYPE
db1=# create table big_objs (
db1(# id integer,
db1(# obj compfoo
db1(# );
CREATE TABLE
db1=# insert into big_objs values (1,(1,'a'));
INSERT 0 1

更多自定义数据类型信息参见 http://hawq.incubator.apache.org/docs/userguide/2.1.0.0-incubating/reference/sql/CREATE-TYPE.html。

序列的例子:

db1=# create sequence myseq start 101;
CREATE SEQUENCE
db1=# select currval('myseq'), nextval('myseq');
ERROR: currval() not supported
db1=# select nextval('myseq');
nextval
---------
101
(1 row)

db1=# select nextval('myseq');
nextval
---------
102
(1 row)

更多序列信息参见 http://hawq.incubator.apache.org/docs/userguide/2.1.0.0-incubating/reference/sql/CREATE-SEQUENCE.html。