TCP KEEPALIVE FOR A BETTER POSTGRESQL EXPERIENCE【譯】
如果您聽說過 TCP keepalive 但不確定那是什么,請繼續閱讀。如果您曾經對以下錯誤消息感到驚訝:
server closed the connection unexpectedly
SSL SYSCALL error: EOF detected
unexpected EOF on client connection
could not receive data from client: Connection reset by peer
那么這篇文章會有用
連接斷開的原因
連接斷開有幾個可能的原因:
數據庫服務器崩潰
上面列表中的前兩條消息可能是因為 PostgreSQL 服務器的問題。如果服務器因任何原因崩潰,您將看到類似的消息。要調查是否存在服務器問題,您應該首先查看 PostgreSQL 日志,看看是否可以找到匹配的崩潰報告。
我們不會在下面討論這種情況,因為它不是網絡問題。
客戶端放棄的連接
如果客戶端在沒有正確關閉數據庫連接的情況下退出,當用網絡socket通信時,服務器會收到文件結束或錯誤。使用v14 中引入的新會話統計信息,您可以在pg_stat_database.sessions_abandoned
字段中看到數據庫斷開連接的信息。
例如,如果應用程序服務器出現故障並重新啟動,它通常不會關閉與數據庫服務器的連接。這並不奇怪,當服務器試圖向客戶端發送數據時,數據庫服務器會迅速檢測到它。但是如果數據庫會話處於空閑狀態,則服務器進程正在等待客戶端發送下一條語句(可以看到pg_stat_activity
中的wait_event
“ 是ClientRead
” )。然后服務器不會立即發現客戶端不再存在!這種揮之不去的后端進程占用一個進程槽,並可能導致您超過max_connections
.
PostgreSQL v14 引入了一個新參數idle_session_timeout
,它會在一段時間后關閉空閑連接。但這也會終止“健康”的空閑連接,因此這不是一個很好的解決方案。TCP keepalive 為這個問題提供了更好的解決方案。
網絡組件導致的連接關閉
有時數據庫連接的兩端都會遇到同樣的問題:每一端都看到另一端“掛斷了它們”。在這種情況下,問題出在數據庫客戶端和服務器之間。
如果存在真正的連接問題,網絡連接可能會斷開。您無法在軟件級別上更改它。但很多時候,斷開連接是由防火牆或路由器的配置方式引起的。網絡組件可能必須“記住”每個打開連接的狀態,而用於此的資源是有限的。因此,“忘記”並丟棄已閑置較長時間的連接似乎是權宜之計。
由於當今的許多 TCP 流量都是通過 HTTP 進行的,而且 HTTP 是無狀態的,所以這通常不是問題。如果您的 HTTP 連接中斷,您只需為下一個請求建立一個新連接,這並不昂貴。但數據庫不同:
- 建立數據庫連接的成本很高
- 數據庫連接不是無狀態的;例如,在關閉連接的情況下,您會丟失打開的事務、臨時表和准備好的語句
- 數據庫會話空閑較長時間是正常的,例如,如果您正在使用連接池,或者當客戶端正在等待長時間運行的分析查詢的結果時
這就是 TCP keepalive 派上用場的地方,它可以作為保持空閑連接打開的一種方式。
什么是 TCP keepalive?
Keepalive 是 TCP 協議的一項功能。當您在TCP network socket中使用SO_KEEPALIVE
選項時,一旦socket idle,計時器就會開始運行。當**keepalive idle time* *到期,並且socket上沒有進一步的活動時,kernel內核將向通信伙伴發送一個“keepalive數據包”。如果對方回答,則認為連接良好,計時器再次開始運行。
如果沒有應答,內核等一個keepalive間隔(keepalive interval ),再發送另一個keepalive packet探活。
重復此過程,直到發送的數據包量達到 keepalive count。如果之后(還是無應答),連接就被認為是結束了,再嘗試使用network socket就會報錯。
請注意,發送keepalive消息的是操作系統內核,而不是應用程序(數據庫服務器或客戶端)。應用程序不知道此過程。
TCP keepalive 有兩個目的:
- 防止網絡連接空閑
- 檢測另一個通信端是否在沒有關閉網絡連接的情況下斷開
(“keepalive”這個名字描述得不太好——“detectdead”更重要)。
TCP 保活默認設置
keepalive 參數的默認值因操作系統而異。在 Linux 和 Windows 上,默認值為:
- keepalive 空閑時間:Linux 和 Windows 上均為 2 小時
- keepalive 間隔:Linux 上 75 秒,Windows 上 1 秒
- keepalive 計數:Linux 上為 9,Windows 上為 10(此值在 Windows 上無法更改)
我沒有找到 MacOS 的默認設置。
使用 TCP keepalive 使空閑的數據庫會話保持活動狀態
為了防止防火牆和路由器關閉空閑連接,我們需要一個低得多的 keepalive 空閑時間設置。這樣在連接關閉之前就能發送keepalive數據包。這能騙到網絡組件相信連接不是空閑的,即使數據庫客戶端和服務器都沒有發送任何數據。
在這情況下,keepalive count and keepalive interval無關。我們所需要的只是足夠早地發送第一個keepalive packet。
使用 TCP keepalive 檢測死連接
在這情況下,減少 keepalive 空閑時間通常是不夠的。如果服務器以 75 秒的間隔發送 9 個 keepalive 數據包,則需要 10 多分鍾才能檢測到死連接。因此,我們還將減少保活計數或保活間隔,或兩者兼而有之——如本例所示。
這個難題還缺少一塊:即使操作系統檢測到網絡連接斷開,數據庫服務器也不會注意到,除非它嘗試使用network socket。如果它正在等待來自客戶端的請求,那將立即發生。但是,如果服務器正忙於執行一個長時間運行的 SQL 語句,它不會注意到死連接,直到查詢完成並嘗試將結果發送回客戶端!為了防止這種情況發生,PostgreSQL v14 引入了新參數client_connection_check_interval
,目前僅在 Linux 上支持。設置此參數會導致服務器定期“輪詢”套接字,即使它還沒有要發送的內容。這樣,它可以檢測到關閉的連接並中斷 SQL 語句的執行。
在 PostgreSQL 服務器上設置 TCP keepalive 參數
PostgreSQL 服務器總是設置SO_KEEPALIVE
TCP 套接字以檢測斷開的連接,但默認的兩小時空閑超時非常長。
您可以設置配置參數tcp_keepalives_idle
,tcp_keepalives_interval
以及tcp_keepalives_count
(Windows 不支持最后一個)來更改所有服務器套接字的設置。
這是為所有數據庫連接配置 TCP keepalive 的最便捷方式,無論使用何種客戶端。
在 PostgreSQL 客戶端上設置 TCP keepalive 參數
PostgreSQL 客戶端共享庫libpq
具有連接參數keepalives_idle
,keepalives_interval
並且keepalives_count
(同樣,Windows 不支持后者)在客戶端配置 keepalive。
這些參數可以在 PostgreSQL 連接字符串中與所有與 鏈接的客戶端接口一起使用libpq
,例如,Psycopg 或 PHP。
PostgreSQL JDBC驅動,沒有使用libpq
,只有連接參數tcpKeepAlive
開啟TCP keepalive(默認關閉),沒有參數配置keepalive空閑時間和其他keepalive設置。
在操作系統上設置 TCP keepalive 參數
您可以更改所有 TCP 連接的操作系統默認值,而不是專門為 PostgreSQL 連接配置 keepalive 設置 - 如果您使用的 PostgreSQL 客戶端應用程序不允許您設置 keepalive 連接參數,這可能很有用。
在 Linux 上,這是通過編輯/etc/sysctl.conf
文件來完成的:
# detect dead connections after 70 seconds``net.ipv4.tcp_keepalive_time = 60``net.ipv4.tcp_keepalive_intvl = 5``net.ipv4.tcp_keepalive_probes = 3
要在不重新啟動機器的情況下激活設置,請運行
sysctl -p
在 Windows 上,您可以通過添加以下注冊表項來更改 TCP keepalive 設置:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters\KeepAliveTime``HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters\KeepAliveInterval
如上所述,keepalive 探測的數量沒有設置,硬編碼為 10。注冊表項的類型必須為DWORD
,並且值以毫秒而不是秒為單位。
更改這些鍵后,重新啟動 Windows 以激活它們。
結論
配置 TCP keepalive 可以通過保持空閑數據庫連接打開或通過及時檢測斷開連接來改善您的 PostgreSQL 體驗。您可以在 PostgreSQL 客戶端、服務器或操作系統上配置 keepalive。
除了配置 keepalive 之外,設置新參數client_connection_check_interval
以在客戶端放棄會話時取消長時間運行的查詢。
【原文】
https://www.cybertec-postgresql.com/en/tcp-keepalive-for-a-better-postgresql-experience/
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
我的環境上默認tcp_keepalives_idle=60,tcp_keepalives_interval=10,tcp_keepalives_count=10
即60s+10*10=160s之后會斷開連接
報錯could not receive data from client: Connection reset by peer,嘗試加大tcp_keepalives_idle