本章討論如何設置和運行數據庫服務器,以及它與操作系統的交互。
一、PostgreSQL用戶賬戶
和對外部世界可訪問的任何服務器守護進程一樣,我們也建議在一個獨立的用戶賬戶下運行PostgreSQL。這個用戶賬戶應該只擁有被該服務器管理的數據,并且應該不能被其他守護進程共享(例如,使用用戶nobody
是一個壞主意)。我們不建議把可執行文件安裝為屬于這個用戶,因為妥協系統可能接著修改它們自己的二進制文件。
要在你的系統中增加一個 Unix 用戶賬戶,查看一個命令useradd
或adduser
。通常會用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 創建的模板數據庫中使用的順序不能更改(除非刪除并重建它們)。使用非C
或POSIX
的區域還會對性能造成影響。因此,第一次就正確地選擇很重要。
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.d
或rc.d
目錄。不管你做什么,服務器必須由PostgreSQL用戶賬戶而不是 root或任何其他用戶啟動。因此你可能應該在你的命令中使用su postgres -c '...'
這種形式。例如:
su postgres -c 'pg_ctl start -D /usr/local/pgsql/data -l serverlog'
下面是一些更加與操作系統相關的建議(在每一種情況中要確保在我們展示通用值的地方使用正確的安裝目錄和用戶名)。
if [ -x /usr/local/pgsql/bin/pg_ctl -a -x /usr/local/pgsql/bin/postgres ]; thensu -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 禁用超時邏輯。
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 refusedIs the server running on host "server.joe.com" and acceptingTCP/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 directoryIs the server running locally and acceptingconnections on Unix domain socket "/tmp/.s.PGSQL.5432"?
最后一行可以驗證客戶端是不是嘗試連接到正確的位置。如果實際上沒有服務器在那里運行,典型的核心錯誤消息將是Connection refused
或No such file or directory
(值得注意的是這種環境中的Connection refused
并不表示服務器得到了你的連接請求并拒絕了它。那種情況會產生一個不同的消息)。其它像Connection timed out
這樣的消息可能表示更基礎的問題,如缺少網絡連接。
四、管理內核資源
PostgreSQL某些時候會耗盡操作系統的各種資源限制,當同一個系統上運行著多個拷貝的服務器或在一個非常大的安裝中時尤其如此。本節解釋了PostgreSQL使用的內核資源以及你可以采取的用于解決內核資源消耗相關問題的步驟。
4.1.?共享內存和信號量
完全缺少這些功能通常表現為服務器啟動時的“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_connections
加autovacuum_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”有關的其他各種設置,如SEMMNU
和SEMUME
?不會影響PostgreSQL。
當使用POSIX信號量時,所需的信號量數量與System V相同, 即每個允許的連接(max_connectionsmax_connectionsmax_connections)、允許的自動清理工作進程 (autovacuum_max_workersautovacuum_max_workersautovacuum_max_workers)和允許的后臺進程 (max_worker_processesmax_worker_processesmax_worker_processes)一個信號量。 在首選此選項的平臺上,POSIX信號量的數量沒有特定的內核限制。
????????至少到版本 5.1 為止,不再需要對這些參數(例如SHMMAX
)做任何特殊的配置,這看起來就像是被配置成允許所有內存都被用作共享內存。這是一種通常被用于其他數據庫(DB/2)的配置。
????????但是,可能需要修改/etc/security/limits
中的全局ulimit
信息,默認的文件尺寸硬限制(fsize
)和文件數量(nofiles
)可能太低。
????????可以使用sysctl
或loader
接口來改變默認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?5.0 及其后的版本中,IPC 參數可以使用sysctl
調整。例如:
$ sysctl -w kern.ipc.semmni=100
????????要使這些設置在重啟后保持,請修改/etc/sysctl.conf
。
????????作為NetBSD的默認設置,你總是會想 調大kern.ipc.semmni
和kern.ipc.semmns
的值,因為他們實在太小了。
????????你可能也希望你的內核將共享內存鎖定在 RAM 中并且防止它被換頁到交換分區。這可以使用sysctl
的設置?kern.ipc.shm_use_phys
來完成。
????????NetBSD?5.0 以前的版本的工作與舊版OpenBSD相似(見下文),除了那些內核參數應該用關鍵詞options
設置而不是option
。
????????在OpenBSD3.3及以后版本,使用sysctl
命令,IPC參數可以被自動調節,例如:
# sysctl kern.seminfo.semmni=100
????????要使這些設置在重啟后保持,請修改/etc/sysctl.conf
。
????????作為OpenBSD的默認配置,你總是會想調大kern.seminfo.semmni
和kern.seminfo.semmns
的值,因為他們實在太小了。
????????在較早的OpenBSD?版本中,你需要編譯定制化內核來修改這些IPC參數。也要確保SYSVSHM
和SYSVSEM
選項為啟用狀態。(這兩項默認都是啟用狀態。) 下面給出一些內核配置文件中如何設置這些參數的例子:
option SYSVSHM
option SHMMAXPGS=4096
option SHMSEG=256option SYSVSEM
option SEMMNI=256
option SEMMNS=512
option SEMMNU=256
????????默認的設置可以滿足正常的安裝。在HP-UX?10 上,SEMMNS
的出廠默認值是 128,這可能對大型數據庫站點太低。
????????IPC參數可以在Kernel Configuration?→?Configurable Parameters下的System Administration Manager(SAM)中被設置。當你完成時選擇Create A New Kernel。
????????默認的最大段尺寸是 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 中配置共享內存的推薦方法是創建一個名為/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=256set 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,你可能也需要提升該區的資源使用限制。更多關于projects
?和prctl
的信息請見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 -r
、?adduser --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
文件控制在登錄期間設置的各種資源限制。詳見操作系統文檔。相關的參數是maxproc
、openfiles
和datasize
。例如:
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=y
和CONFIG_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,讓它們中斷當前事務并立刻退出。然后服務器等待所有服務器進程退出并最終關閉。 如果服務處于在線備份模式,備份模式將被終止并致使備份無用。
????????這是立即關閉模式。服務器將給所有子進程發送?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
。請用你的路徑進行適當的替換。
-
如果在創建一個備份,確認你的數據庫沒有在被更新。這不會影響備份的完整性,但是那些更改當然不會被包括在備份中。如果必要,編輯
/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-ca
或verify-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.crt
和server.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-SHA
或NULL-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.keyopenssl x509 -req -in server.csr -text -days 365 \-CA root.crt -CAkey root.key -CAcreateserial \-out server.crt
server.crt
和server.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.crt
和intermediate.crt
應連接成一個證書文件包中并存儲在服務器上。server.key
還應該存儲在服務器上。root.crt
應將其存儲在客戶端上,以便客戶端可以驗證服務器的葉證書是否已由鏈接到其受信任根證書的證書鏈簽名。root.key
和intermediate.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.com
到db.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
。