打印

PostgreSQL学习文档 8.0 四

18.6. 表空间
PostgreSQL 里的表空间允许数据库管理员在文件系统里定义那些代表数据库对象的文件存放的位置。 一旦创建了表空间,那么就可以在创建数据库对象的时候引用它。

通过使用表空间,管理员可以控制一个 PostgreSQL 安装的磁盘布局。 这么做至少有两个用处。首先,如果初始化集群所在的分区或者卷用光了空间,而又不能逻辑上扩展或者别的什么操作, 那么表空间可以在一个不同的分区上创建和使用,直到系统可以重新配置。

第二,表空间允许管理员根据数据库对象的使用模式安排数据位置,从而优化性能。 比如,一个很频繁使用的索引可以放在非常快的,并且非常可靠的磁盘上,比如一种非常贵的固态设备。 而同时,一个存储归档的数据,很少使用的,或者对性能要求不高的表可以存储在一个没那么昂贵,比较慢的磁盘系统上。

要定义一个表空间,使用 CREATE TABLESPACE 命令, 比如:

CREATE TABLESPACE fastspace LOCATION '/mnt/sda1/postgresql/data';
这个位置必须是一个现有的空目录,并且属于 PostgreSQL 系统用户。 所有随后在该表空间创建的对象都将被存放在这个目录下的文件里。

注意: 通常在一个逻辑文件系统上建立多个表空间没有什么意义,因为你无法控制一个逻辑文件系统里的不同文件的位置。 不过,PostgreSQL 并不做这方面的任何强制,并且它实际上并不知道你的系统上的文件系统边界。 它只是在你告诉它使用的目录里存储文件。

创建表空间本身必须用数据库超级用户身份进行,但是在那之后,你就可以允许普通数据库用户利用它了。 要做这件事情,在表空间上给这些用户授予 CREATE 权限。

表,索引和整个数据库都可以放在特定的表空间里。想要这么做的话, 在给定表空间上有 CREATE 权限的用户必须把表空间的名字以一个参数的形式传递给相关的命令。 比如,下面的命令在表空间 space1 上创建一个表:

CREATE TABLE foo(i int) TABLESPACE space1;

另外,还可以使用 default_tablespace 参数:

SET default_tablespace = space1;
CREATE TABLE foo(i int);
只要 default_tablespace 设置为不是空字串的任何其他东西, 那么它就为没有明确使用 TABLESPACE 子句的 CREATE TABLE 和 CREATE INDEX 命令提供一个隐含的 TABLESPACE 子句。

与一个数据库相关联的表空间用于存储该数据库的系统表,以及任何使用该数据库的服务器进程创建的临时文件。 另外,如果在创建数据库对象是没有给出 TABLESPACE 子句(不管是明确的还是通过 default_tablespace), 那么这是在该数据库里创建这些任何对象使用的缺省表空间。 如果创建数据库时没有给它声明一个表空间,那么它使用与它拷贝的模版数据库相同的表空间。

initdb 自动创建两个表空间。pg_global 表空间用于共享的系统表。 pg_default 是 template1 和 template0 数据库的缺省表空间 (因此,这个表空间也将是任何其它数据库的缺省表空间,除非在 CREATE DATABASE 的时候使用了明确的 TABLESPACE 子句。)

创建了表空间之后,它就可以用于任何数据库,只要请求的用户有足够权限。 这意味着除非我们把使用这个表空间的所有数据库里的所有对象抖删除掉,否则我们不能删除该表空间。

要删除一个空的表空间,使用 DROP TABLESPACE 命令。

要判断一套现有的表空间,检查系统表 pg_tablespace,比如

SELECT spcname FROM pg_tablespace;
psql 程序的 \db 元命令也可以用于列出现有表空间。

为了简化表空间的实现,PostgreSQL 使用了大量符号连接。 这就意味着表空间只能在支持符号连接的系统上使用。

目录 $PGDATA/pg_tblspc 包含指向集群里定义的每个非内置的表空间的符号连接。 尽管我们不建议,但是我们还是可能通过手工重定义这些连接来调整表空间的布局。 两个警告:在 postmaster 运行的时候不要这么干;并且,在你重启 postmaster 之后, 更新 pg_tablespace 表以显示新的位置。(如果你不这么做,pg_dump 将继续显示旧的表空间位置。)

TOP

Chapter 19. 用户认证
Table of Contents
19.1. pg_hba.conf 文件
19.2. 认证方法
19.2.1. 信任认证
19.2.2. 口令认证
19.2.3. Kerberos 认证
19.2.4. 基于 Ident 的认证
19.2.5. PAM 认证
19.3. 认证问题
当一个客户端应用与数据库服务器进行联接时,它声明它将以哪个 PostgreSQL 用户的名称进行联接, 就象我们登录一台 Unix 计算机一样。在 SQL 环境里, 活跃的数据库用户名决定数据库对象的各种访问权限 — 参阅Chapter 17获取更多信息。因此, 实际上我们要限制的是用户可以联接的数据库。

认证 是数据库服务器建立客户端的标识, 然后通过一些手段判断是否允许此客户端应用(或者运行这个客户端应用的用户)与它所要求的用户名进行联接的过程。

PostgreSQL 提供多种不同的客户端认证方式。认证某个特定客户端联接所使用的方法可以通过基于(客户端)的主机地址,数据库和用户的方式进行选择; 一些认证方法还允许你通过用户名进行限制。

PostgreSQL 用户名在逻辑上是和服务器运行的操作系统用户名相互独立的。 如果某个服务器的所有用户在那台服务器机器上也有帐号, 那么给数据库用户赋与操作系统用户名是有意义的。不过, 一个接收远程访问的服务器很有可能有许多没有本地操作系统帐号的用户, 因而在这种情况下数据库用户和操作系统用户名之间不必有任何联系。

19.1. pg_hba.conf 文件
客户端认证是由一个配置文件控制的,通常其文件名是 pg_hba.conf, 存放在数据库集群的数据目录里。 (HBA 的意思是 host-based authentication:基于主机的认证。) 在initdb初始化数据目录的时候,它会安装一个缺省的文件。 不过我们也可以把认证配置文件放在其它地方;参阅 hba_file 配置参数。

文件 pg_hba.conf 的常用格式是一套记录, 每行一条。空白行行被忽略,井号( # )开头的注释也被忽略。 一条记录是由若干用空格和/或 tab 分隔的字段组成。 如果字段用引号包围,那么它可以包含空白。记录不能跨行存在。

每条记录声明一种联接类型,一个客户端 IP 地址范围(如果和联接类型相关的话),一个数据库名,一个用户名字, 以及对匹配这些参数的联接使用的认证方法。 第一条匹配联接类型,客户端地址和联接企图请求的数据库名和用户名的记录将用于执行认证。 这个处理过程没有"跨越"或者"回头"的说法:如果选择了一条记录而且认证失败, 那么将不考虑后面的记录。如果没有匹配的记录,那么访问将被拒绝。

每条记录可以下面七种格式之一

local      database  user  authentication-method  [authentication-option]
host       database  user  CIDR-address  authentication-method  [authentication-option]
hostssl    database  user  CIDR-address  authentication-method  [authentication-option]
hostnossl  database  user  CIDR-address  authentication-method  [authentication-option]
host       database  user  IP-address  IP-mask  authentication-method  [authentication-option]
hostssl    database  user  IP-address  IP-mask  authentication-method  [authentication-option]
hostnossl  database  user  IP-address  IP-mask  authentication-method  [authentication-option]

各个字段的含义如下:



local
这条记录匹配通过 Unix 域套接字进行的联接企图。 没有这种类型的记录,就不允许 Unix 域套接字的联接。

host
这条记录匹配通过 TCP/IP 进行的联接尝试。 host 记录匹配 SSL 和非 SSL 的连接请求。

注意: 除非服务器带着合适的 listen_addresses 配置参数值启动,否则将不可能进行远程的 TCP/IP 连接, 因为缺省的行为是只监听本地自环地址 localhost 的连接。

hostssl
这条记录匹配使用 TCP/IP 的 SSL 联接企图。 但必须是使用 SSL 加密的联接。

要使用这个选项,制作服务器的时候必须打开 SSL 支持。而且在服务器启动的时候, 必须SSL选项通过配置选项ssl打开。 (参阅 Section 16.7 获取更多信息)。

hostnossl
这个记录与 hostssl 有着正相反的逻辑: 它只匹配那些在 TCP/IP 上不使用 SSL 的连接请求。

database
声明记录所匹配的数据库。值 all 表明该记录匹配所有数据库, 值 sameuser表示如果被请求的数据库和请求的用户同名,则匹配。 samegroup 表示请求的用户必须是一个与数据库同名的组中的成员。 在其他情况里,这就是一个特定的 PostgreSQL 的名字。 我们可以通过用逗号分隔的方法声明多个数据库。 一个包含数据库名的文件可以通过对该文件前缀 @ 来声明。

user
为这条记录声明所匹配的PostgreSQL用户。值 all 表明它匹配于所有用户。 否则,它就是特定 PostgreSQL 用户的名字。多个用户名可以通过用逗号分隔的方法声明。组名字可以通过用 + 做组名字前缀来声明。 一个包含用户名的文件可以通过在文件名前面前缀 @ 来声明。该文件必需和 pg_hba.conf 在同一个目录。

CIDR-address
声明这条记录匹配的客户端机器的 IP 地址范围。它包含一个标准的点分十进制的 IP 地址和一个 CIDR 掩码长度。 (IP 地址只能用数值声明,不能用域或者主机名。) 掩码长度表示客户端 IP 地址必须匹配的高位二进制位数。 在给出的 IP 地址里,这个长度的右边的二进制位必须为零。 在 IP 地址,/,和 CIDR 掩码长度之间不能有空白。

典型的 CIDR 地址是 172.20.143.89/32,这表明一个主机, 或者 172.20.143.0/24 表示一个网络。 要声明单个主机,给 IPv4 地址声明 CIDR 掩码 32,给 IPv6 地址声明 128。

以 IPv4 格式给出的 IP 地址会匹配那些拥有对应地址的 IPv6 连接,比如 127.0.0.1 将匹配 IPv6 地址 ::ffff:127.0.0.1。 一个以 IPv6 格式给出的记录将只匹配 IPv6 连接,即使对应的地址在 IPv4-in-IPv6 范围内。请注意如果系统的 C 库不支持 IPv6 地址,那么 IPv6 的格式将被拒绝。

这个域只适用于 host,hostssl 和 hostnossl 记录。

IP-address
IP-mask
这些方法可以用于作为 CIDR-address 表示法的替补。 它不是声明掩码的长度,而是在另外一个字段里声明实际的掩码。 比如,255.0.0.0 表示 IPv4 CIDR 掩码长度 8,而 255.255.255.255 表示 CIDR 掩码长度 32。 同样的匹配逻辑将用于一个点分的 IP-mask。

这些字段只适用于 host, hostssl, 和 hostnossl 记录。

authentication-method(认证方法)
声明通过这条记录联接的时候使用的认证方法。 可能的选择在下面简介,详细情况在 Section 19.2。



trust
无条件地允许联接。这个方法允许任何可以与PostgreSQL 数据库服务器联接的用户以他们期望的任意 PostgreSQL 数据库用户身份进行联接,而不需要口令。 参阅 Section 19.2.1 获取细节。

reject
联接无条件拒绝。常用于从一个组中"过滤"某些主机。

md5
要求客户端提供一个 MD5 加密的口令进行认证。 参阅 Section 19.2.2 获取细节。

crypt
要求客户端提供一个 crypt() 加密的口令用于认证。 7.2 以前的客户端只能支持 crypt。 对于 7.2 以及以后的客户端,我们建议使用 md5。 参阅 Section 19.2.2 获取细节。

password
要求客户端提供一个未加密的口令进行认证。 因为口令是以明文形式在网络上传递的, 所以我们不应该在不安全的网络上使用这个方式。 参阅 Section 19.2.2 获取细节。

krb4
用 Kerberos V4 认证用户。只有在进行 TCP/IP 联接的时候才能用。 参阅 Section 19.2.3 获取细节。 (译注:Kerberos,"克尔波洛斯",故希腊神话冥王哈得斯的多头看门狗。 Kerberos 是 MIT 开发出来的基与对称加密算法的认证协议和/或密钥交换方法。 其特点是需要两个不同用途的服务器,一个用于认证身份, 一个用于通道两端用户的密钥交换。同时 Kerberos 对网络时间同步要求比较高,以防止回放攻击,因此通常伴随 NTP 服务。)

krb5
用 Kerberos V5 认证用户。只有在进行 TCP/IP 联接的时候才能用。 参阅 Section 19.2.3 获取细节。 (译注:Kerberos V5 是上面 V4 的改良,主要是不再依赖 DES 算法, 同时增加了一些新特性。)

ident
获取客户的操作系统名(对于 TCP/IP 联接,用户的身份是通过与运行在客户端上的 ident 服务器联接进行判断的,对于本地联接,它是从操作系统获取的。) 然后检查一下,看看用户是否允许以要求的数据库用户进行联接, 方法是参照在 ident 关键字后面声明的映射。 参阅 Section 19.2.4 获取细节。

pam
使用操作系统提供的可插入的认证模块服务 (Pluggable Authentication Modules) (PAM)来认证。参阅 Section 19.2.5 获取细节。


authentication-option
这个可选的字段的含义取决与选择的认证方法。细节在下面。


用 @ 构造包含的文件是当作一列名字读取的, 这些名字可以用空白或者逗号分隔。注释用 # 引入, 就像在 pg_hba.conf 里那样,允许嵌套 @ 构造。 除非跟在 @ 后面的文件名是一个绝对路径,否则被当作与包含该文件的目录相对的路径。

因为认证时系统是为每个联接请求顺序检查 pg_hba.conf 里的记录的,所以这些记录的顺序是非常关键的。 通常,靠前的记录有比较严的联接匹配参数和比较弱的 认证方法,而靠后的记录有比较松的匹配参数和比较严的认证方法。 比如,我们一般都希望对本地 TCP/IP 联接使用 trust 认证, 而对远端的 TCP/IP 联接要求口令。在这种情况下我们将 trust 认证方法用于来自 127.0.0.1 的联接,这条记录将出现在允许更广泛的客户端 IP 地址的使用口令认证的记录前面。

在启动和主服务器进程( postmaster )收到SIGHUP 信号的时候, 系统都会重新装载 pg_hba.conf 文件。 如果你在活跃的系统上编辑了该文件,你就需要用 kill 向 postmaster 发一个 SIGHUP信号,好让它重新读取该文件。

在 Example 19-1 里是 pg_hba.conf 记录的一些例子。 阅读下文理解不同认证方法的细节。

Example 19-1. pg_hba.conf 记录的例子

# 允许在本机上的任何用户使用 Unix 域套接字(本地连接的缺省)
# 以任何身份联接任何数据库
#
#
# TYPE  DATABASE    USER        CIDR-ADDRESS           METHOD
local   all         all                                trust


# 和上面相同,但是使用的是自环的(loopback)TCP/IP 连接
#
# TYPE  DATABASE    USER        CIDR-ADDRESS           METHOD
host         all    all         127.0.0.1/32           trust

# 和上面一行相同,但是用的是独立的掩码字段
#
# TYPE  DATABASE    USER        IP-ADDRESS          METHOD
host    all         all         127.0.0.1     255.255.255.255     trust


# 允许 IP 地址为 192.168.93.x 的任何主机与数据库
# "template1" 相连,用与他们在自己的主机上相同 ident 的用户名标识他自己
# (通常是他的 Unix 用户名)
# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
host    template1   all         192.168.93.0/24       ident sameuser


# 允许来自主机 192.168.12.10 的用户与 "template1" 数据库联接,
# 只要该用户提供了在正确的口令。
# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
host    template1   all         192.168.12.10/32      md5


# 如果前面没有其它 "host" 行,那么下面两行将拒绝所有来自
# 192.168.54.1 的联接请求 (因为前面的记录先匹配),
# 但是允许来自互联网上其它任何地方的有效的 Kerberos 5 认证的联接
# 零掩码表示不考虑主机 IP 的任何位。因此它匹配任何主机:
# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
host    all         all         192.168.54.1/32       reject
host    all         all         0.0.0.0/0             krb5

# 允许来自 192.168.x.x 的任何用户与任意数据库联接,只要他们通过 ident 检查
# 但如果 ident 说该用户是 "bryanh" 而他要求以 PostgreSQL 用户 "guest1" 联接,
# 那么只有在 `pg_ident.conf' 里有 "omicron" 的映射,说 "bryanh" 允许以
#  "guest1" 进行联接时才真正可以进行联接。
#
# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
host    all         all         192.168.0.0/16        ident omicron

# 如果下面是用于本地联接的仅有的三行,那么它们将允许本地用户
# 只和它们自己的数据库联接(数据库名和用户名同名),
# 只有管理员和组"support"里的成员例外,他们可以联接到任何数据库。
# 文件 $PGDATA/admins 列出了那些允许与所有数据库联接的用户名。
# 在所有情况下都需要口令。
#
# TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
local   sameuser    all                               md5
local   all         @admins                           md5
local   all         +support                          md5

# 上面最后两行可以合起来写成一行
local   all         @admins,+support                  md5

# 数据库字段也可以使用列表和文件名,但组不行:
local   db1,db2,@demodbs  all                         md5

TOP

19.2. 认证方法
下面的小节更详细地描述认证方法。

19.2.1. 信任认证
如果声明了 trust (信任)认证模式, PostgreSQL 就假设任何可以联接到服务器的人都可以以任何他声明的数据库用户(包括数据库超级用户)连接。 当然,在 database 和 user 字段里面的限制仍然适用。 这个方法应该用于那些在联接到服务器已经有足够操作系统层次保护的环境里。

trust 认证对于单用户工作站的本地联接是非常合适和方便的。 通常它本身并不适用于多用户环境的机器。 不过,即使在多用户的机器上,你也可以使用 trust, 只要你利用文件系统权限限制了对服务器的 Unix 域套接字文件的访问。 要做这些限制,你可以设置 参数 unix_socket_permissions (以及可能还有 unix_socket_group),就象 Section 16.4.2 里描述的那样。 或者你可以设置 unix_socket_directory,把Unix 域套接字文件放在一个经过恰当限制的目录里。

设置文件系统权限只能帮助 Unix 套接字联接。它不会限制本地 TCP/IP 联接; 因此,如果你想利用文件系统权限来控制本地安全,那么删除 pg_hba.conf 里的 host ... 127.0.0.1 ... 行,或者把它改为一个非 trust 的认证方法。

trust 认证模式只适合 TCP/IP 连接,只有在你信任那些在 pg_hba.conf 里声明为 trust,允许联接到服务器的行上的所有机器上面的所有用户的时候才是合适的。 很少有理由使用 trust 作为任何除来自localhost(127.0.0.1)以外的 TCP/IP 联接的认证方式。

19.2.2. 口令认证
以口令为基础的认证方法包括 md5,crypt, 和 password。这些方法操作上非常类似,只不过口令通过联接传送的方法不同。 但是只有 md5 支持加密的口令存储在 pg_shadow 里; 其它两种方法要求在该表中存储未加密的口令。

如果你担心口令被窃听("sniffing", 那么 md5 比较合适,如果你必需支持 7.2 以前的老的客户端, 那么可以选 crypt。如果我们在开放的互联网上使用, 应该尽可能避免使用 password(除非你在联接上使用了 SSL, SSH,或者其他通讯安全的联接封装。)

PostgreSQL 数据库口令与任何操作系统用户口令无关。 各个数据库用户的口令是存储在pg_shadow系统表里面的。 口令可以用 SQL 语言命令 CREATE USER 和 ALTER USER 等管理,也就是说, CREATE USER foo WITH PASSWORD 'secret';。缺省时,如果没有明确地设置口令,存储的口令是空并且该用户的口令认证总会失败。

要限制允许访问某数据库的用户集, 可以在 pg_hba.conf 中的 user 字段列出这些用户,就像前面的小节解释的那样。

19.2.3. Kerberos 认证
Kerberos 是一种适用于在公共网络上进行分布计算的工业标准的安全认证系统。 对 Kerberos 系统的叙述远远的超出了本文档的范围; 总的说来它是相当复杂(同样也相当强大)的系统。 Kerberos FAQ 或 MIT 雅典娜计划 是个开始探索 的好地方。现存在好几种Kerberos发布的源代码。

要使用 Kerberos,对它的支持必须在制作的时候打开。 参阅 Chapter 14 获取更多细节。 Kerberos 4 和 5 都被支持, 不过我们在一次制作中只能支持一个版本。

PostgreSQL 运行时象一个普通的 Kerberos 服务。 服务主的名字是 servicename/hostname@realm, 这里的 servicename 是 postgres (除非在配置时用 ./configure --with-krb-srvnam=whatever 选择了一个不同的 hostname)。 hostname 是服务器及其全称的主机名字。 服务器的域是服务器机器的优先域。

客户主自给必须用它们自己的 PostgreSQL 用户名作为第一个部件,比如 pgusername/otherstuff@realm。 目前 PostgreSQL 没有检查客户的域;因此如果你打开了跨域的认证,那么在任意域里任何可以和你通讯的主都会被接受。

确认你的服务器的密钥文件是可以被 PostgreSQL 服务器帐户读取(最好就是只读的)(又见 Section 16.1)。密钥文件( keytab)的位置是用配置参数 krb_server_keyfile 声明的。 (又见 Section 16.4。)缺省时在 Kerberos 4 里是 /etc/srvtab, Kerberos 5里是 /usr/local/pgsql/etc/krb5.keytab (或者任何在制作的时候声明为 sysconfdir 的目录。)

要生成密钥文件(keytab),可以用下面例子(对版本5)

kadmin% ank -randkey postgres/server.my.domain.org
kadmin% ktadd -k krb5.keytab postgres/server.my.domain.org
阅读 Kerberos 的文档获取详细信息。

在和数据库联接的时候,请确保自己对每个主都拥有一张匹配所请求的数据库用户名的门票。 例子:对于数据库用户 fred,主 fred@EXAMPLE.COM 和 fred/users.example.com@EXAMPLE.COM 都可以用于与数据库服务器认证。

如果你在你的Apache web 服务器上使用了mod_auth_krb和 mod_perl, 你可以用一个mod_perl脚本进行 AuthType KerberosV5SaveCredentials。 这样就有了一个通过 web 的安全数据库访问,不需要额外的口令。

19.2.4. 基于 Ident 的认证
身份(ident)认证方法的运做模式是使用一个映射文件列出许可的用户和对应的用户的配对, 然后通过获取客户端的操作系统用户名以及判断许可的数据库用户名的方法来认证。 判断客户端的用户名是非常关键的安全点,根据连接类型的不同,它的实现方法也略有不同。

19.2.4.1. 透过 TCP/IP 的身份认证
"Identification Protocol(标识协议)"在 RFC 1413 里面描述。实际上每个类Unix的操作系统都带着一个缺省时侦听113端口的身份服务器。 身份服务器的基本功能是回答类似这样的问题: "是什么用户从你的端口X初始化出来联接到我的端口Y上来了?"。 因为在建立起物理联接后,PostgreSQL 既知道 X 也知道 Y, 因此它可以询问运行尝试联接的客户端的主机,并且理论上可以用这个方法判断发起联接的操作系统用户。

这样做的缺点是它取决于客户端的完整性:如果客户端不可信或者被攻击者攻破, 而且它们可以在113端口上运行任何程序并且返回他们选择的任何用户的话,就无法认证了。 因此这个认证方法只适用于封闭的网络, 这样的网络里的每台客户机都处于严密的控制下并且数据库和操作系统管理员可以比较方便地联系上。 换句话说,你必须信任运行身份(ident)服务的机器。下面是警告:   身份标识协议并不适用于认证或者访问控制协议。
   
--RFC 1413   


19.2.4.2. 透过本地套接字的身份认证
在支持用于 Unix 域套接字的SO_PEERCRED请求的系统上, (目前是 Linux, FreeBSD, NetBSD, OpenBSD和 BSD/OS), 身份认证也可以用于局部联接。这个时候,使用身份认证不会增加安全风险; 实际上这也是在这种系统上使用本地联接时的优选方法。

在没有 SO_PEERCRED 请求的系统上,身份认证只能通过TCP/IP连接获取。 如果需要绕开这个限制,我们可以声明 localhost 地址 127.0.0.1,然后让连接指向这个地址。 这个方法适用于你相信本机身份认证服务器的场合。

19.2.4.3. Ident 映射
当使用以身份为基础的认证时,在判断了初始化联接的操作系统用户的名字后, PostgreSQL 判断他是否可以以他所请求的数据库用户的身份联接。 这个判断是由跟在pg_hba.conf 文件里的 ident 关键字后面的身份映射控制的。 有一个预定义的身份映射是sameuser,表示任何操作系统用户都可以以同名数据库用户进行联接 (如果后者存在的话)。其他映射必须手工创建。

非 sameuser 的身份映射定义在身份映射文件里, 其名字缺省是 pg_ident.conf, 并且缺省存放在集群的数据目录里。 (不过,我们可以把映射文件放在其它地方,参阅 ident_file 配置参数。) 身份映射文件包含下面通用的格式:

map-name ident-username database-username
注释和空白和 pg_hba.conf 文件里的一样处理。map-name 是将用于在pg_hba.conf里引用这个映射的任意名称。 另外两个域声明某个操作系统用户被允许以哪个数据库用户的身份进行联接。 同一个map-name 可以重复用于在一个映射里声明更多的用户映射。 对一个操作系统用户可以映射为多少个数据库用户没有限制,反之亦然。

在系统启动和主服务器( postmaster )收到一个 SIGHUP 信号的时候会读取 pg_ident.conf 文件。如果你在一台活跃的系统上编辑该文件, 那么你需要给 postmaster 发信号(用 pg_ctl reload 或者 kill -HUP 令其重新读取该文件。)

Example 19-2里是一个可以和在 Example 19-1 里面演示的pg_hba.conf文件配合使用的 pg_ident.conf 文件。 在这个例子的设置里,任何登录到 192.168 网络里的机器的用户,如果用户名不是 bryanh,ann,或 robert就不能获准访问。 Unix 用户robert只有在试图以PostgreSQL用户 bob身份联接时才允许访问,而不能是 robert 或其他什么身份。 ann 将只允许以ann的身份联接。 用户bryanh允许以他自己的 bryanh 身份或者做为 guest1 进行联接。

Example 19-2. 一个 pg_ident.conf 文件例子

# MAPNAME     IDENT-USERNAME    PG-USERNAME

omicron       bryanh            bryanh
omicron       ann               ann
# bob 在这台机器上的用户名是 robert
omicron       robert            bob
# bryanh 也可以以 guest1 身份连接
omicron       bryanh            guest1
19.2.5. PAM 认证
这个认证方法操作起来类似 password, 只不过它使用 PAM(Pluggable Authentication Modules)作为认证机制。 缺省的 PAM 服务名是 postgresql。 你可以在文件 pg_hba.conf 的 pam 关键字后面提供自己的可选服务名。 有关 PAM 的更多信息,请阅读 Linux-PAM页面和 Solaris PAM 页面。

TOP

19.3. 认证问题
真正的认证失败以及相关的问题通常由类似下面的错误信息表白自身。


FATAL:  no pg_hba.conf entry for host "123.123.123.123", user "andym", database "testdb"
这条信息出现的最大可能是你已经联接了服务器,但她不愿意和你说话。 就象信息自己表示的那样,服务器拒绝了联接请求,因为她没有在她的 pg_hba.conf 配置文件里找到认证记录。


Password authentication failed for user 'andym'
这样的信息表示你联接了服务器,并且她也愿意和你交谈, 但是你必须通过pg_hba.conf 文件里声明的认证方法。 检查你提交的口令,或者如果错误信息提到这些 Kerberos 或 IDENT 认证类型时检查你的这些软件。


FATAL 1:  user "andym" does not exist
这是表示此用户不存在的另一方法。


FATAL 1:  Database "testdb" does not exist
你试图联接的数据库不存在。请注意如果你没有声明数据库名,缺省是数据库用户名,这可能正确也可能不正确。

请注意服务器日志可能包含比报告给客户端的更多的有关认证失败的信息。如果你被失败的原因搞糊涂了,那么请检查日志。

TOP

20.1. 区域支持
区域支持指的是应用中考虑字母,排序,数字格式化等与文化相关的问题。 PostgreSQL 使用服务器操作系统提供的标准 ISO C 和POSIX的区域机制。 更多的信息请参考你的系统的文档。

20.1.1. 概述
区域支持是在使用 initdb 创建一个数据库集群的时候自动初始化的。 缺省时,initdb 将会按照它的执行环境的区域设置初始化数据库集群; 因此如果你的系统已经设置为你的数据库集群想要的区域, 那么你就没有什么可干的。如果你想使用其它的区域(或者你还不知道你的系统设置的区域是什么), 那么你可以用 --locale告诉 initdb 你需要的区域究竟是哪个。 比如:

initdb --locale=sv_SE

这个例子就把区域设置为瑞典(sv),用瑞典语说话(SE)。 其他的可能性是 en_US(美国英语)和 fr_CA (加拿****语)。 如果有多于一种字符集可以用于区域,那么声明看起来象下面这样: cs_CZ.ISO8859-2。你的系统里有哪些可用的区域设置,它们的名字是什么, 这些信息都取决于你的操作系统提供商提供了什么以及你安装了什么东西。 (在大多数系统上,命令 locale -a 将提供所有可用的区域的一个列表。)

有时候,把几种区域规则混合起来也很有用,比如,使用英语字符规则而用西班牙语信息。 为了支持这些,我们有一套区域子范畴用于控制区域规则的某一方面:


LC_COLLATE 字符串排序顺序
LC_CTYPE 字符分类(什么是字母?它是这个字母的等效大写?)
LC_MESSAGES 信息的语言
LC_MONETARY 货币金额的格式
LC_NUMERIC 数字的格式
LC_TIME 日期和时间的格式


这些范畴名转换成 initdb 选项的名字以覆盖某个特定范畴的区域选择。 比如,要把区域设置为加拿****语,但使用 U.S. 规则进行货币格式化,可以使用 initdb --locale=fr_CA --lc-monetary=en_US。

如果你想要你的系统表现得象没有区域支持,那么使用特殊的区域 C 或 POSIX。

一些区域范畴的性质是它们的值必需在数据库集群的生命期内固定。 也就是说,一旦运行了 initdb,你就再也不能更改它们了。 LC_COLLATE 和 LC_CTYPE 就是这样的范畴。它们影响索引的排序顺序,因此它们必需保持固定, 否则在文本字段上的索引将会崩溃。PostgreSQL 通过记录 initdb 看到的 LC_COLLATE 和 LC_CTYPE 来强迫这一点。 服务器在启动的时候自动使用这两个数值。

其它区域范畴可以在服务器启动的时候根据需要设置运行时配置变量来改变(参阅 Section 16.4.8.2 获取细节)。 initdb 选择的缺省值实际上只是做为服务器运行缺省写入配置文件 postgresql.conf。 如果你在 postgresql.conf 里面删除了相应的赋值,那么服务器将会继承来自运行环境的设置。

请注意服务器的区域行为是由它看到的环境变量决定的,而不是由任何客户端的环境变量影响的。 因此,我们要在启动服务器之前认真地设置好这些变量。 这样带来的一种情况是如果客户端和服务器设置成不同的区域, 那么消息可能以不同的语言呈现,实际情况取决于它们的源是什么。

注意: 在我们谈到从执行环境继承区域的时候,我们的意思是在大多数操作系统上的下列动作: 对于一个给定的区域范畴,比如字符集,按照下面的顺序评估这些环境变量, 直到找到一个设置了的:LC_ALL, LC_COLLATE(变量对应相应的范畴), LANG。如果这些环境变量一个都没有设置,那么区域缺省为 C。

一些信息区域化库也使用环境变量 LANGUAGE, 它覆盖所有其它用于设置语言信息的区域设置。如果有问题, 请参考你的操作系统的文档,特别是 gettext 的文档获取更多信息。

要打开信息翻译成用户选择的语言,制作时必需打开 NLS 选项。这个选项独立于其它区域支持。

20.1.2. 行为
区域支持特别影响下面的特性:



使用 ORDER BY 和区域的查询里的排序。

The ability to use indexes with LIKE clauses 使用 LIKE 子句的索引的能力

函数里的 to_char家族

模式匹配中的 LIKE 和 ~ 操作符


PostgreSQL 里使用非 C 或 POSIX 区域的缺点是速度。 它降低了字符处理的速度和阻止了在 LIKE 类查询里面普通索引的使用。 因此,只有在你实际上需要的时候才使用它。

20.1.3. 问题
如果经过上面解释后区域支持仍然不能运转,那你就要检查一下看看你的操作系统的区域支持是否正确配置。 要检查某个区域是否安装并且正常运转,你可以使用象命令 locale -a。 (如果你的系统提供了该命令)。

请检查核实 PostgreSQL 确实使用了你认为它该用的区域设置。 LC_COLLATE 和 LC_CTYPE 设置都是在 initdb 的时候决定的,如果不重复 initdb 是不可能改变的。其它的区域设置包括 LC_MESSAGES 和 LC_MONETARY 都是由服务器启动的环境决定的, 但是可以在运行时修改。你可以用 SHOW 命令检查数据库活跃的区域设置。

目录 src/test/locale 包含 PostgreSQL 的区域支持的测试套件。

那些通过分析错误信息处理服务器端错误的客户端应用很明显会有问题,因为服务器来的信息可能会是以不同语言表示的。 我们建议这类应用的开发人员改用错误代码机制。

维护信息翻译表需要许多志愿者的坚持不懈的努力, 他们就是希望 PostgreSQL 以它们的语言说话的人。 如果你的语言的信息目前还不可用或者没有完全翻译完成, 那么我们很欢迎你的协助。如果你想帮忙,那么请参考 Chapter 44 或者向开发者邮递列表发邮件。

TOP

20.2. 字符集支持
PostgreSQL 里面的字符集支持你能够以各种字符集存储文本, 包括单字节的字符集,比如 ISO 8859 系列和 EUC (扩展 Unix 编码 Extended Unix Code),Unicode 和 Mule 国际编码。所有字符集都可以在服务器上透明地使用。 (如果你使用了来自其它数据源地扩展函数,那么它取决于他们是否正确地书写了代码。) 缺省的字符集是在使用 initdb 初始化你的 PostgreSQL 数据库集群的时候选择的。 在你的使用 createdb 或者 SQL 命令 CREATE DATABASE 的时候是可以覆盖这个缺省的。因此,你可以有多个数据库,每个都有不同的字符集。

20.2.1. 支持字符集编码
Table 20-1 显示了可以用在服务器的字符集。

Table 20-1. 服务器字符集

名字 描述
SQL_ASCII ASCII
EUC_JP 日文 EUC
EUC_CN 中文 EUC
EUC_KR 韩文 EUC
JOHAB 韩文 EUC (Hangle base)
EUC_TW 台湾 EUC
UNICODE Unicode(UTF-8)
MULE_INTERNAL Mule 内部编码
LATIN1 ISO 8859-1/ECMA 94 (Latin Alphabet No.1)
LATIN2 ISO 8859-2/ECMA 94 (Latin Alphabet No.2)
LATIN3 ISO 8859-3/ECMA 94 (Latin Alphabet No.3)
LATIN4 ISO 8859-4/ECMA 94 (Latin Alphabet No.4)
LATIN5 ISO 8859-9/ECMA 128 (Latin Alphabet No.5)
LATIN6 ISO 8859-10/ECMA 144 (Latin Alphabet No.6)
LATIN7 ISO 8859-13 (Latin Alphabet no.7)
LATIN8 ISO 8859-14 (Latin Alphabet no.8)
LATIN9 ISO 8859-15 (Latin Alphabet no.9)
LATIN10 ISO 8859-16/ASRO SR 14111 (Latin Alphabet no.10)
ISO_8859_5 ISO 8859-5/ECMA 113 (Latin/Cyrillic)
ISO_8859_6 ISO 8859-6/ECMA 114 (Latin/Arabic)
ISO_8859_7 ISO 8859-7/ECMA 118 (Latin/Greek)
ISO_8859_8 ISO 8859-8/ECMA 121 (Latin/Hebrew)
KOI8 KOI8-R(U)
ALT Windows CP866
WIN874 Windows CP874 (Thai)
WIN1250 Windows CP1250
WIN Windows CP1251
WIN1256 Windows CP1256 (Arabic)
TCVN TCVN-5712/Windows CP1258 (Vietnamese)

Important: 在 PostgreSQL7.2 之前, LATIN5错误地表示 ISO 8859-5 的意思。 从 7.2 开始 LATIN5表示 ISO8859-9。 如果你有一个在 7.1 或者之前创建的使用了 LATIN5的数据库, 而且你希望移植到 7.2(或者以后的版本), 那么你必须非常仔细地注意这个变化。

并非所有API支持上面列出的编码。比如, PostgreSQL JDBC 驱动就不支持MULE_INTERNAL, LATIN6,LATIN8 和 LATIN10。

20.2.2. 设置字符集
initdb 为一个 PostgreSQL 集群定义缺省的字符集,比如:

initdb -E EUC_JP
把缺省字符集设置为 EUC_JP (用于日文的扩展的 Unix 编码)。 如果你喜欢用长选项声明的话,你可以用 --encoding 代替 -E。 如果没有给出-E或者--encoding选项, 则使用SQL_ASCII。

你可以创建一个有着不同编码的数据库:

createdb -E EUC_KR korean
将创建一个使用EUC_KR字符集的名字叫 korean 的数据库。 另外一种实现方法是使用 SQL 命令:

CREATE DATABASE korean WITH ENCODING 'EUC_KR';
数据库的编码是用系统表 pg_database 里的一个 编码字段代表的。 你可以用psql的-l选项或 \l命令列出这些编码。

$ psql -l
            List of databases
   Database    |  Owner  |   Encoding
---------------+---------+---------------
euc_cn        | t-ishii | EUC_CN
euc_jp        | t-ishii | EUC_JP
euc_kr        | t-ishii | EUC_KR
euc_tw        | t-ishii | EUC_TW
mule_internal | t-ishii | MULE_INTERNAL
regression    | t-ishii | SQL_ASCII
template1     | t-ishii | EUC_JP
test          | t-ishii | EUC_JP
unicode       | t-ishii | UNICODE
(9 rows)

Important: 虽然你可以给一个数据库声明你需要的任何编码,但选择一个与你选择的区域不一致的编码还是不妥的做法。 LC_COLLATE 和 LC_CTYPE 设置暗示一个特定的编码, 与区域相关的操作(比如排序)在不兼容的编码里很有可能产生错误的解析。

因为这些区域设置都是由 initdb 冻结的, 所以在不同的数据库里使用不同的编码更多是理论而不是现实。 这些机制很有可能在将来版本的 PostgreSQL 得到改进。

一个安全使用多种编码的方法是在 initdb 的时候把区域设置为 C 或者 POSIX,这样旧关闭了任何实际的区域敏感性。

20.2.3. 服务器和客户端之间的自动字符集转换
PostgreSQL 支持一些编码在服务器和前端之间的自动编码转换。 转换信息在系统表 pg_conversion 中存储。 你可以使用 SQL 命令 CREATE CONVERSION 创建一个新的转换。 PostgreSQL带着一些预定义的转换。它们在 Table 20-2 中列出。

Table 20-2. 客户/服务器字符集转换

服务器字符集 可用客户端字符集
SQL_ASCII SQL_ASCII, UNICODE, MULE_INTERNAL  
EUC_JP EUC_JP, SJIS, UNICODE, MULE_INTERNAL  
EUC_CN EUC_CN, UNICODE, MULE_INTERNAL  
EUC_KR EUC_KR, UNICODE, MULE_INTERNAL  
JOHAB JOHAB, UNICODE  
EUC_TW EUC_TW, BIG5, UNICODE, MULE_INTERNAL  
LATIN1 LATIN1, UNICODE, MULE_INTERNAL  
LATIN2 LATIN2, WIN1250, UNICODE, MULE_INTERNAL  
LATIN3 LATIN3, UNICODE, MULE_INTERNAL  
LATIN4 LATIN4, UNICODE, MULE_INTERNAL  
LATIN5 LATIN5, UNICODE  
LATIN6 LATIN6, UNICODE, MULE_INTERNAL  
LATIN7 LATIN7, UNICODE, MULE_INTERNAL  
LATIN8 LATIN8, UNICODE, MULE_INTERNAL  
LATIN9 LATIN9, UNICODE, MULE_INTERNAL  
LATIN10 LATIN10, UNICODE, MULE_INTERNAL  
ISO_8859_5 ISO_8859_5, UNICODE, MULE_INTERNAL, WIN, ALT, KOI8  
ISO_8859_6 ISO_8859_6, UNICODE  
ISO_8859_7 ISO_8859_7, UNICODE  
ISO_8859_8 ISO_8859_8, UNICODE  
UNICODE EUC_JP, SJIS, EUC_KR, UHC, JOHAB, EUC_CN, GBK, EUC_TW, BIG5, LATIN1 到 LATIN10, ISO_8859_5, ISO_8859_6, ISO_8859_7, ISO_8859_8, WIN, ALT, KOI8, WIN1256, TCVN, WIN874, GB18030, WIN1250  
MULE_INTERNAL EUC_JP, SJIS, EUC_KR, EUC_CN, EUC_TW, BIG5, LATIN1 到 LATIN5, WIN, ALT, WIN1250 BIG5, ISO_8859_5, KOI8
KOI8 ISO_8859_5, WIN, ALT, KOI8, UNICODE, MULE_INTERNAL  
ALT ISO_8859_5, WIN, ALT, KOI8, UNICODE, MULE_INTERNAL  
WIN ISO_8859_5, WIN, ALT, KOI8, UNICODE, MULE_INTERNAL  
WIN874 WIN874, UNICODE  
WIN1250 LATIN2, WIN1250, UNICODE, MULE_INTERNAL  
WIN ISO_8859_5, WIN, ALT, KOI8, UNICODE, MULE_INTERNAL  
WIN1256 WIN1256, UNICODE  
TCVN TCVN, UNICODE  

要想打开自动字符集转换功能,你必须告诉 PostgreSQL 你想在客户端使用的字符集(编码)。你可以用好几种方法实现这个目的。



用 psql 里的 \encoding 命令。 \encoding 允许你动态修改客户端编码。 比如,把编码改变为 SJIS,键入:

\encoding SJIS

使用 libpq 函数。 \encoding 在做此用途的时候实际上是调用 PQsetClientEncoding()。

int PQsetClientEncoding(PGconn *conn, const char *encoding);
这里 conn 与后端的联接,而 encoding 是你想用的编码。如果编码设置成功它返回 0,否则返回 -1。本次联接的当前编码可以用下面函数显示:

int PQclientEncoding(const PGconn *conn);
请注意它只返回编码 ID,而不是象 EUC_JP 这样的编码符号字串。 要把编码 ID 转换为编码符号,你可以用:

char *pg_encoding_to_char(int encoding_id);

使用 SET client_encoding TO。 可以用 SQL 命令设置客户端编码:

SET CLIENT_ENCODING TO 'value';
你还可以把 SQL 语法里的 SET NAMES用于这个目的:

SET NAMES 'value';
查询当前客户端编码:

SHOW client_encoding;
返回缺省编码:

RESET client_encoding;

使用 PGCLIENTENCODING。 如果在客户端的环境里定义了 PGCLIENTENCODING 环境变量, 那么在与服务器进行联接时将自动选择客户端编码。 (这个编码随后可以用上面谈到的任何其它方法覆盖。)

使用client_encoding配置变量。 如果在 postgresql.conf 里设置了 client_encoding 变量, 那么在与服务器建立了联接之后,这个客户端编码将自动选定。(这个设置随后可以被上面提到 的其他方法覆盖。)


假如无法进行特定的字符转换 — 比如, 你选的服务器编码是EUC_JP, 客户端是LATIN1,那么有些日文字符不能转换成LATIN1。这时, 不能用LATIN1字符集表示的字母将被转换成圆括弧包围的十六进制,像,(826C) 这样。

20.2.4. 进一步阅读
下面是学习各种类型的编码系统的好地方。



ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/cjk.inf
详细地解释了第3.2节出现的EUC_JP, EUC_CN,EUC_KR,EUC_TW。

http://www.unicode.org/
Unicode 的家目录。

RFC 2044
定义了UTF-8。

TOP

Chapter 21. 日常数据库维护工作
Table of Contents
21.1. 日常清理
21.1.1. 恢复磁盘空间
21.1.2. 更新规划器统计
21.1.3. 避免事务 ID 重叠造成的问题
21.2. 经常重建索引
21.3. 日志文件维护
为了保持所安装的 PostgreSQL 服务器平稳运行, 我们必须做一些日常性的维护工作。我们在这里讨论的这些工作都是经常重复的事情, 可以很容易地使用标准的 Unix 工具,比如cron 脚本来实现。 不过,设置合适的脚本以及检查它们是否成功执行则是数据库管理员的责任,

一件很明显的维护工作就是经常性地创建数据的备份拷贝。 如果没有最近的备份,那么你就没有从灾难中恢复的机会(比如磁盘坏了,失火,误删了表等等)。 可以在PostgreSQL 里面使用的备份和恢复机制在 Chapter 22 里面有比较详细的讨论。

其它主要的维护范畴的工作包括周期性的 "vacuuming" (清理)数据库。 这个工作我们在 Section 21.1里讨论。

其它需要周期性注意的东西是日志文件的管理。 我们在 Section 21.3 里讨论了这个问题。

PostgreSQL 和其它数据库产品比较起来是低维护量的。 但是,适当在这些任务上放一些注意将更加能够确保我们的愉快工作和获取对这个系统富有成效的经验。

21.1. 日常清理
由于以下几个原因,必须周期性运行 PostgreSQL 的 VACUUM 命令∶



恢复那些由已更新的或已删除的行占据的磁盘空间.

更新 PostgreSQL 查询规划器使用的数据统计信息.

避免因为事务 ID 重叠造成的老旧数据的丢失.

对上面每个条件进行 VACUUM 操作的频率和范围因 不同的节点而不同.因此,数据库管理员必须理解这些问题并且 开发出合适的维护策略.本节的重点就放在解释这些高级别的问题; 至于命令语法的细节,请参阅 VACUUM 命令手册页.

从 PostgreSQL 7.2 开始, VACUUM 的标准形式可以和普通的数据库操作 (selects, inserts, updates, deletes, 但不包括表定义的修改)。 因此日常的清理也不再象以前的版本那样具有干扰性, 也不再那么特别要求安排在每天的低使用的时间里进行.

从 PostgreSQL 8.0 开始,有一些配置参数可以设置, 用来进一步减小后端清理的的性能影响。参阅 Section 16.4.3.4。

21.1.1. 恢复磁盘空间
在正常的 PostgreSQL 操作里, 对一行的UPDATE或DELETE并未立即删除旧版本的数据行。 这个方法对于获取多版本并行控制的好处是必要的(参阅 Chapter 12): 如果一个行的版本仍有可能被其它事务看到,那么你就不能删除它。 但到了最后,不会有任何事务对过期的或者已经删除的元组感兴趣。 而它占据的空间必须为那些新的元组使用而回收, 以避免对磁盘空间增长的无休止的需求。这件事是通过运行 VACUUM 实现的。

很明显,那些经常更新或者删除元组的表需要比那些较少更新的表清理的更频繁一些。 所以,设置一个周期性的 cron 任务 VACUUM 那些选定的表,而忽略那些已经知道变化比较少的表. 这个方法只是在你拥有大量更新频繁的表和大量很少更新的表的时候有意义 — 清理一个小表的额外开销根本不值得担心.

VACUUM 命令有两个变种。第一种形式,叫做"懒汉 vacuum"或者只是 VACUUM, 在表和索引中标记过期的数据为将来使用;它并不试图立即恢复这些过期数据使用的空间。 因此,表文件不会缩小,并且任何文件中没有使用的空间都不会返回给操作系统。 这个变种的 VACUUM 可以和通常的数据库操作并发执行。

第二种形式是 VACUUM FULL 命令。 这个形式使用一种更加激进的算法来恢复过期的的行版本占据的空间。 任何 VACUUM FULL 释放的空间都立即返回给操作系统。 但是,这个形式的 VACUUM 命令在进行 VACUUM FULL 一个表的时候在其上要求一个排他锁。 因此,经常使用 VACUUM FULL 会对并发数据库查询有着非常糟糕的影响。

标准形式的 VACUUM 最适合用于维护相当程度的磁盘用量的稳定状态。 如果你需要把磁盘空间归还给操作系统,那么你可以使用 VACUUM FULL — 不过如果释放的磁盘空间又会很快再次被分配又怎样? 如果维护更新频繁的表,那么中等频率的多次标准 VACUUM 运行方法比很低频率的 VACUUM FULL 更好。

对于大多数节点而言,我们推荐的习惯是在一天中的低使用的时段安排一次整个数据库的 VACUUM, 必要时外加对更新频繁的表的更经常的清理。 (有些环境下,对那些更新非常频繁的表可能会每几分钟就 VACUUM 一次。) 如果你的集群中有多个数据库,别忘记对每个库进行清理;vacuumdb 脚本可能会帮你的忙。

提示: contrib/pg_autovacuum 可以用于高频率的清理操作。

如果你知道自己刚删除掉一个表中大部分的行,那么我们建议使用VACUUM FULL, 这样该表的稳定态尺寸可以因为VACUUM FULL更富侵略性的方法而显著减小。 日常的磁盘空间清理,请使用 VACUUM,而不是 VACUUM FULL。

如果你有一个表,它的内容经常被完全删除,那么可以考虑用 TRUNCATE,而不是后面跟着 VACUUM 的 DELETE。 TRUNCATE 立即删除整个表的内容, 而不要求随后的 VACUUM 或者VACUUM FULL 来恢复现在没有用的磁盘空间。

21.1.2. 更新规划器统计
PostgreSQL 的查询规划器依赖一些有关表内容的统计信息用以为查询生成好的规划。 这些统计是通过ANALYZE 命令获得的,你可以直接调用这条命令, 也可以把它当做 VACUUM 里的一个可选步骤来调用。 拥有合理准确的统计是非常重要的,否则,选择了恶劣的规划很可能降低数据库的性能。

和为了回收空间做清理一样,经常更新统计信息也是对更新频繁的表更有用。 不过,即使是更新非常频繁的表,如果它的数据的统计分布并不经常改变,那么也不需要更新统计信息。 一条简单的拇指定律就是想想表中字段的最大很最小值改变的幅度。 比如,一个包含行更新时间的 timestamp 字段将是随着行的追加和更新稳定增长最大值的; 这样的字段可能需要比那些包含访问网站的 URL 的字段更频繁一些更新统计信息。 那些 URL 字段可能改变得一样频繁,但是其数值的统计分布的改变相对要缓慢得多。

我们可以在特定的表,甚至是表中特定的字段上运行 ANALYZE, 所以如果你的应用有需求的话,我们是可以对某些信息更新得比其它信息更频繁的。 不过,在实际中,这种做法的有用性是值得怀疑的。 从 PostgreSQL 7.2 开始, ANALYZE 是一项相当快的操作,即时在大表上也很快, 因为它使用了统计学上的随机采样的方法进行行采样, 而不是把每一行都读取进来。因此,每隔一段时间对整个数据库运行一便这条命令可能更简单。

提示: 尽管用 ANALYZE 按字段进行挖掘的方式可能不是很实用, 但你可能还是会发现值得按字段对 ANALYZE 收集的统计信息的详细级别进行调整。 那些经常在WHERE子句里使用的字段如果有非常不规则的数据分布, 那么就可能需要比其它字段更细致的数据图表.参阅 ALTER TABLE SET STATISTICS.

我们对大多数节点都建议在每天的低使用时段安排一次数据库范围的 ANALYZE: 这个任务可以有效地和每天的 VACUUM 组合在一起。 不过,这对那些表统计信息改变相对缓慢的节点可能会过于夸张, 而且少一些的 ANALYZE 也足够了。

21.1.3. 避免事务 ID 重叠造成的问题
PostgreSQL 的 MVCC 事务语意依赖于比较事务 ID(XID)的数值: 一条带有大于当前事务的 XID 的插入 XID 的行版本是"属于未来的", 并且不应为当前事务可见。但是因为事务 ID 的大小有限(在我们写这些的时候是 32 位),如果一次集群如果运行的时间很长(大于 4 千兆次事务), 那么它就要受到事务 ID 重叠的折磨∶XID 计数器回到零位, 然后突然间所有以前的事务就变成看上去是在将来的 --- 这意味着它们的输出将变得可见。 简而言之,可怕的数据丢失,(实际上数据仍然在那里,但是如果你无法获取数据,这么说也只是幸灾乐祸。)

在 PostgreSQL 7.2 之前, 防御 XID 重叠的唯一办法就是至少每4千兆事务就重新做一次initdb。 这种做法对高流量的节点而言当然不是令人满意的做法,所以我们设计了更好的方法。 新的方法允许某个服务器仍然保持运行状态,不需要 initdb 或者任何类型的重启。 代价就是下面这样的维护要求: 数据库中的每个表都必须在每十亿次事务中至少清理一次 .

从实际角度出发,这个要求不算一个很繁重的要求, 但是因为如果我们没能满足这个要求的后果是全部数据的丢失(而不仅仅是磁盘空间的浪费或者性能的下降), 我们制作了一些特殊的东西来帮助数据库管理员跟踪自上次VACUUM 以来的时间。本节剩余的部分给出这些细节。

XID 比较的新方法剥离出两个特殊的 XID,数字 1 和 2 (BootstrapXID 和 FrozenXID)。 这两个 XID 总是被认为表任何普通的 XID 旧。普通的 XID(那些大于 2 的)使用模-231运算进行比较。 这就意味着对于每个普通的 XID,总是有二十亿个 XID 是"更旧"以及二十亿个 XID"更新"; 表达这个意思的另外一个方法是普通的 XID 空间是没有终点的环。 因此,一旦一条元组带着特定的普通 XID 创建出来,那么该元组 将在以后的二十亿次事务中表现得是"在过去",而不管我们说的是哪个普通 XID。 如果该元组在超过二十亿次事务之后仍然存在, 那么它就会突然变成在将来的元组。为了避免数据丢失,老的元组必须在到达二十亿次事务的年龄之前的某个时候赋予 XID FrozenXID。 一旦它被赋予了这个特殊的 XID,那么它们在所有普通事务面前表现为 "在过去",而不管事务 ID 是否重叠, 因此这样的元组直到删除之前都会完好,不管要保存多长时间.这个 XID 的重新赋值是VACUUM 控制的.

VACUUM 的正常策略是给任何其普通 XID 有超过十亿次已过去事务行版本重新赋值为 FrozenXID。 这个策略保留了原来的插入 XID 直到该数值不再令人感兴趣为止。 (实际上,大多数行版本将可能在还没有"冻结"之前就完成生存和消亡了)。 在这个策略下,任何表在两次 VACUUM 运行之间的最大的安全间隔是十亿次事务: 如果你等的时间更长,那么最后就可能就会有一条不够老的行版本在重新赋值时变成比二十亿次事务更老, 并因此重叠到了未来 — 也就是说,你失去它了。(当然,它在另外二十亿次事务之后会重新出现,不过那样也无济于事。)

因为上面的原因,我们需要周期性地运行 VACUUM, 所以很难有哪个表会到十亿次事务还没有清理过。但是,为了帮助管理员确保满足了这个要求, VACUUM 在系统表pg_database 里存储了事务 ID 统计。 尤其是一个数据库的 pg_database 行中的 datfrozenxid 字段在任何数据库范围的 VACUUM 操作(也就是没有声明任何表的VACUUM)之后将会被更新。 这个字段里存储的数值是该 VACUUM 命令使用的冻结终止的 XID。 系统保证在该数据库中所有比这个终止 XID 老的普通 XID 都被 FrozenXID 代替。 检查这个信息的一个便利的方法是执行下面的查询

SELECT datname, age(datfrozenxid) FROM pg_database;
age 字段用于测量从终止 XID 到当前事务的 XID 的数目。

使用了这种标准的冻结策略,对一个刚清理过的数据库而言, age 字段将从十亿处开始。当age到达二十亿次的时候, 数据库必须再次清理以避免事务标识重叠造成的问题。 我们建议的策略是至少每半个十亿次(5亿次)事务 VACUUM 一次数据库, 这样就可以保证足够的安全边界范围.为了帮助满足这条规则, 如果有任何 pg_database 记录显示出超过15亿次事务的 age, 那么每次数据库范围的VACUUM 都会自动发出一条警告,比如:

play=# VACUUM;
WARNING:  Some databases have not been vacuumed in 1613770184 transactions.
        Better vacuum them within 533713463 transactions,
        or you may have a wraparound failure.
VACUUM

带着 FREEZE 选项的 VACUUM 使用了更大胆的冻结策略: 如果行版本已经老得被所有打开的事务看做是良好的, 那么就都冻结.特别是如果在一个空闲的数据库上运行 VACUUM FREEZE,那么就保证该数据库中所有的行版本都被冻结。 因此,只要该数据库没有其它的变化,那么它就不需要后续的清理以避免事务 ID 重叠问题。 这个技巧被 initdb 用于准备 template0数据库。 我们也应该用这个方法对所有在 pg_database表里标记着 datallowconn = false的数据库进行初始化, 因为我们还没有任何便利的方法 VACUUM 一个你无法联接的数据库。 请注意,VACUUM 将忽略那些pg_database 记录里有 datallowconn = false 条件的有关未清理数据库的自动警告信息。 以避免给出关于这些数据库的虚假的警告信息; 因此,确保这样的数据库的正常冻结是你的责任。


Warning
要想确保避免事务重叠的问题, 我们必须至少每十亿次事务清理一次每个数据库中的每个表,包括系统表。 我们已经有过因为没有做整个数据库范围的 vacuum 命令,而只是清理用户自己活跃的用户表而导致的数据丢失的例子。 只清理用户数据表看上去可行,但只是一段时间里如此。

TOP

21.2. 经常重建索引
有时候我们值得用 REINDEX 命令周期的重建索引。(还有一个 contrib/reindexdb 可以用于重建整个数据库的索引。) 不过,PostgreSQL 7.4 比以前地版本明显地减少了这件事情地需求。

TOP

21.3. 日志文件维护
把数据库服务器的日志输出保存在一个地方是个好主意, 而不是仅仅把它们放到 /dev/null 里。 在碰到危险的问题的时候,日志输出是非常宝贵的。 不过,日志输出可能很庞大(特别是在比较高的调试级别上), 而且你不会无休止地保存它们.你需要"旋转"日志文件, 这样生成新的日志文件并且经常抛弃老的.

如果你简单地把postmaster的stderr定向到一个文件中, 你会有日志输出, 但是截断日志文件的唯一的方法是停止并重起postmaster。 这样做对于开发环境中使用 PostgreSQL 可能是可以的,但是你肯定不想在生产环境上这么干。

一个更好的办法是把 postmaster 的 stderr 输出发送到某种日志旋转程序里。 我们有一个内置的日志旋转程序,你可以通过在 postgresql.conf 里设置配置参数 redirect_stderr 为 true 的办法打开它。这个程序的控制参数在 Section 16.4.6.1 里描述。

另外,你可能会觉得把 postmaster 的stderr 输出给某些日志旋转脚本会更好些,特别是你已经在其它服务器上用了这个程序的时候。 比如,包含在 Apache 发布里的 rotatelogs 工具就可以用于 PostgreSQL。 要这么做,只需要把 postmaster 的 stderr 重定向到指定程序。 如果你用 pg_ctl 启动服务器,那么 stderr 已经重定向到 stdout, 因此你只需要一个管道命令,比如:

pg_ctl start | rotatelogs /var/log/pgsql_log 86400

另外一种生产级的管理日志输出的方法就是把它们发送给 syslog,让 syslog 处理文件旋转。 要利用这个工具,我们需要设置 postgresql.conf 里的 log_destination 配置参数设置为 syslog (记录 syslog 日志)。 然后在你想强迫 syslog 守护进程开始写入一个新日志文件的时候, 你就可以发送一个 SIGHUP 信号给它。 如果你想自动旋转日志文件,那么我们可以配置 logrotate 程序处理 syslog 的日志文件。

不过,在很多系统上,syslog 不是非常可靠,特别是在大型日志信息的情况下; 它可能在你最需要那些信息的时候截断或者丢弃它们。 还有,在 linux 上,syslog 会把每个消息刷新到磁盘上, 导致很恶劣的性能。 (你可以在 syslog 配置文件里面的文件名开头使用一个 - 来关闭这个行为。)

请注意上面描述的所有解决方案关注的是在可配置的间隔上开始一个新的日志文件, 它们并没有处理删除旧的,不再需要的日志文件的事情。你可能还需要设置一个批处理,周期地删除旧日志文件。 另外一个可能的解法是配置日志旋转程序,让它周期地覆盖旧的日志文件。

TOP

Chapter 22. 备份和恢复
Table of Contents
22.1. SQL 转储
22.1.1. 从转储中恢复
22.1.2. 使用 pg_dumpall
22.1.3. 处理大数据库
22.1.4. 注意
22.2. 文件系统级别的备份
22.3. 在线备份以及即时恢复(PITR)
22.3.1. 设置 WAL 归档
22.3.2. 进行一次基础备份
22.3.3. 从在线备份中恢复
22.3.4. 时间线
22.3.5. 注意
22.4. 在不同版本之间迁移
和任何包含珍贵数据的东西一样,PostgreSQL 数据库也应该经常备份。尽管这个过程相当简单, 但是我们还是应该理解做这件事所用的一些技巧和假设。

备份 PostgreSQL 数据有三种完全不同的方法:



SQL 转储

文件系统级别备份

在线备份

每种备份都有自己的优点和缺点。

22.1. SQL 转储
SQL 转储的方法采用的主意是创建一个文本文件,这个文本里面都是 SQL 命令,当把这个文件回馈给服务器时,将重建与转储时状态一样的数据库。 PostgreSQL 为这个用途提供了应用工具 pg_dump。这条命令的基本用法是:

pg_dump dbname > outfile
正如你所见,pg_dump 把结果输出到标准输出。 我们下面就可以看到这样做有什么好处。

pg_dump 是一个普通的 PostgreSQL 客户端应用(尽管是个相当聪明的东西。)这就意味着你可以从任何可以访问该数据库的远端主机上面进行备份工作。 但是请记住 pg_dump 不会以任何特殊权限运行。具体说来, 就是它必须要有你想备份的表的读权限,因此,实际上你几乎总是要成为数据库超级用户。

要声明 pg_dump 应该以哪个用户身份进行联接,使用命令行选项 -h host 和 -p port。 缺省主机是本地主机或你的环境变量PGHOST声明的值。 类似,缺省端口是环境变量PGPORT或(如果它不存在的话)编译好了的缺省值。 (服务器通常有相同的缺省,所以还算方便。)

和任何其他 PostgreSQL 客户端应用一样, pg_dump 缺省时用与当前操作系统用户名同名的数据库用户名进行联接。 要覆盖这个名字,要么声明 -U 选项, 要么设置环境变量PGUSER。 请注意 pg_dump 的联接也和普通客户应用一样要通过客户认证机制(在 Chapter 19)里描述。

由 pg_dump 创建的备份在内部是一致的, 也就是说,在pg_dump运行的时候对数据库的更新将不会被转储。 pg_dump 工作的时候并不阻塞其他的对数据库的操作。 (但是会阻塞那些需要排它锁的操作,比如 VACUUM FULL。)

Important: 如果你的数据库结构依赖于 OID (比如说用做外键),那么你必须告诉 pg_dump 把 OID 也倒出来。 要倒 OID,可以使用 -o 命令行选项。 缺省时也不会转储"大对象"。如果你使用大对象,请参考 pg_dump 的命令手册页。

22.1.1. 从转储中恢复
pg_dump 生成的文本文件可以由 psql 程序读取。 从转储中恢复的常用命令是

psql dbname < infile
这里的 infile 就是你给pg_dump命令的 outfile参数。这条命令不会创建数据库 dbname,你必须在执行psql 前自己从template0创建(也就是说,用命令 createdb -T template0 dbname)。 psql 支持类似 pg_dump 的选项用以控制数据库服务器位置和用户名。 参阅 psql 的手册获取更多信息。

在开始运行恢复之前,目标库和所有在转储出来的库中拥有对象的用户, 以及曾经在某些对象上被赋予权限的用户都必须已经存在。 如果这些不存在,那么恢复将失败,因为恢复过程无法把这些对象恢复成原有的所有权和/或权限。 (有时候你希望恢复权限,不过通常你不需要这么做。)

一旦完成恢复,在每个数据库上运行 ANALYZE 是明智的举动, 这样优化器就有有用的统计数据了。你总是可以运行 vacuumdb -a -z 来 VACUUM ANALYZE 所有数据库;这个等效于手工运行 VACUUM ANALYZE。

pg_dump 和 psql 可以通过管道读写, 这样我们就可能从一台主机上将数据库目录转储到另一台主机上,比如

pg_dump -h host1 dbname | psql -h host2 dbname

Important: pg_dump生成的转储输出是相对于template0的。这就意味着任何 加入到template1的语言,过程等都会经由 pg_dump 转储。结果是,在恢复的时候,如果你使用的是客户化的template1, 那么你必须从template0中创建空的数据库,就象我们上面的例子那样。

有关如何有效地向 PostgreSQL 里装载大量数据的建议, 请参考 Section 13.4。

22.1.2. 使用 pg_dumpall
上面的方法在备份整个数据库集群的时候比较麻烦而且不方便。因此我们提供了 pg_dumpall 程序。 pg_dumpall 备份一个给出的集群中的每个数据库,同时还确保保留象用户和组这样的全局数据状态。 这个命令的基本用法是:

pg_dumpall > outfile
生成的转储可以用 psql 恢复:

psql template1 < infile
(实际上,你可以声明任意现有的数据库进行连接,但是如果你是向一个空的数据库装载,那么 template1 是你唯一的选择。) 恢复pg_dumpall的转储的时候通常需要数据库超级用户权限,因为我们需要它来恢复用户和组信息。

22.1.3. 处理大数据库
因为 PostgreSQL 允许表的大小大于你的系统允许的最大文件大小, 可能把表转储到一个文件会有问题,因为生成的文件很可能比你的系统允许的最大文件大。 因为 pg_dump 输出到标准输出,你可以用标准的 Unix 工具绕开这个问题:

使用压缩的转储. 使用你熟悉的压缩程序,比如说 gzip。

pg_dump dbname | gzip > filename.gz
用下面命令恢复:

createdb dbname
gunzip -c filename.gz | psql dbname
或者

cat filename.gz | gunzip | psql dbname

使用 split。. split 命令允许你 你用下面的方法把输出分解成操作系统可以接受的大小。 比如,让每个块大小为 1 兆字节:

pg_dump dbname | split -b 1m - filename
用下面命令恢复:

createdb dbname
cat filename* | psql dbname

使用客户化转储格式. 如果PostgreSQL是在一个安装了zlib 压缩库的系统上制作的, 那么客户化转储格式将在写入输出文件的时候压缩数据。 它会生成和使用 gzip 类似大小的转储文件, 但是还附加了一个优点:你可以有选择地恢复库中的表。 下面的命令用客户化转储格式转储一个数据库:

pg_dump -Fc dbname > filename
客户化格式的转储不是脚本,不能用于 psql, 而是需要使用 pg_restore 转储。 请参考 pg_dump 和 pg_restore 的手册获取细节。

22.1.4. 注意
出于向下兼容的考虑,缺省的时候 pg_dump 并不转储大对象。 要转储大对象,你必须使用客户化或者 tar 输出格式, 并且在 pg_dump 中使用-b选项。 参阅 pg_dump 手册获取详细信息。 在 PostgreSQL 源码树的 contrib/pg_dumplo 路径里也包含一个可以转储大对象的程序。

请熟悉一下 pg_dump 的手册页。

TOP


感谢一直以来您对我们的支持!
当前时区 GMT+8, 现在时间是 2008-8-22 06:49 京ICP证060528 号

Designed By 17DST