目录
HAWQ 本质上是一个数据库系统,所以这里所说的对象指的是数据库对象。和其他关系数据库类似,HAWQ 中有数据库、表空间、表、视图、自定义数据类型、自定义函数、序列等对象,本篇将简述这些对象的创建与管理。对 HAWQ 中表的存储方式与分布策略等特性的选择,会对数据库性能产生极大的影响,同时这也是一个复杂的话题,将在下一篇单独讨论。
一、创建和管理数据库
HAWQ 中数据库的概念与 MySQL 类似,一个 HAWQ 实例中通常会建立多个数据库,这和 Oracle 中数据库的概念不同。在 Oracle 体系结构中,数据库是一个“最大”的概念,大多数情况下一个 Oracle 数据库对应一个实例,RAC 是一个数据库对应多个实例。尽管可以在一个 HAWQ 系统中创建很多数据库,但是客户端程序在某一时刻只能连接到一个数据库,这也决定了 HAWQ 不能执行跨库的查询。
1. 模板数据库
HAWQ 初始化后, 就有了 template0 和 template1 两个模板库,开始时这两个库的内容是一样的,并且 template0 库和 template1 都不可删除。两者最主要的区别是,缺省时可以连接 template1 并在其中创建对象,但不能连接te mplate0。
不能删除模板数据库:
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 currently accepting connections
2. 创建数据库
创建数据库的用户必须要适当的权限,比如超级用户,或者被设置了 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 中的所有数据库所共享的,而另外一些对象,如表,则只有它所在的数据库能感知它的存在。
3. 查看数据库列表
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
Previous connection kept
4. 修改数据库
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 statement yet
5. 删除数据库
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 db2
一个数据库有连接时是不允许删除的,必须先终止所有连接,在没有连接之后再删除数据库。
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 - gpadmin
(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
2. 创建表空间
创建完文件空间,使用 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 statement
3. 使用表空间存储数据库对象
拥有表空间上 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 里。特别要指出的是,所有非共享的系统表也都存放在这里。
4. 查看表空间和文件空间
每个 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, 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)
5. 删除表空间和文件空间
只有表空间的属主或超级用户可以删除表空间。直到表空间所有的数据库对象都被删除后,才能删除表空间。
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 权限。当创建了一个模式,需要给用户授予访问模式的权限。
2. 创建模式
使用 CREATE SCHEMA 命令创建一个新模式。为了在模式中创建和访问对象,完整的对象名称由模式名+对象名组成,对象名和模式名用点号分隔。可以创建一个属于其他人的模式,语法是:CREATE SCHEMA <schemaname> AUTHORIZATION <username>;
3. 模式查找路径
可以设置 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;,该命令将删除模式及该模式下的所有对象(表、数据、函数等等)。
5. 系统模式
使用 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 ~]$ hawq stop cluster -u -M fast
# 创建数据库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 内建的数据类型参见“Data Types | Apache HAWQ (Incubating) Docs”。
(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 statement yet
2. 删除表
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>。
3. 查看表对应的 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 系统目录表中表的相关信息
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)
2. 查看视图定义
db1=# \d v1
View "public.v1"
Column | Type | Modifiers
--------+---------+-----------
a | integer |
View definition:
SELECT t1.a
FROM t1
ORDER BY t1.a DESC;
3. 删除视图
db1=# drop view v1;
六、其他对象
HAWQ 还支持自定义数据类型、自定义函数、序列等对象。如果用过 Oracle,对这些对象一定不会陌生。
1. 自定义数据类型
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
更多自定义数据类型信息参见“CREATE TYPE | Apache HAWQ (Incubating) Docs”。
2. 序列
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)
更多序列信息参见“CREATE SEQUENCE | Apache HAWQ (Incubating) Docs”。
自定义函数将在后面“过程语言”一篇详细描述。