9.1第二十一章

数据库管理
----
每个运行PostgreSQL服务器的实例都管理着一个或多个数据库。因此，在组织SQL（"数据库对象"）对象的层次中，数据库位于最顶层。本章描述数据库的属性，以及如何创建，管理和删除它们。
==概述==
数据库是一些SQL对象（"数据库对象"）的集合；通常每个数据库对象（表，函数等等）属于并且只属于一个数据库。（不过有几个系统表，比如 pg_database，属于整个集群并且可以在集群之内的每个数据库里访问。）更准确地说，一个数据库是一个模式的集合，而模式包含表，函数等等。因此完整的层次是：服务器，数据库，模式，表（或者其他类型对象，比如函数）。
在与数据库服务器联接的时候，客户应该在它的联接请求里面带有它想与之联接的数据库名称。不允许在一次联接里面对多个数据库访问．不过，没有限制一个客户与同一个或者其他数据库可以建立的联接数量．数据库是物理上相互隔离的，对它们的访问控制是在联接层次进行控制的。如果一个PostgreSQL 服务器实例用于承载那些应该分隔并且相互之间并不知晓的用户和项目，那么我们建议把它们放在不同的数据库里。如果项目或者用户是相互关联的，并且可以相互使用对方的资源，那么应该把它们放在同一个数据库里，但可能是不同的模式里。模式只是一个纯粹的逻辑结构，谁能访问某个模式由权限系统控制。有关管理模式的更多信息在 Section 5.7 里。
数据库是使用 CREATE DATABASE 命令创建的（参阅 Section 21.2）， 用 DROP DATABASE 命令删除（参阅 Section 21.5）。要判断现有数据库的集合，检查系统表 pg_database，比如
SELECT datname FROM pg_database;
psql 程序的 \l 元命令和 -l 命令行选项也可以用来列出现存数据库。
注意: SQL 标准把数据库称作"目录（catalogs）"，不过这两个东西实际上没有什么区别。
==创建数据库==
为了创建数据库，必须先运行PostgreSQL服务器（参阅 Section 17.3）。
创建数据库可以用 SQL 命令 CREATE DATABASE：
CREATE DATABASE name;
这里的 name 遵循SQL标识符的一般规则。当前角色自动成为此新数据库的所有者。以后删除这个数据库也是这个用户的特权（同时还会删除其中的所有对象， 即使那些对象有不同的所有者也这样。）
创建数据库是一个受限制的操作。参阅 Section 20.2 获取如何赋以权限的信息。
因为你需要连接到数据库服务器才能执行命令CREATE DATABASE，那么问题是任意节点的第一个数据库是怎样创建的？第一个数据库总是由initdb命令在初始化数据存储区的时候创建的。（参阅Section 17.2。）这个数据库叫postgres。 因此要创建第一个"真正"的数据库的时候你可以与postgres联接。
第二个数据库，template1， 也是在数据库集群初始化时被创建的。每创建一个新的数据库时，实际上就是克隆了 template1 数据库。这就意味着你对 template1 做的任何修改都会传播到所有随后创建的数据库。正因如此，应该避免在 template1 数据库中创建任何对象，除非你想将它们传播到后面创建的所有数据库中。更多细节见Section 21.3。
方便起见，你还可以用一个可以在 shell 中执行的程序来创建新数据库，createdb。
createdb dbname
createdb 没变什么魔术，它和postgres连接并执行 CREATE DATABASE 命令。createdb 的手册页包含使用它的细节。需要注意的是不带任何参数调用 createdb 将以当前用户名为名称创建数据库。
注意: Chapter 19 包含有关如何限制某个用户可以连接的数据库的信息。
有时候你想为其他人创建一个数据库。并让那个人成为新数据库的所有者，这样他就可以自己配置和管理这个数据库。要实现这个目的，使用下列命令中的某一条：
CREATE DATABASE dbname OWNER rolename;
上面的是在 SQL 环境中执行的，或者
createdb -O rolename dbname
在shell中执行。
只有超级用户才可以为其他用户（并且该超级用户不能是该用户的成员）创建数据库。
==模板数据库==
CREATE DATABASE 实际上是通过拷贝一个现有的数据库进行工作的。缺省情况下，它拷贝名字叫 template1 的标准系统数据库。 所以该数据库是创建新数据库的"模板"。如果你给 template1 增加对象，这些对象将被拷贝到随后创建的用户数据库中。这样的行为允许节点对数据库中的标准套件进行修改。 比如，如果你把过程语言 PL/pgSQL 安装到 template1 里，那么你在创建用户数据库的时候它们就会自动可得，而不需要额外的动作。
还有第二个标准的系统数据库，叫 template0。 这个数据库包含和 template1 一开始时一样的数据内容，也就是说，只有当前版本的 PostgreSQL 标准对象。在数据库集群初始化之后，不应该对 template0 做任何修改。通过告诉 CREATE DATABASE 使用 template0 而不是 template1 进行拷贝，你可以创建一个"纯净"的用户数据库，它不会包含任何 template1 里节点所附加的东西。这一点在恢复 pg_dump 转储的时候是非常方便的：转储脚本应该在一个纯洁的数据库中恢复以确保我们创建了被转储出的数据库中的正确内容， 而不和可能已经存在在 template1 中的对象相冲突。
另一个不用 template1 而用 template0 去拷贝的原因是，拷贝 template0 时你可以指定新的编码和本地化设置，而拷贝 template1 时必须使用相同的设置。因为 template1 可能已经指定了编码和本地化设置，而 template0 却没有。
要通过拷贝 template0 的方式创建一个数据库，使用命令:
CREATE DATABASE dbname TEMPLATE template0;
这条命令是在 SQL 环境里的，另一个命令：
createdb -T template0 dbname
是从shell里执行的。
我们可以创建额外的模板数据库，而且实际上只要为 CREATE DATABASE 指定模版名字就可以拷贝集群中的任何一个数据库。不过，我们必需明白，这个功能并非一般性的"COPY DATABASE"工具。这里有个限制是源数据库在拷贝时不能连接任何会话。如果源数据库存在任何其他的连接，在使用 CREATE DATABASE 拷贝时就会失败，在拷贝进行中，试图连接到源数据库的操作都会被阻止。
在 pg_database 里针对每个数据库有两个有用的标志：字段 datistemplate 和 datallowconn。datistemplate 表示该数据库是否准备用做 CREATE DATABASE 的模板。如果设置了这个标志，那么该数据库可以由任何有 CREATEDB 权限的用户克隆；如果没有设置，那么只有超级用户和该数据库的所有者可以克隆它。如果 datallowconn 为假，那么将不允许与该数据库发生任何新的连接（不过现有的会话不会因为把该标志设置为假而被中断）。template0 数据库通常被标记为 datallowconn = false 以避免对它的修改。template0 和 template1应该总是标记为datistemplate = true。
注意：除了 template1 在CREATE DATABASE 时是缺省的源数据库外，template1 和 template0 没有任何其他特殊状态。例如，你可以删除template1并根据template0重新创建以消除任何不利的影响。当你不小心污染了template1时推荐这种做法。（删除template1前，必须先在pg_database中设置其datistemplate属性为false。）数据库集群在初始化时还会自动创建postgres数据库。其做为用户和应用程序缺省连接的数据库。它只是template1的一个简单拷贝，必要时也可以删除和重建。
==数据库配置==
回顾一下 Chapter 18，我们知道PostgreSQL 服务器提供了大量的运行时配置变量。你可以为许多这样的变量设置数据库相关的缺省值。
比如，如果由于某种原因，你想关闭某个数据库上的 GEQO 优化器，通常情况下你会在所有数据库中关闭它，或者是保证每个连接过来的客户端都很小心地设置了 SET geqo TO off。要令这个设置在特定数据库里成为缺省，你可以执行下面的命令：
ALTER DATABASE mydb SET geqo TO off;
这样就保存了设置（但不是立即生效）。在随后的连接中，看起来好像是在会话开始后马上调用了 SET geqo TO off;请注意用户仍然可以在该会话中更改这个设置；它只是缺省。要撤消某个设置，使用ALTER DATABASE dbname RESET varname.
==删除数据库==
删除数据库的命令是 DROP DATABASE：
DROP DATABASE name;
只有数据库的所有者，或者超级用户可以删除数据库。删除数据库会删除数据库中包括的所有对象。数据库的删除是不可恢复的。
你不能使用 DROP DATABASE 删除与你连接的数据库。不过，你可以联接到其他数据库去执行，包括template1数据库，template1也是你删除集群中最后一个用户数据库的唯一方法。
方便起见，有一个shell程序也可以删除数据库，dropdb：
dropdb dbname
(与createdb不同，它缺省不是删除与当前用户同名的数据库。)
==表空间==
PostgreSQL 里的表空间允许数据库管理员在文件系统里定义那些存储数据库对象的文件存放的位置。一旦创建了表空间，那么就可以在创建数据库对象的时候引用它。
通过使用表空间，管理员可以控制一个 PostgreSQL 安装的磁盘布局。这么做至少有两个好处。首先，如果初始化集群所在的分区或者卷用光了空间，而又不能扩展空间，那么可以在另一个分区上创建和使用表空间，直到系统可以重新配置。
第二，表空间允许管理员根据数据库对象的使用模式安排数据位置，从而优化性能。比如，一个很频繁使用的索引可以放在非常快的，并且非常可靠的磁盘上，比如一种非常贵的固态设备。而同时，一个存储归档的数据，很少使用的或者对性能要求不高的表可以存储在一个没那么昂贵，比较慢的磁盘系统上。
要定义一个表空间，使用命令 CREATE TABLESPACE，例如：
CREATE TABLESPACE fastspace LOCATION '/mnt/sda1/postgresql/data';
这个位置必须是一个现有的空目录，并且属于 PostgreSQL 系统用户。所有随后在该表空间创建的对象都将被存放在这个目录下的文件里。
注意: 通常在一个逻辑文件系统上建立多个表空间没有什么意义，因为你无法控制一个逻辑文件系统里的不同文件的位置。 不过，PostgreSQL 并不做这方面的任何强制，并且它实际上并不知道你的系统上的文件系统边界。它只是在你指定的目录里存储文件。
创建表空间必须用数据库超级用户身份进行，但是在那之后，你就可以允许普通数据库用户利用它了。你只需在表空间上给这些用户授予 CREATE 权限。
表、索引和整个数据库都可以放在特定的表空间里。想要这么做的话，在给定表空间上有 CREATE 权限的用户必须把表空间的名字以一个参数的形式传递给相关的命令。比如，下面的命令在表空间 space1 上创建一个表：
CREATE TABLE foo(i int) TABLESPACE space1;
另外，还可以设置缺省的表空间：
SET default_tablespace = space1;
CREATE TABLE foo(i int);
只要 default_tablespace 设置为某个非空字符串，那么它就为没有明确使用 TABLESPACE 子句的 CREATE TABLE 和 CREATE INDEX 命令提供一个隐含的 TABLESPACE 子句。
还有一个临时表空间 temp_tablespaces，它用来指定临时表、索引以及临时文件的存放位置，比如用于大数据排序。此时可以指定多个表空间，而不是一个，这样临时对象可以被装载到多个表空间里。但是每个临时对象只被创建在一个随机的表空间里。
与一个数据库相关联的表空间用于存储该数据库的系统表，而且，如果在创建表、索引和临时文件时没有指定TABLESPACE且没有设置default_tablespace以及temp_tablespace,那么也会缺省使用该空间。如果创建数据库时没有给它声明一个表空间，那么它使用与它拷贝的模版数据库相同的表空间。
数据库集群初始化时自动创建两个表空间。pg_global 表空间用于共享的系统表。pg_default 是 template1 和 template0 数据库的缺省表空间（因此，这个表空间也将是任何其它数据库的缺省表空间，除非在 CREATE DATABASE 的时候使用了明确的 TABLESPACE 子句）。
表空间一但被创建就可以被集群中任何数据库使用，前提时使用者需要拥有相应的权限。这也意味着只要有任意一个数据库还在使用，该表空间就不能被删除。
删除一个空的表空间，使用命令 DROP TABLESPACE：
要查询已存在的表空间，可以查询系统表pg_tablespace，例如
SELECT spcname FROM pg_tablespace;
还可以使用psql程序提供的 \db 元命令来列出已存在的表空间。
PostgreSQL 使用符号链接来简单表空间的实现。这也意味着表空间只可用在支持符号连接的系统上。
目录 $PGDATA/pg_tblspc 中存放了集群中的符号连接来指向每个非内置的表空间。尽管不推荐，可以手动重定义这些链接来调整表空间布局。两个警告：不在要服务器运行时做；服务器重启后记得更新新位置到pg_tablespace 表。（否则，pg_dump仍然会输出先前位置的表空间。）