PostgreSQL教程(二十一):服务器管理(三)之服务器设置和操作

本章讨论如何设置和运行数据库服务器,以及它与操作系统的交互。

一、PostgreSQL用户账户

和对外部世界可访问的任何服务器守护进程一样,我们也建议在一个独立的用户账户下运行PostgreSQL。这个用户账户应该只拥有被该服务器管理的数据,并且应该不能被其他守护进程共享(例如,使用用户nobody是一个坏主意)。我们不建议把可执行文件安装为属于这个用户,因为妥协系统可能接着修改它们自己的二进制文件。

要在你的系统中增加一个 Unix 用户账户,查看一个命令useraddadduser。通常会用postgres(本书中也假定用这个账户),但是你可以使用另一个名称。

二、创建一个数据库集簇

在你能做任何事情之前,你必须在磁盘上初始化一个数据库存储区域。我们称之为一个数据库集簇(SQL标准使用的术语是目录集簇)。一个数据库集簇是被一个运行数据库服务器的单一实例所管理的多个数据库的集合。在初始化之后,一个数据库集簇将包含一个名为postgres的数据库,它表示被功能、用户和第三方应用所使用的默认数据库。数据库服务器本身并不要求postgres数据库存在。另一个在初始化过程中为每一个集簇创建的数据库被称为template1。顾名思义,它将被用于创建后续数据库的模板;它不应该被用于实际工作(在集簇内创建新数据库的更多信息请见后续章节)。

在文件系统术语中,一个数据库集簇是一个单一目录,所有数据都将被存储在其中。我们称它为数据目录数据区域 。在哪里存储你的数据完全由你选择。没有默认的位置,不过/usr/local/pgsql/data/var/lib/pgsql/data位置比较流行。要初始化一个数据库集簇,使用和PostgreSQL一起安装的命令initdb。你的数据库集簇的文件系统位置由-D选项指定,例如:

$ initdb -D /usr/local/pgsql/data

注意你必须在使用PostgreSQL用户账户(如前一节所示)登录后执行这个命令。

提示:

作为-D选项的一种替换方案,你可以设置环境变量PGDATA

另一种替代方案是,你可以通过pg_ctl程序来运行initdb

$ pg_ctl -D /usr/local/pgsql/data initdb

如果你使用pg_ctl来启停服务器(见第三节 ),这种方法可能更直观,以为这样pg_ctl将是你用来管理数据库服务器实例的唯一命令。

如果你指定的目录还不存在,initdb将尝试创建它。当然,如果initdb没有在父目录中的写权限,这将会失败。通常推荐让PostgreSQL用户拥有数据目录及其父目录,这样就不存在上面的问题了。如果想要的父目录也不存在,你将需要先创建它,如果父目录不可写则使用 root 特权。因此,该过程可能像这样:

root# mkdir /usr/local/pgsql
root# chown postgres /usr/local/pgsql
root# su postgres
postgres$ initdb -D /usr/local/pgsql/data

如果数据目录存在并且已经包含文件,initdb将拒绝运行。这可以避免无意中覆盖一个已有的安装。

因为数据目录包含所有存储在数据库里的数据,所以最重要的是保护这个目录不受未授权的访问。因此,initdb会回收禁止除PostgreSQL用户,也可以选择组,之外所有用户的访问权限。当组访问启用时,是只读的。它允许相同组中未被授权的用户作为集簇属主,备份集簇数据或者执行其他只需要读访问权限的操作。

注意在现有集群启用或禁用组访问时,需要关闭集群,且重新启动PostgreSQL之前设置所有的目录和文件到恰当的模式。否则,数据目录中会存在多种模式。集群仅可以被其属主访问,恰当的模式应该是,其目录设置为0700,普通文件设置为0600。允许集群被组可读,恰当的模式应该是,其目录设置为0750,普通文件设置为0640

不过,虽然目录的内容是安全的,但默认的客户端认证设置允许任意本地用户连接到数据库甚至成为数据库超级用户。如果你不信任其他本地用户, 我们建议你使用initdb-W--pwprompt--pwfile选项之一给数据库超级用户赋予一个口令。还可以指定-A md5-A password,这样就不会使用默认的trust 身份认证。或者在执行initdb之后、第一次启动服务器之前 修改生成的pg_hba.conf文件(另外一些可行的方法包括peer认证或者用文件系统权限限制连接)。

initdb同时也为数据库集簇初始化默认区域。 通常,它将只是使用环境中的区域设置并且把它们应用于被初始化的数据库。 可以为数据库指定一个不同的区域; 特定数据库集簇中使用的默认排序顺序是通过initdb设置的, 虽然你可以创建使用不同排序顺序的新数据库,但在 initdb 创建的模板数据库中使用的顺序不能更改(除非删除并重建它们)。使用非CPOSIX的区域还会对性能造成影响。因此,第一次就正确地选择很重要。

initdb还为数据库集簇设置默认的字符集编码。通常字符集编码应该选择与区域设置匹配。

C以及非POSIX区域对于字符集排序依赖于操作系统的排序规则库。这控制着索引中存储的键的排序。为此,通过快照恢复、二进制流复制、更换不同的操作系统或者升级操作系统都不能把一个集簇切换到一种不兼容的排序规则库版本。

2.1 二级文件系统的使用

很多安装会在文件系统(卷)而不是机器的"根"卷上创建它们的数据库集簇。如果你选择这样做,我们不建议尝试使用二级卷的顶层目录(挂载点)作为数据目录。最好的做法是在PostgreSQL用户拥有的挂载点目录中创建一个目录,然后在其中创建数据目录。这可以避免权限问题,特别是对于pg_upgrade这类操作,并且它也能在二级卷被断线后确保干净的失败。

2.2. 网络文件系统的使用

许多安装会在网络文件系统上创建它们的数据库集簇。有时直接通过NFS, 或通过内部使用NFS的网络附加存储设备(NAS)完成。 PostgreSQL不对 NFS文件系统做特殊处理,即它假定NFS的行为和本地连接的设备完全一样。如果客户端或者服务器NFS没有提供标准的文件系统语义,这将导致可靠性问题 (参阅https://www.time-travellers.org/shane/papers/NFS_considered_harmful.html)。 具体来说,延迟(异步)写入到NFS服务器可以导致数据损坏问题。 如果可能的话,把NFS文件系统挂载为同步(无高速缓存)可以避免这种灾难。还有,我们不推荐软挂载的NFS文件系统。

存储区域网络(SAN)通常使用非NFS的通讯协议,并且可能或者不可能遭受这类灾难。建议咨询供应商的文档来了解数据一致性保证。PostgreSQL无法做到比它所使用的文件系统更可靠。

三、启动数据库服务器

在任何人可以访问数据库前,你必须启动数据库服务器。 数据库服务器程序是postgres, 它必须知道在哪里能找到它要用的数据。这是用-D选项实现的。 因此,启动服务器最简单的方法是:

$ postgres -D /usr/local/pgsql/data

这将把服务器放在前台运行。这个步骤同样必须以PostgreSQL用户帐户登录来操作。如果没有-D选项,服务器将尝试使用环境变量PGDATA命名的目录。如果这个环境变量也没有提供则导致失败。

通常最好在后台启动postgres。要这样做,使用常用的 Unix shell 语法:

$ postgres -D /usr/local/pgsql/data >logfile 2>&1 &

如上所示,把服务器的stdout和stderr输出存储到某个地方是非常重要的。这将对审计目的和诊断问题有所帮助。

postgres还接受其它一些命令行选项,更多详细信息见下一章。

这些 shell 语法很容易让人觉得无聊。因此我们提供了包装器程序pg_ctl以简化一些任务。例如:

pg_ctl start -l logfile

将在后台启动服务器并且把输出放到指定的日志文件中。-D选项和postgres中的一样。pg_ctl还可以用于停止服务器。

通常,你会希望在计算机启动的时候启动数据库服务器。自动启动脚本是操作系统相关的。PostgreSQL在contrib/start-scripts目录中提供了几种。安装将需要 root 权限。

不同的系统在引导时有不同的启动守护进程的习惯。许多系统有一个文件/etc/rc.local/etc/rc.d/rc.local。其他的使用init.drc.d目录。不管你做什么,服务器必须由PostgreSQL用户账户而不是 root或任何其他用户启动。因此你可能应该在你的命令中使用su postgres -c '...'这种形式。例如:

su postgres -c 'pg_ctl start -D /usr/local/pgsql/data -l serverlog'

下面是一些更加与操作系统相关的建议(在每一种情况中要确保在我们展示通用值的地方使用正确的安装目录和用户名)。

  • 对于FreeBSD,找找PostgreSQL源码发布中的文件contrib/start-scripts/freebsd

  • 在OpenBSD上, 把下面几行加到/etc/rc.local文件中:

    if [ -x /usr/local/pgsql/bin/pg_ctl -a -x /usr/local/pgsql/bin/postgres ]; then
    su -l postgres -c '/usr/local/pgsql/bin/pg_ctl start -s -l /var/postgresql/log -D /usr/local/pgsql/data'
    echo -n ' postgresql'
    fi

  • 在Linux系统上将

    /usr/local/pgsql/bin/pg_ctl start -l logfile -D /usr/local/pgsql/data

加入到/etc/rc.d/rc.local/etc/rc.local中,还可以在PostgreSQL的源码发布中找找文件contrib/start-scripts/linux

在使用systemd时,可以使用下面的服务单元文件(例如/etc/systemd/system/postgresql.service):

[Unit]
Description=PostgreSQL database server
Documentation=man:postgres(1)

[Service]
Type=notify
User=postgres
ExecStart=/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data
ExecReload=/bin/kill -HUP $MAINPID
KillMode=mixed
KillSignal=SIGINT
TimeoutSec=0

[Install]
WantedBy=multi-user.target

使用Type=notify要求服务器的二进制文件使用configure --with-systemd编译。

要仔细地考虑超时设置。在写作这份文档时,systemd的默认超时时长是 90 秒,并且将会杀死没有在这段时间内报告准备好的进程。但是PostgreSQL服务器可能因为执行崩溃恢复而导致启动过程大大超过这个默认时间。建议的值是 0 禁用超时逻辑。

  • 在NetBSD上,你可以根据爱好选择FreeBSD或Linux的启动脚本。

  • 在Solaris上,创建一个名为/etc/init.d/postgresql的文件,其中包含下列行:

    su - postgres -c "/usr/local/pgsql/bin/pg_ctl start -l logfile -D /usr/local/pgsql/data"

然后在/etc/rc3.d中创建一个符号链接S99postgresql指向它。

当服务器在运行时,它的PID被保存在数据目录中的postmaster.pid文件。这样做 可以防止多个服务器实例运行在同一个数据目录中,并且也可以被用来关闭服务器。

3.1 服务器启动失败

有几个常见的原因会导致服务器启动失败。通过检查服务器日志或使用手工启动的方法(不做标准输出或标准错误的重定向), 就可以看到出现什么错误消息。下面我们详细地解释一些最常见的错误消息。

LOG:  could not bind IPv4 address "127.0.0.1": Address already in use
HINT:  Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
FATAL:  could not create any TCP/IP sockets

正如这个消息所说的,这表示:你试图在一个已经有服务器运行着的端口上再启动另一个服务器。不过,如果核心错误消息不是Address already in use或其变体,那就有可能是别的问题。 例如,试图在一个被保留的端口上启动服务器会收到下面这样的消息:

$ postgres -p 666
LOG:  could not bind IPv4 address "127.0.0.1": Permission denied
HINT:  Is another postmaster already running on port 666? If not, wait a few seconds and retry.
FATAL:  could not create any TCP/IP sockets

像这样的消息:

FATAL:  could not create shared memory segment: Invalid argument
DETAIL:  Failed system call was shmget(key=5440001, size=4011376640, 03600).

可能意味着你的内核对共享内存区的限制小于PostgreSQL试图创建的工作区域(本例中是 4011376640 字节)。或者可能意味着根本就没有 System-V 风格的共享内存支持被配置在你的内核中。作为一种临时的解决方案, 你可以试着以小于正常数量的缓冲区(shared_buffers)启动服务器。 你最终还是会希望重新配置内核以增加共享内存允许的尺寸。 当你试图在同一台机器上启动多个服务器,并且它们所需的总空间超过了内核的限制,也会报这个错。

一个这样的错误:

FATAL:  could not create semaphores: No space left on device
DETAIL:  Failed system call was semget(5440126, 17, 03600).

意味着你已经用光了磁盘空间。它的意思是你的内核对System V信号量的限制小于PostgreSQL想创建的数量。和上面一样,你可以通过减少允许的连接数(max_connections)来绕开这个限制,但最终你还是会希望提高内核的限制。

如果你收到一个"illegal system call"错误, 那么很有可能是你的内核根本不支持共享内存或者信号量。这种情况下你唯一的选择就是重新配置内核并且把这些特性打开。

关于配置System V IPC功能的细节请见下面第四节。

3.2 客户端连接问题

尽管可能在客户端出现的错误情况范围宽广而且是应用相关的,但的确有几种与服务器的启动方式直接相关。除了下面提到的几种错误之外的问题都应该在相应的客户端应用文档中。

psql: could not connect to server: Connection refused
        Is the server running on host "server.joe.com" and accepting
        TCP/IP connections on port 5432?

这是常见的"I couldn't find a server to talk to"失败。上面的情况看起来是发生在尝试 TCP/IP 通信时。常见的错误是忘记把服务器配置成允许 TCP/IP 连接。

另外,当试图通过 Unix 域套接字与本地服务器通信时,你会看到这个:

psql: could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

最后一行可以验证客户端是不是尝试连接到正确的位置。如果实际上没有服务器在那里运行,典型的核心错误消息将是Connection refusedNo such file or directory(值得注意的是这种环境中的Connection refused 表示服务器得到了你的连接请求并拒绝了它。那种情况会产生一个不同的消息)。其它像Connection timed out这样的消息可能表示更基础的问题,如缺少网络连接。

四、管理内核资源

PostgreSQL某些时候会耗尽操作系统的各种资源限制,当同一个系统上运行着多个拷贝的服务器或在一个非常大的安装中时尤其如此。本节解释了PostgreSQL使用的内核资源以及你可以采取的用于解决内核资源消耗相关问题的步骤。

4.1. 共享内存和信号量

PostgreSQL需要操作系统提供进程间通信(IPC)特性, 特别是共享内存和信号量。Unix驱动的系统通常提供 "System V" IPC、 "POSIX" IPC,或者两者都有。 Windows有它自己的这些功能的实现,这里不讨论。

完全缺少这些功能通常表现为服务器启动时的"Illegal system call"错误。这种情况下,除了重新配置内核之外别无选择。PostgreSQL没有它们就不能工作。 不过,在现代操作系统中这种情况是罕见的。

在启动服务器时,PostgreSQL通常分配少量的System V共享内存, 和大量的POSIX (mmap)共享内存。另外, 在服务器启动时会创建大量信号量,这些信号量可以是System V或POSIX风格。 目前,POSIX信号量用于Linux和FreeBSD系统,而其他平台则使用System V信号量。

注意:

在PostgreSQL 9.3之前,只使用了System V共享内存, 所以启动服务器所需的System V共享内存的数量更大一些。 如果你在运行着一个老版本的服务器,请参考该服务器版本的文档。

System V IPC特性通常受系统范围分配限制的限制。 当PostgreSQL超出了这些限制之一时,服务器会拒绝启动并且并且留下一条有指导性的错误消息,其中描述了问题以及应该怎么做(又见上述3.1节)。相关的内核参数在不同系统之间的命名方式一致,表4.1给出了一个概述。不过,设置它们的方法却多种多样。下面给出了对于某些平台的建议:

表 4.1. System V IPC参数

名称 描述 运行一个PostgreSQL实例所需的值值
SHMMAX 共享内存段的最大尺寸(字节) 至少 1kB,但是默认值通常要高一些
SHMMIN 共享内存段的最小尺寸(字节) 1
SHMALL 可用共享内存的总量(字节或页面) 如果是字节,同SHMMAX;如果是页面, 为ceil(SHMMAX/PAGE_SIZE),加上其他应用程序的空间
SHMSEG 每个进程的最大共享内存段数目 只需要 1 段,但是默认值高很多
SHMMNI 系统范围内的最大共享内存段数目 SHMSEG外加其他应用的空间
SEMMNI 信号量标识符(即,集合)的最大数目 至少ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5) / 16)加上其他应用程序的空间
SEMMNS 系统范围内的最大信号量数目 ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5) / 16) * 17外加其他应用的空间
SEMMSL 每个集合中信号量的最大数目 至少 17
SEMMAP 信号量映射中的项数 见文本
SEMVMX 信号量的最大值 至少 1000 (默认值常常是 32767,如非必要不要更改)

PostgreSQL要求少量字节的 System V 共享内存(在 64 位平台上通常是 48 字节) 用于每一个服务器拷贝。在大多数现代操作系统上,这个量很容易得到。但是,如果你运行了很多个服务器拷贝, 或者其他应用也在使用 System V 共享内存,可能需要增加SHMALL(系统范围内 System V 共享内存的总量)。注意在很多系统上SHMALL是以页面而不是字节来度量。

不太可能出问题的是共享内存段的最小尺寸(SHMMIN),对PostgreSQL来说应该最多大约是 32 字节(通常只是1)。而系统范围(SHMMNI)或每个进程(SHMSEG)的最大共享内存段数目不太可能会导致问题,除非你的系统把它们设成零。

当使用System V信号量时,PostgreSQL对每个允许的连接(max_connections)、每个允许的自动清理工作者进程(autovacuum_max_workers)和每个允许的后台进程(max_worker_processes)使用一个信号量, 以16个为一个集合。每个这种集合还包含第 17 个信号量, 其中存储一个"magic number",以检测和其它应用使用的信号量集合的冲突。 系统里的最大信号量数目是由SEMMNS设置的, 因此这个值必须至少和max_connectionsautovacuum_max_workers再加max_worker_processes一样大, 并且每 16 个连接外加工作者还要另外加一个(见表4.1中的公式)。参数SEMMNI 决定系统中同一时刻可以存在的信号量集合的数目限制。因此这个参数必须至少为ceil((max_connections + autovacuum_max_workers + max_worker_processes + 5) / 16)。降低允许的连接数目是一种临时的绕开失败(来自函数semget)的方法,通常使用让人混乱的措辞"No space left on device"。

在某些情况下可能还有必要增大SEMMAP,使之至少与SEMMNS相近。如果系统有这个参数(很多系统没有),这个参数定义信号量资源映射的尺寸,在其中每个连续的可用信号量块都需要一项。 每当一个信号量集合被释放,那么它要么会被加入到该与被释放块相邻的一个现有项,或者它会被注册在一个新映射项中。如果映射被填满,被释放的信号量将丢失(直到重启)。因此信号量空间的碎片时间长了会导致可用的信号量比应有的信号量少。

与"semaphore undo"有关的其他各种设置,如SEMMNUSEMUME 不会影响PostgreSQL。

当使用POSIX信号量时,所需的信号量数量与System V相同, 即每个允许的连接(max_connectionsmax_connectionsmax_connections)、允许的自动清理工作进程 (autovacuum_max_workersautovacuum_max_workersautovacuum_max_workers)和允许的后台进程 (max_worker_processesmax_worker_processesmax_worker_processes)一个信号量。 在首选此选项的平台上,POSIX信号量的数量没有特定的内核限制。

AIX

至少到版本 5.1 为止,不再需要对这些参数(例如SHMMAX)做任何特殊的配置,这看起来就像是被配置成允许所有内存都被用作共享内存。这是一种通常被用于其他数据库(DB/2)的配置。

但是,可能需要修改/etc/security/limits中的全局ulimit信息,默认的文件尺寸硬限制(fsize)和文件数量(nofiles)可能太低。

FreeBSD

可以使用sysctlloader接口来改变默认IPC配置。下列参数可以使用sysctl设置:

# sysctl kern.ipc.shmall=32768
# sysctl kern.ipc.shmmax=134217728
# sysctl kern.ipc.semmap=256

要让这些设置在重启之后也保持,请修改/etc/sysctl.conf

对于sysctl所关心的来说这些信号量相关的设置都是只读的,但是可以在/boot/loader.conf中设置:

kern.ipc.semmni=256
kern.ipc.semmns=512

修改该配置文件后,需要重启一次让新设置生效。

你可能也希望你的内核将共享内存锁定在 RAM 中并且防止它被换页到交换分区。这可以使用sysctl的设置 kern.ipc.shm_use_phys来完成。

如果通过启用sysctl的security.jail.sysvipc_allowed运行在 FreeBSD jail 中,运行在不同 jail 中的postmaster应当被不同的操作系统用户运行。这可以提高安全性,因为它阻止非 root 用户干涉不同 jail 中的共享内存或信号量,并且它允许 PostgreSQL IPC 清理代码正确地工作(在 FreeBSD 6.0 及其后的版本中,IPC 清理代码不能正确地检测到其他 jail 中的进程,也不能阻止不同 jail 中的 postmaster 运行在相同的端口)。

FreeBSD 4.0 之前的版本的工作与旧版OpenBSD相似(见下文)。

NetBSD

在NetBSD 5.0 及其后的版本中,IPC 参数可以使用sysctl调整。例如:

$ sysctl -w kern.ipc.semmni=100

要使这些设置在重启后保持,请修改/etc/sysctl.conf

作为NetBSD的默认设置,你总是会想 调大kern.ipc.semmnikern.ipc.semmns的值,因为他们实在太小了。

你可能也希望你的内核将共享内存锁定在 RAM 中并且防止它被换页到交换分区。这可以使用sysctl的设置 kern.ipc.shm_use_phys来完成。

NetBSD 5.0 以前的版本的工作与旧版OpenBSD相似(见下文),除了那些内核参数应该用关键词options设置而不是option

OpenBSD

在OpenBSD3.3及以后版本,使用sysctl命令,IPC参数可以被自动调节,例如:

# sysctl kern.seminfo.semmni=100

要使这些设置在重启后保持,请修改/etc/sysctl.conf

作为OpenBSD的默认配置,你总是会想调大kern.seminfo.semmnikern.seminfo.semmns的值,因为他们实在太小了。

在较早的OpenBSD 版本中,你需要编译定制化内核来修改这些IPC参数。也要确保SYSVSHMSYSVSEM选项为启用状态。(这两项默认都是启用状态。) 下面给出一些内核配置文件中如何设置这些参数的例子:

option        SYSVSHM
option        SHMMAXPGS=4096
option        SHMSEG=256

option        SYSVSEM
option        SEMMNI=256
option        SEMMNS=512
option        SEMMNU=256

HP-UX

默认的设置可以满足正常的安装。在HP-UX 10 上,SEMMNS的出厂默认值是 128,这可能对大型数据库站点太低。

IPC参数可以在Kernel Configuration → Configurable Parameters下的System Administration Manager(SAM)中被设置。当你完成时选择Create A New Kernel。

Linux

默认的最大段尺寸是 32 MB,并且默认的最大总尺寸是 2097152 个页面。一个页面几乎总是 4096 字节,除了在使用少见"huge pages"的内核配置中(使用getconf PAGE_SIZE来验证)。

共享内存尺寸设置可以通过sysctl接口来更改。例如,要允许 16 GB:

$ sysctl -w kernel.shmmax=17179869184
$ sysctl -w kernel.shmall=4194304

另外在重启之间这些设置可以被保存在文件/etc/sysctl.conf中。我们强烈推荐这样做。

古老的发型可能没有sysctl程序,但是可以通过操纵/proc文件系统来得到等效的更改:

$ echo 17179869184 >/proc/sys/kernel/shmmax
$ echo 4194304 >/proc/sys/kernel/shmall

剩下的默认值都被设置得很宽大,并且通常不需要更改。

macOS

在 macOS 中配置共享内存的推荐方法是创建一个名为/etc/sysctl.conf的文件,其中包含这样的变量赋值:

kern.sysv.shmmax=4194304
kern.sysv.shmmin=1
kern.sysv.shmmni=32
kern.sysv.shmseg=8
kern.sysv.shmall=1024

注意在某些 macOS 版本中,所有五个 共享内存参数必须在/etc/sysctl.conf中设置,否则值将会被忽略。

注意近期的 macOS 版本会忽略把SHMMAX设置成非 4096 倍数值的尝试。

在这个平台上,SHMALL以 4kB 的页面度量。

在更老的 macOS 版本中,你将需要重启来让共享内存参数的更改生效。到了 10.5,可以使用sysctl随时改变除了SHMMNI之外的所有参数。但是最好还是通过/etc/sysctl.conf来设置你喜欢的值,这样重启之后这些值还能被保持。

只有在 macOS 10.3.9 及以后的版本中才遵循/etc/sysctl.conf文件。如果你正在使用 10.3.x 之前的发布,你必须编辑文件/etc/rc并且在下列命令中改变值:

sysctl -w kern.sysv.shmmax
sysctl -w kern.sysv.shmmin
sysctl -w kern.sysv.shmmni
sysctl -w kern.sysv.shmseg
sysctl -w kern.sysv.shmall

注意/etc/rc通常会被 macOS 的系统更新所覆盖,因此你应该在每次更新之后重做这些编辑。

在 macOS 10.2 及更早的版本中,应该在文件/System/Library/StartupItems/SystemTuning/SystemTuning中编辑这些命令。

Solaris 2.6 至 2.9 (Solaris 6 至 Solaris 9)

相似的设置可以在/etc/system中更改,例如:

set shmsys:shminfo_shmmax=0x2000000
set shmsys:shminfo_shmmin=1
set shmsys:shminfo_shmmni=256
set shmsys:shminfo_shmseg=256

set semsys:seminfo_semmap=256
set semsys:seminfo_semmni=512
set semsys:seminfo_semmns=512
set semsys:seminfo_semmsl=32

你需要重启来让更改生效。关于更老版本的 Solaris 下的共享内存的信息请见Shared memory uncovered - SunWorld - September 1997

Solaris 2.10 (Solaris 10) 及以后
OpenSolaris

在 Solaris 10 及以后的版本以及 OpenSolaris 中,默认的共享内存和信号量设置已经足以应付大部分PostgreSQL应用。Solaris 现在将SHMMAX的默认值设置为系统 RAM的四分之一。要进一步调整这个设置,使用与postgres用户有关的一个项目设置。例如,以root运行下列命令:

projadd -c "PostgreSQL DB User" -K "project.max-shm-memory=(privileged,8GB,deny)" -U postgres -G postgres user.postgres

这个命令增加user.postgres项目并且将用于postgres用户的最大共享内存设置为 8GB,并且在下次用户登录进来时或重启PostgreSQL(不是重新载入)时生效。上述假定PostgreSQL是由postgres组中的postgres用户所运行。不需要重新启动服务器。

对于将有巨大数量连接的数据库服务器,我们推荐的其他内核设置修改是:

project.max-shm-ids=(priv,32768,deny)
project.max-sem-ids=(priv,4096,deny)
project.max-msg-ids=(priv,4096,deny)

此外,如果你正在在一个区中运行PostgreSQL,你可能也需要提升该区的资源使用限制。更多关于projectsprctl的信息请见System Administrator's Guide中的 "Chapter2: Projects and Tasks"。

4.2 systemd RemoveIPC

如果正在使用systemd,则必须注意IPC资源(共享内存和信号量) 不会被操作系统过早删除。从源代码安装PostgreSQL时,这尤其值得关注。 PostgreSQL发布包的用户不太可能受到影响,因为postgres用户通常是作为系统用户创建的。

控制当用户完全退出时是否移除IPC对象。系统用户免除。 此设置在死板的systemd中默认为on, 但某些操作系统分配默认为关闭。

当此设置打开时,典型的观察效果是PostgreSQL服务器使用的信号量对象在明显随机的时间被删除, 从而导致服务器崩溃,并显示日志消息

LOG: semctl(1234567890, 0, IPC_RMID, ...) failed: Invalid argument

不同类型的IPC对象(共享内存与信号量,System V与POSIX)在systemd 中略有不同,因此可能会发现某些IPC资源不会像其他IPC资源一样被删除。 但依靠这些微妙的差异是不可取的。

"注销用户"可能会作为维护工作的一部分发生,或者当管理员以 postgres用户或类似名称登录时手动发生,所以通常难以防止。

什么是"系统用户"是由/etc/login.defs中的 SYS_UID_MAX设置在systemd编译时确定的。

打包和部署脚本应该小心,通过使用useradd -radduser --system或等价物来创建postgres用户作为系统用户。

或者,如果用户帐户创建不正确或无法更改,建议设置

RemoveIPC=no

/etc/systemd/logind.conf或其他适当的配置文件中。

小心:

至少要确保这两件事中的一件,否则PostgreSQL服务器将非常不可靠。

4.3 资源限制

Unix类操作系统强制了许多种资源限制,这些限制可能干扰你的PostgreSQL服务器的操作。尤其重要的是对每个用户的进程数目的限制、每个进程打开文件数目的限制以及每个进程可用的内存的限制。这些限制中每个都有一个"硬"限制和一个"软"限制。实际使用的是软限制,但用户可以自己修改成最大为硬限制的数目。而硬限制只能由root用户修改。系统调用setrlimit负责设置这些参数。 shell的内建命令ulimit(Bourne shells)或limit(csh)被用来从命令行控制资源限制。 在 BSD 衍生的系统上,/etc/login.conf文件控制在登录期间设置的各种资源限制。详见操作系统文档。相关的参数是maxprocopenfilesdatasize。例如:

default:\
...
        :datasize-cur=256M:\
        :maxproc-cur=256:\
        :openfiles-cur=256:\
...

-cur是软限制。增加-max可设置硬限制)。

内核也可以在某些资源上有系统范围的限制。

  • 在Linux上,/proc/sys/fs/file-max决定内核可以支持打开的最大文件数。你可以通过往该文件写入一个不同的数值修改此值, 或者通过在/etc/sysctl.conf中增加一个赋值来修改。 每个进程的最大打开文件数限制是在编译内核的时候固定的;更多信息请见/usr/src/linux/Documentation/proc.txt

PostgreSQL服务器为每个连接都使用一个进程, 所以你应该至少和允许的连接同样多的进程,再加上系统其它部分所需要的进程数目。 通常这个并不是什么问题,但如果你在一台机器上运行多个服务器,资源使用可能就会紧张。

打开文件的出厂默认限制通常设置为"socially friendly"的值, 它允许许多用户在一台机器上共存,而不会导致不成比例的系统资源使用。 如果你在一台机器上运行许多服务器,这也许就是你想要的,但是在专门的服务器上, 你可能需要提高这个限制。

在另一方面,一些系统允许独立的进程打开非常多的文件;如果不止几个进程这么干,那系统范围的限制就很容易被超过。如果你发现这样的现像, 并且不想修改系统范围的限制,你就可以设置PostgreSQL的 max_files_per_process配置参数来限制打开文件数的消耗。

4.4 Linux内存过量使用

在 Linux 2.4 及其后的版本中,默认的虚拟内存行为对PostgreSQL不是最优的。由于内核实现内存过量使用的方法,如果PostgreSQL或其它进程的内存要求导致系统用光虚拟内存,那么内核可能会终止PostgreSQL的 postmaster 进程(主服务器进程)。

如果发生了这样的事情,你会看到像下面这样的内核消息(参考你的系统文档和配置,看看在哪里能看到这样的消息):

Out of Memory: Killed process 12345 (postgres).

这表明postgres进程因为内存压力而被终止了。尽管现有的数据库连接将继续正常运转,但是新的连接将无法被接受。要想恢复,PostgreSQL应该被重启。

一种避免这个问题的方法是在一台你确信其它进程不会耗尽内存的机器上运行PostgreSQL。 如果内存资源紧张,增加操作系统的交换空间可以帮助避免这个问题,因为内存不足(OOM)杀手(即终止进程这种行为)只有当物理内存和交换空间都被用尽时才会被调用。

如果PostgreSQL本身是导致系统内存耗尽的原因,你可以通过改变你的配置来避免该问题。在某些情况中,降低内存相关的配置参数可能有所帮助,特别是 shared_buffers和work_mem两个参数。在其他情况中,允许太多连接到数据库服务器本身也可能导致该问题。在很多情况下,最好减小max_connections并且转而利用外部连接池软件。

在 Linux 2.6 及其后的版本中,可以修改内核的行为,这样它将不会"过量使用"内存。尽管此设置不会阻止OOM 杀手被调用,但它可以显著地降低其可能性并且将因此得到更鲁棒的系统行为。这可以通过用sysctl选择严格的过量使用模式来实现:

sysctl -w vm.overcommit_memory=2

或者在/etc/sysctl.conf中放置一个等效的项。你可能还希望修改相关的设置vm.overcommit_ratio。 详细信息请参阅内核文档的https://www.kernel.org/doc/Documentation/vm/overcommit-accounting文件。

另一种方法,可以在改变或不改变vm.overcommit_memory的情况下使用。它将 postmaster 进程的进程相关的OOM score adjustment 值设置为-1000,从而保证它不会成为 OOM 杀手的目标。 这样做最简单的方法是在 postmaster 的启动脚本中执行

echo -1000 > /proc/self/oom_score_adj

并且要在调用 postmaster 之前执行。请注意这个动作必须以 root 完成,否则它将不会产生效果。所以一个被 root 拥有的启动脚本是放置这个动作最容易的地方。如果这样做,你还应该在调用 postmaster 之前在启动脚本中设置这些环境变量:

export PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj
export PG_OOM_ADJUST_VALUE=0

这些设置将导致 postmaster 子进程使用普通的值为零的 OOM score adjustment 运行,所以 OOM 杀手仍能在需要时把它们作为目标。如果你想要子进程用某些其他 OOM score adjustment 值运行,可以为PG_OOM_ADJUST_VALUE使用其他的值(PG_OOM_ADJUST_VALUE也能被省略,那时它会被默认为零)。如果你没有设置PG_OOM_ADJUST_FILE,子进程将使用和 postmaster 相同的 OOM score adjustment 运行,这是不明智的,因为重点是确保 postmaster 具有优先的设置。

更老的 Linux 内核不提供/proc/self/oom_score_adj,但是可能有一个具有相同功能的早期版本,它被称为/proc/self/oom_adj。这种方式工作起来完全相同,除了禁用值是-17而不是-1000

注意:

有些厂商的 Linux 2.4 内核被报告有着 2.6 过量使用sysctl参数的早期版本。不过,在没有相关代码的 2.4 内核里设置vm.overcommit_memory为 2 将会让事情更糟。我们推荐你检查一下实际的内核源代码(见文件mm/mmap.c中的vm_enough_memory函数),验证一下这个是在你的内核中是被支持的, 然后再在 2.4 安装中使用它。文档文件overcommit-accounting的存在能当作是这个特性存在的证明。如果有疑问,请咨询一位内核专家或你的内核厂商。

4.5 Linux大页面

当PostgreSQL使用大量连续的内存块时,使用大页面会减少开销, 特别是在使用大shared_buffers时。 要在PostgreSQL中使用此特性,您需要一个包含 CONFIG_HUGETLBFS=yCONFIG_HUGETLB_PAGE=y的内核。 您还必须调整内核设置vm.nr_hugepages。要估计所需的巨大页面的数量, 请启动PostgreSQL,而不启用巨大页面,并使用 /proc文件系统来检查postmaster的匿名共享内存段大小以及系统的巨大页面大小。 这可能看起来像:

$ head -1 $PGDATA/postmaster.pid
4170
$ pmap 4170 | awk '/rw-s/ && /zero/ {print $2}'
6490428K
$ grep ^Hugepagesize /proc/meminfo
Hugepagesize:       2048 kB

6490428 / 2048 大约是3169.154,因此在这个示例中你至少需要 3170个大页面,我们可以设置:

$ sysctl -w vm.nr_hugepages=3170

如果机器上的其他程序也需要大页面,则更大的设置将是合适的。 不要忘记将此设置添加到/etc/sysctl.conf, 以便在重启后重新应用它。

有时候内核会无法立即分配想要数量的大页面,所以可能有必要重复该命令或者重新启动。 (在重新启动之后,应立即将大部分机器的内存转换为大页面。) 要验证巨大的页面分配情况,请使用:

$ grep Huge /proc/meminfo

可能还需要赋予数据库服务器的操作系统用户权限,让他能通过sysctl 设置vm.hugetlb_shm_group以使用大页面, 和/或赋予使用ulimit -l锁定内存的权限。

PostgreSQL中大页面的默认行为是 尽可能使用它们并且在失败时转回到正常页面。要强制使用大页面,你可 以在postgresql.conf中把huge_pages设置成 on。注意此设置下如果没有足够的大页面可用, PostgreSQL将会启动失败。

Linux大页面特性的详细描述可见https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt.

五、关闭服务器

有几种关闭数据库服务器的方法。通过给postgres进程发送不同的信号,你就可以控制关闭类型。

SIGTERM

这是智能关闭模式。在接收SIGTERM后, 服务器将不允许新连接,但是会让现有的会话正常结束它们的工作。仅当所有的会话终止后它才关闭。 如果服务器处在线备份模式,它将等待直到在线备份模式不再被激活。当在线备份模式被激活时, 仍然允许新的连接,但是只能是超级用户的连接(这一例外允许超级用户连接来终止在线备份模式)。 如果服务器在恢复时请求智能关闭,恢复和流复制只有在所有正常会话都终止后才停止。

SIGINT

这是快速关闭模式。服务器不再允许新的连接,并向所有现有服务器进程发送SIGTERM,让它们中断当前事务并立刻退出。然后服务器等待所有服务器进程退出并最终关闭。 如果服务处于在线备份模式,备份模式将被终止并致使备份无用。

SIGQUIT

这是立即关闭模式。服务器将给所有子进程发送 SIGQUIT并且等待它们终止。如果有任何进程没有在 5 秒内终止,它们将被发送 SIGKILL。主服务器进程将在所有子进程退出之后立刻退出,而无需做普通的数据库关闭处理。这将导致在下一次启动时(通过重放 WAL 日志)恢复。只在紧急 时才推荐这种方式。

pg_ctl程序提供了一个发送这些信号关闭服务器的方便的接口。 另外,你在非 Windows 系统上可以用kill直接发送这些信号。可以用ps程序或者从数据目录的postmaster.pid文件中找到postgres进程的PID。例如,要做一次快速关闭:

$ kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`

重要:

最好不要使用SIGKILL关闭服务器。 这样做将会阻止服务器释放共享内存和信号量,那么在开始一个新的服务器之前,可能需要手动完成这些释放。 此外,使用SIGKILL杀掉postgres进程时,postgres不会有机会将信号传播到它的子进程,所以也必须手工杀掉单个的子进程。

要终止单个会话同时允许其他会话继续,使用pg_terminate_backend() 或发送SIGTERM信号到该会话相关的子进程。

六、升级一个PostgreSQL集簇

本节讨论如何把你的数据库数据从一个PostgreSQL发行升级到一个更新的发行。

当前PostgreSQL版本号由主要版本号和次要版本号组成。 例如,在版本号10.1中,10是主要版本号,1是次要版本号,这意味着这将是主版本10的第一个次要版本。 对于PostgreSQL版本10.0之前的版本,版本号由三个数字组成,例如9.5.3。 在这些情况下,主要版本由版本号的前两个数字组(例如9.5)组成,次要版本是第三个数字, 例如3,这意味着这将是主要版本9.5的第三次要版本。

次要发行从来不改变内部存储格式并且总是向前并向后兼容同一主版本号中的次要发行。 例如版本10.1与版本10.0和版本10.6兼容。类似的,例如9.5.3与9.5.0、9.5.1和9.5.6兼容。 要在兼容的版本间升级,你只需要简单地在服务器关闭时替换可执行文件并重启服务器。 数据目录则保持不变 --- 次要升级就这么简单。

对于PostgreSQL的 发行, 内部数据存储格式常被改变,这使升级复杂化。传统的把数据移动到 新主版本的方法是先转储然后重新载入到数据库,不过这可能会很慢。 一种更快的方式是pg_update。如下文所讨论的, 复制方法也能被用于升级。

新的主版本也通常会引入一些用户可见的不兼容性,因此可能需要应用程序编程上的改变。所有用户可见的更改都被列在发行注记中,请特别注意标有 "Migration" 的小节。如果你正在跨越几个主版本升级,一定要阅读每个中间版本的发行注记。

小心的用户在完全切换过去之前将希望在新版本上测试他们的客户端应用。因此,建立一个新旧版本的并存安装通常是一个好主意。在测试一个PostgreSQL主要升级时,考虑下列可能的改变类别:

管理

用于管理员监控和控制服务器的功能在每一个主发行中经常会改变和增加。

SQL

通常这包括新的 SQL 命令功能并且在行为上没有更改,除非在发行注记中有特别提到。

库 API

通常libpq等库值增加新功能,除非在发行注记中有特别提到。

系统目录

系统目录改变通常只影响数据库管理工具。

服务器 C-语言 API

这涉及到后端函数 API 中的改变,它使用 C 编程语言编写。这些改变影响引用服务器内部后端函数的代码。

6.1 通过pg_update升级数据

一种升级方法是从PostgreSQL的一个主版本转储数据并将它重新载入到另一个主版本中 --- 要这样做,你必须使用pg_dumpall这样的逻辑备份工具,文件系统级别的备份方法将不会有用(这也阻止你在一个不兼容版本的PostgreSQL中使用一个数据目录,因此在一个数据目录上尝试启动一个错误的服务器版本不会造成很大的危害)。

我们推荐你从较新版本的PostgreSQL中使用pg_dump和pg_dumpall程序,这样可以利用在这些程序中可能存在的改进。当前发行的转储程序可以读取任何 7.0 以上版本服务器中的数据。

这些指令假定你现有的安装位于/usr/local/pgsql目录,并且数据区域在/usr/local/pgsql/data。请用你的路径进行适当的替换。

  1. 如果在创建一个备份,确认你的数据库没有在被更新。这不会影响备份的完整性,但是那些更改当然不会被包括在备份中。如果必要,编辑/usr/local/pgsql/data/pg_hba.conf文件中的权限(或等效的方法)来不允许除你之外的任何人使用数据库。

    要备份你的数据库安装,键入:

    pg_dumpall > outputfile

要制作备份,你可以使用你正在运行版本的pg_dumpall命令。但是,要得到最好的结果,试试使用PostgreSQL 11.2 的pg_dumpall命令,因为这个版本包含了对旧版本的缺陷修复和改进。虽然这个建议可能看起来很奇怪,因为你还没有安装新版本,但如果你计划平行地安装新版本,遵循这个建议是很明智的。在这种情况下,你可以正常完成安装并且稍后再来传输数据。这也将减少停机时间。

2.关闭旧服务器:

pg_ctl stop

在那些自动启动PostgreSQL的系统上,可能有一个启动文件将完成同样的事情。例如,在一个Red Hat Linux系统中,我们会发现这也能用:

/etc/rc.d/init.d/postgresql stop

3.如果从备份恢复,重命名或删除旧的安装目录(如果它不是针对特定版本的)。重命名该目录是一个好主意,而不是删除它,因为如果你碰到问题并需要返回到它,它还存在。记住该目录可能消耗可观的磁盘空间。要重命名该目录,使用类似的命令:

mv /usr/local/pgsql /usr/local/pgsql.old

(注意将该目录作为一个单一单元移动,这样相对路径可以保持不变)。

4.安装新版本的PostgreSQL

5.如果需要,创建一个新的数据库集簇。记住你必须在登录到一个特殊的数据库用户账户(如果你在升级,你就已经有了这个账户)时执行这些命令。

/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data

6.恢复你之前的pg_hba.conf以及任何postgresql.conf修改。

7.启动数据库服务器,也要使用特殊的数据库用户账户:

复制代码
`/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data`   8.最后,使用新的 psql从备份恢复你的数据
复制代码
`/usr/local/pgsql/bin/psql -d postgres -f ``outputfile`通过在一个不同的目录中安装新的服务器并且并行地在不同的端口运行新旧两个服务器可以达到最低的停机时间。那么你可以这样用:
pg_dumpall -p 5432 | psql -d postgres -p 5433

来转移你的数据。

6.2 通过pg_upgrade升级数据

pg_upgrade模块允许一个安装从一个 PostgreSQL主版本"就地"升级成另一个主版本。 升级可以在数分钟内被执行,特别是使用--link模式时。它 要求和上面的pg_dumpall相似的步骤,例如启动/停止 服务器、运行initdb。pg_upgrade 文档概述了所需的步骤。

6.3 通过复制升级数据

也可以用PostgreSQL的已更新版本逻辑复制来创建一个~ 后备服务器,逻辑复制支持在不同主版本的PostgreSQL之间~ 的复制。后备服务器可以在同一台计算机或者不同的计算机上。一旦它和主服务器(运行旧版本的PostgreSQL)同步好,你可以切换主机并且将后备服~ 务器作为主机,然后关闭旧的数据库实例。这样一种切换使得一次升级的停机时间只有数秒。

这种升级方法可以用内置的逻辑复制工具和外部的逻辑复制系统如pglogical,Slony,Londiste,和Bucardo。

七、阻止服务器欺骗

服务器在运行时,它不可能让恶意用户取代正常的数据库服务器。然而,当服务器关闭时, 一个本地用户可以通过启动它们自己的服务器来欺骗正常的服务器。行骗的服务器可以读取客户端发送的密码和查询语句, 但是不会返回任何数据,因为PGDATA这个目录是安全的(它有目录权限)。 欺骗是可能的,因为任何用户都可以启动一个数据库服务器;客户端无法识别一个无效的服务器,除非它被专门配置。

一种阻止local连接欺骗的方法是使用一个 Unix 域套接字目录 (unix_socket_directories),该目录只对一个被信任的本地用户有写权限。 这可以防止恶意用户在该目录中创建自己的套接字文件。如果你担心有些应用程序可能仍然引用/tmp下的套接字文件并且因此容易受到欺骗,可在操作系统启动时创建一个符号链接/tmp/.s.PGSQL.5432指向一个被重定位的套接字文件。你也可能需要修改/tmp清除脚本防止删除这个符号链接。

local连接的另一个选项是对客户端使用requirepeer指定所需的连接到该套接字的服务器进程的拥有者。

要在TCP连接上防止欺骗,最好的解决方案是使用 SSL 证书,并且确保客户检查服务器的证书。 要做到这点,服务器必须配置为仅接受hostssl连接,并且有 SSL 密钥和证书文件。 TCP 客户端连接必须使用sslmode=verify-caverify-full进行连接,并且安装有适当的根证书文件。

八、加密选项

PostgreSQL提供了几个不同级别的加密, 并且在保护数据不会因为数据库服务器偷窃、不道德的管理员、不安全网络等因素而泄漏方面 提供很高的灵活性。加密可能也是保护一些诸如医疗记录或财务交易等敏感数据所要求的。

口令加密

数据库用户的口令都是以哈希(取决于password_encryption配置)的方式存储, 所以管理员不能限定实际的口令赋予用户。如果 SCRAM 或者 MD5 加密算法被用于客户端认证, 那么未加密的口令甚至都不可能出现在服务器上,因为客户端在通过网络发送口令之前,就已经加密过。推荐使用SCRAM,因为它是互联网标准而且相比于PostgreSQL特定的MD5认证协议更安全。

指定列加密

pgcrypto模块允许对特定域进行加密存储。这个功能只对某些敏感数据有用。 客户端提供解密的密钥,然后数据在服务器端解密并发送给客户端。

在数据被解密和在服务器与客户端之间传递时,解密数据和解密密钥将会在服务器端存在短暂的一段时间。 这就给那些能完全访问数据库服务器的人提供了一个短暂的截获密钥和数据的时间,例如系统管理员。

数据分区加密

存储加密可以在文件系统层面或者块层面上执行。Linux 文件系统加密 选项包括 eCryptfs 和 EncFS,而 FreeBSD 使用 PEFS。快层面或者全 盘加密选项包括 Linux 上的 dm-crypt + LUKS 以及 FreeBSD 上的 GEOM 模块 geli 及 gbde。很多其他操作系统也支持这个功能,包括 Windows。

这个机制避免了在整个计算机或者驱动器被盗的情况下, 未加密的数据被从驱动器中读取。它无法防止在文件系统被挂 载时的攻击,因为在挂载之后,操作系统提供数据的解密视图。不过,要想挂载该文件系统,你需要有一些方法把加密密钥传递给操作 系统,并且有时候这个密钥就存储在挂载该磁盘的主机上的某处。

跨网络加密数据

SSL 连接加密所有跨网络发送的数据:口令、查询以及返回的数据。pg_hba.conf文件允许管理员指定哪些主机可以使用 非加密连接(host),以及哪些主机需要使用 SSL 加密的连接(hostssl)。客户端还可以指定它们只通过 SSL 连接到服务器。我们还可以使用Stunnel或SSH加密传输。

SSL 主机认证

客户端和主机都可以提供 SSL 证书给对方。这在两边都需要一些额外的配置, 但是这种方式提供了比仅使用口令更强的身份验证。 它避免一个计算机伪装成服务器,这个时长只要足够读取客户端发送的口令就行了。它还避免了 "中间人"攻击,在其中有一台计算机处于客户端和服务器之间并伪装成服务器读取和传递两者之间的所有数据。

客户端加密

如果服务器所在机器的系统管理员是不可信的,那么客户端加密数据也是必要的。在这种情况下,未加密的数据从来不会在数据库服务器上出现。数据在发送给服务器之前加密,而数据库结果在能使用之前必须在客户端上解密。

九、用SSL进行安全的TCP/IP连接

PostgreSQL 有一个对使用 SSL 连接加密客户端/服务器通讯的本地支持,它可以增加安全性。这个特性要求在客户端和服务器端都安装 OpenSSL 并且在编译 PostgreSQL 的时候打开这个支持。

9.1. Basic Setup

当SSL支持被编译在PostgreSQL中时,可以通过将postgresql.conf中的 ssl设置为on让PostgreSQL服务器带着SSL支持被启动。 服务器在同一个 TCP 端口监听普通连接和SSL连接,并且将与任何正在连接的客户端协商是否使用SSL。默认情况下,这是客户端的选项,关于如何设置服务器来要求某些或者所有连接使用SSL请见后续章节。

要SSL模式中启动服务器,包含服务器证书和私钥的文件必须存在。默认情况下,这些文件应该分别被命名为server.crtserver.key并且被放在服务器的数据目录中,但是可以通过配置参数ssl_cert_file和ssl_key_file指定其他名称和位置。

在 Unix 系统上,server.key上的权限必须不允许所有人或组的任何访问,通过命令chmod 0600 server.key可以做到。或者,该文件可以由 root 所拥有并且具有组读访问(也就是0640权限)。这种设置适用于由操作系统管理证书和密钥文件的安装。用于运行PostgreSQL服务器的用户应该被作为能够访问那些证书和密钥文件的组成员。

如果数据目录允许组读取访问,则证书文件可能需要位于数据目录之外,以符合上面概述的安全要求。通常,启用组访问权限是为了允许非特权用户备份数据库,在这种情况下,备份软件将无法读取证书文件,并且可能会出错。

如果私钥被一个密码保护着,服务器将提示要求这个密码,并且在它被输入前不会启动。 使用密码还会禁用在不重启服务器的情况下更改服务器的SSL配置的功能。 此外,密码保护的私钥在Windows上根本无法使用。

server.crt中的第一个证书必须是服务器的证书,因为它必须与服务器的私钥匹配。"intermediate"的证书颁发机构,也可以追加到文件。假设根证书和中间证书是使用v3_ca扩展名创建的,那么这样做避免了在客户端上存储中间证书的必要。这使得中间证书更容易到期。

无需将根证书添加到中server.crt。相反,客户端必须具有服务器证书链的根证书。

9.2 OpenSSL配置

PostgreSQL读取系统范围的OpenSSL配置文件。默认情况下,该文件被命名为openssl.cnf并位于openssl version -d报告的目录中。通过将环境变量设置OPENSSL_CONF为所需配置文件的名称,可以覆盖此默认值。

OpenSSL支持各种强度不同的密码和身份验证算法。虽然许多密码可以在OpenSSL的配置文件中被指定,您可以通过修改postgresql.conf配置文件中指定专门针对数据库服务器使用密码的 ssl_ciphers配置。

注意:

使用NULL-SHANULL-MD5可以得到身份验~ 证但没有加密开销。不过,中间人能够读取和传递客户端和服务器之间的通信。此外,加~ 密开销相比身份认证的开销是最小的。出于这些原因,我们建议不要使用 NULL 密码。

9.3 使用客户端证书

要求客户端提供受信任的证书,把你信任的根证书颁发机构(CA)的证书放置在数据目录文件中。并且修改postgresql.conf中的参数ssl_ca_file到新的文件名,还要把认证选项clientcert=1加入到pg_hba.conf文件中合适的hostssl行上。然后将在 SSL 连接启动时从客户端请求该证书(一段对于如何在客户端设置证书的描述请见后续章节)。服务器将验证客户端的证书是由受信任的证书颁发机构之一签名。

如果希望避免将链接到现有根证书的中间证书显示在ssl_ca_file文件中(假设根证书和中间证书是使用 v3_ca 扩展名创建的),则这些证书也可以显示在ssl_ca_file 文件中。如果参数ssl_ca_file被设置,证书撤销列表(CRL)项也要被检查(显示 SSL 证书用法的图标见Certificates for SSL Applications)。

clientcert认证选项适用于所有的认证方法,但仅适用于pg_hba.conf中用hostssl指定的行。 当clientcert没有指定或设置为 0时,如果配置了 CA 文件,服务器将仍然会根据它验证任何提交的客户端证书 --- 但是它将不会坚持要求出示一个客户端证书。

如果你在设置客户端证书,你可能希望用cert认证方法,这样证书控制用户认证以及提供连接安全。(在使用cert认证方法时,没有必要显式地指定clientcert=1)。

9.4. SSL 服务器文件用法

表9.1总结了与服务器上 SSL 配置有关的文件(显示的文件名是默认的名称。本地配置的名称可能会不同)。

表 9.1. SSL 服务器文件用法

文件 内容 效果
ssl_cert_file ($PGDATA/server.crt) 服务器证书 发送给客户端来说明服务器的身份
ssl_key_file ($PGDATA/server.key) 服务器私钥 证明服务器证书是其所有者发送的,并不说明证书所有者是值得信任的
ssl_ca_file 可信的证书颁发机构 检查客户端证书是由一个可信的证书颁发机构签名的
ssl_crl_file 被证书授权机构撤销的证书 客户端证书不能出现在这个列表上

服务器在服务器启动时以及服务器配置重新加载时读取这些文件。在Windows系统上,只要为新客户端连接生成新的后端进程,它们也会重新读取。

如果在服务器启动时检测到这些文件中的错误,服务器将拒绝启动。但是,如果在配置重新加载过程中检测到错误,则会忽略这些文件,并继续使用旧的SSL配置。在Windows系统上,如果在后端启动时检测到这些文件中存在错误,则该后端将无法建立SSL连接。在所有这些情况下,错误情况都会在服务器日志中报告。

9.5 创建证书

要为服务器创建一个有效期为365天的简单自签名证书,可以使用下面的OpenSSL命令,将*dbhost.yourdomain.com*替换为服务器的主机名:

openssl req -new -x509 -days 365 -nodes -text -out server.crt \
  -keyout server.key -subj "/CN=dbhost.yourdomain.com"

然后执行:

chmod og-rwx server.key

如果文件的权限比这个更自由,服务器将拒绝该文件。要了解更多关于如何创建你的服务器私钥和证书的细节, 请参考OpenSSL文档。

尽管可以使用自签名证书进行测试,但是在生产中应该使用由证书颁发机构(CA)(通常是企业范围的根CA)签名的证书。

要创建其身份可以被客户端验证的服务器证书,请首先创建一个证书签名请求(CSR)和一个公共/专用密钥文件:

openssl req -new -nodes -text -out root.csr \
  -keyout root.key -subj "/CN=root.yourdomain.com"
chmod og-rwx root.key

然后,使用密钥对请求进行签名以创建根证书颁发机构(使用Linux上的默认OpenSSL配置文件位置):

openssl x509 -req -in root.csr -text -days 3650 \
  -extfile /etc/ssl/openssl.cnf -extensions v3_ca \
  -signkey root.key -out root.crt

最后,创建由新的根证书颁发机构签名的服务器证书:

openssl req -new -nodes -text -out server.csr \
  -keyout server.key -subj "/CN=dbhost.yourdomain.com"
chmod og-rwx server.key

openssl x509 -req -in server.csr -text -days 365 \
  -CA root.crt -CAkey root.key -CAcreateserial \
  -out server.crt

server.crtserver.key应该存储在服务器上,并且root.crt应该存储在客户端上,以便客户端可以验证服务器的叶证书已由其受信任的根证书签名。root.key应该离线存储以用于创建将来的证书。

也可以创建一个包括中间证书的信任链:

# root 
openssl req -new -nodes -text -out root.csr \
  -keyout root.key -subj "/CN=root.yourdomain.com"
chmod og-rwx root.key
openssl x509 -req -in root.csr -text -days 3650 \
  -extfile /etc/ssl/openssl.cnf -extensions v3_ca \
  -signkey root.key -out root.crt

# intermediate 
openssl req -new -nodes -text -out intermediate.csr \
  -keyout intermediate.key -subj "/CN=intermediate.yourdomain.com"
chmod og-rwx intermediate.key
openssl x509 -req -in intermediate.csr -text -days 1825 \
  -extfile /etc/ssl/openssl.cnf -extensions v3_ca \
  -CA root.crt -CAkey root.key -CAcreateserial \
  -out intermediate.crt

# leaf 
openssl req -new -nodes -text -out server.csr \
  -keyout server.key -subj "/CN=dbhost.yourdomain.com"
chmod og-rwx server.key
openssl x509 -req -in server.csr -text -days 365 \
  -CA intermediate.crt -CAkey intermediate.key -CAcreateserial \
  -out server.crt

server.crtintermediate.crt应连接成一个证书文件包中并存储在服务器上。server.key还应该存储在服务器上。root.crt应将其存储在客户端上,以便客户端可以验证服务器的叶证书是否已由链接到其受信任根证书的证书链签名。root.keyintermediate.key应离线存储以用于创建将来的证书。

十、使用SSH隧道的安全TCP/IP连接

可以使用SSH来加密客户端和PostgreSQL服务器之间的网络连接。如果处理得当,这将提供一个足够安全的网络连接,即使是对那些无 SSL 能力的客户端。

首先确认在PostgreSQL服务器的同一台机器上正确运行着一个SSH服务器,并且你可以使用ssh作为某个用户登入。然后你可以从客户端机器采用下面这种形式的命令建立一个安全的隧道:

ssh -L 63333:localhost:5432 joe@foo.com

-L参数中的第一个数(63333)是隧道在你那一端的端口号,它可以是任意未用过的端口(IANA 把端口 49152 到 65535 保留为个人使用)。第二个数(5432)是隧道的远端:你的服务器所使用的端口号。在端口号之间的名字或 IP 地址是你准备连接的数据库服务器的主机,至于你是从哪个主机登入的,在这个例子中则由foo.com表示。为了使用这个隧道连接到数据库服务器,你在本地机器上连接到端口 63333:

psql -h localhost -p 63333 postgres

对于数据库服务器,在这个环境中它将把你看做是连接到localhost的主机foo.com上的真实用户joe,并且它会使用被配置用于来自这个用户和主机的连接的认证过程。注意服务器将不会认为连接是 SSL 加密的,因为事实上SSH服务器和PostgreSQL服务器之间没有加密。只要它们在同一台机器上,这就不会造成任何额外的安全风险。

为了让隧道设置成功,你必须允许通过ssh作为joe@foo.com连接,就像你已经尝试使用ssh来创建一个终端会话。

你应当也已经设定好了端口转发:

ssh -L 63333:foo.com:5432 joe@foo.com

但是数据库服务器则将会看到连接从它的foo.com接口进来,它没有被默认设置listen_addresses = 'localhost'所打开。这通常不是你想要的。

如果你必须通过某个登录主机"跳"到数据库服务器,一个可能的设置看起来像:

ssh -L 63333:db.foo.com:5432 joe@shell.foo.com

注意这种从shell.foo.comdb.foo.com的连接的方法将不会被 SSH 隧道加密。当网络被限制于各种方法时,SSH 提供了相当多的配置可能性。详情请参考 SSH 的文档。

提示:

一些其他的应用可以提供安全隧道,它们使用和刚刚描述的 SSH 概念上相似的过程。

十一、在Windows上注册事件日志

要为操作系统注册一个Windows 事件日志库,发出这个命令:

regsvr32 pgsql_library_directory/pgevent.dll

这会创建被事件查看器使用的注册表项,默认事件源命名为PostgreSQL

要指定一个不同的事件源名称。使用/n/i选项:

regsvr32 /n /i:event_source_name pgsql_library_directory/pgevent.dll

要从操作系统反注册事件日志库,发出这个命令:

regsvr32 /u [/i:event_source_name] pgsql_library_directory/pgevent.dll

注意:

要启用数据库服务器中的事件日志,在postgresql.conf中修改log_destination来包括eventlog

相关推荐
Hacker_LaoYi16 分钟前
SQL注入的那些面试题总结
数据库·sql
Hacker_LaoYi2 小时前
【渗透技术总结】SQL手工注入总结
数据库·sql
独行soc2 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)
数据库·sql·安全·web安全·漏洞挖掘·hw
远游客07133 小时前
centos stream 8下载安装遇到的坑
linux·服务器·centos
独行soc4 小时前
#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍08-基于时间延迟的SQL注入(Time-Based SQL Injection)
数据库·sql·安全·渗透测试·漏洞挖掘
清平乐的技术专栏5 小时前
Hive SQL 查询所有函数
hive·hadoop·sql
LIKEYYLL5 小时前
GNU Octave:特性、使用案例、工具箱、环境与界面
服务器·gnu
云云3215 小时前
搭建云手机平台的技术要求?
服务器·线性代数·安全·智能手机·矩阵
云云3216 小时前
云手机有哪些用途?云手机选择推荐
服务器·线性代数·安全·智能手机·矩阵
CircleMouse6 小时前
Centos7, 使用yum工具,出现 Could not resolve host: mirrorlist.centos.org
linux·运维·服务器·centos