原文地址: https://www.tony-yin.site/201...
本文整理了一些筆者遇到的postgresql
和pgpool
的常見問題和解決方案。php
Postgresql
做爲數據後端,pgpool
做爲postgresql
的中間件,經過vip
對客戶端提供服務,並利用自身的failover
機制保證數據庫HA
。html
Nodes:node
192.168.1.1 192.168.1.2 192.168.1.3
Vip:python
192.168.1.4
Version:web
Postgresql: 9.4.20 Pgpool: 4.0.3
[root@host1 ~]# psql -h 192.168.1.4 -p 9998 -U postgres postgres -c "show pool_nodes" Password for user postgres: node_id | hostname | port | status | lb_weight | role | select_cnt | load_balance_n ode | replication_delay | last_status_change ---------+---------------+------+--------+-----------+---------+------------+--------------- ----+-------------------+--------------------- 0 | 192.168.1.1 | 5432 | up | 0.333333 | standby | 0 | false | 0 | 2019-10-23 16:14:42 1 | 192.168.1.2 | 5432 | up | 0.333333 | primary | 34333174 | true | 0 | 2019-10-23 16:14:42 2 | 192.168.1.3 | 5432 | up | 0.333333 | standby | 0 | false | 0 | 2019-10-23 16:14:42 (3 rows)
[root@host1]# pcp_dettach_node -n 0 -p 9898 -h 192.168.1.1 -U postgres
[root@host1]# pcp_attach_node -n 0 -p 9898 -h 192.168.1.1 -U postgres
[root@host1]# pcp_watchdog_info -h 192.168.1.1 -p 9898 -U postgres Password: 3 YES 192.168.1.1:9998 Linux host16 192.168.1.1 192.168.1.1:9998 Linux host16 192.168.1.1 9998 9000 4 MASTER 192.168.1.2:9998 Linux host17 192.168.1.2 9998 9000 7 STANDBY 192.168.1.3:9998 Linux host18 192.168.1.3 9998 9000 7 STANDBY
[root@host1]# psql -h 192.168.1.1 -p 5432 -U postgres postgres -c "select pg_is_in_recovery()" Password for user postgres: pg_is_in_recovery ------------------- f (1 row)
這個命令並不會真正的將postgresql
後端從standby
改成master
,而只是修改了pgpool
的內部狀態;簡而言之只是修改了pgpool
的狀態,而postgresql
對應的recovery
文件並無改變,仍是須要failover
腳原本改變。sql
pcp_promote_node -n 0 -p 9898 -h 192.168.1.1 -U postgres
一套環境上部署的數據庫常常會發生腦裂問題,後經定位發現是pgpool
配置文件涉及other_pgpool_id
的參數項沒有正確配置致使,沒有遞增。shell
這些配置項不正確是因爲該環境上的ansible
版本爲2.7
,而數據庫自動化部署是基於ansible 2.4
開發,ansible 2.4.2
後,jinja2
部分高級語法發生改變,pgpool
配置文件是經過jinja2
生成的,部分配置項若是還使用原有語法,那麼有些配置結果不會達到預期,因此須要根據ansible
版本定製配置文件模板。數據庫
下面是模板文件pgpool.conf.j2
對應不一樣版本的對應配置:django
# - Other pgpool Connection Settings - {% set other_pgpool_id = 0 %} {% for backend in pgpool_cluster_entries %} {% if inventory_hostname != backend.ip %} heartbeat_destination{{other_pgpool_id}} = '{{backend.ip}}' heartbeat_destination_port{{other_pgpool_id}} = 9694 heartbeat_device{{other_pgpool_id}} = '' {% set other_pgpool_id = other_pgpool_id + 1 %} {% endif %} {% endfor %} # - Other pgpool Connection Settings - {% set other_pgpool_id = 0 %} {% for backend in pgpool_cluster_entries %} {% if inventory_hostname != backend.ip %} other_pgpool_hostname{{other_pgpool_id}} = '{{backend.ip}}' other_pgpool_port{{other_pgpool_id}} = 9998 other_wd_port{{other_pgpool_id}} = 9000 {% set other_pgpool_id = other_pgpool_id + 1 %} {% endif %} {% endfor %}
# - Other pgpool Connection Settings - {% set other_pgpool_id = namespace(a=0) %} {% for backend in pgpool_cluster_entries %} {% if inventory_hostname != backend.ip %} heartbeat_destination{{other_pgpool_id.a}} = '{{backend.ip}}' heartbeat_destination_port{{other_pgpool_id.a}} = 9694 heartbeat_device{{other_pgpool_id.a}} = '' {% set other_pgpool_id.a = other_pgpool_id.a + 1 %} {% endif %} {% endfor %} # - Other pgpool Connection Settings - {% set other_pgpool_id = namespace(a=0) %} {% for backend in pgpool_cluster_entries %} {% if inventory_hostname != backend.ip %} other_pgpool_hostname{{other_pgpool_id.a}} = '{{backend.ip}}' other_pgpool_port{{other_pgpool_id.a}} = 9998 other_wd_port{{other_pgpool_id.a}} = 9000 {% set other_pgpool_id.a = other_pgpool_id.a + 1 %} {% endif %} {% endfor %}
pgpool
做爲postgresql
的中間件,當集羣內存在至少兩個節點時,就會進行選舉,若是此時第三個節點還沒起來,當選舉完成後,pgpool
不會將沒有參加選舉的節點自動加入集羣,須要手工attach
進集羣,或者同時重啓pgpool
進行重啓選舉,即pgpool
自己不具備重啓後能自動加入集羣並恢復的機制。後端
將掉線節點手動從新加入數據庫集羣中,例如掉線節點爲192.168.1.1
而且node id
爲0
,執行下面的attach
命令:
pcp_attach_node -n 0 -p 9898 -h 192.168.1.1 -U postgres
分別在三個節點上,中止pgpool
服務
systemctl stop pgpool.service
Pgpool
每次選舉都會讀取pgpool
狀態文件,爲了不影響下次選舉,因此須要刪除該狀態文件
rm -f /var/log/pgpool/pgpool_status
分別在三個節點上,啓動pgpool
服務
systemctl start pgpool.service
NetworkManager
未關閉致使。
NetworkManager
開啓會影響pgpool
的正常工做,需確保關閉。
該問題是個小几率偶現問題,即master
節點斷電後,新的master
被選舉出,新的master
會將本地配置文件修改成master
對應的,而後還在成爲新master
的過程當中,這時候經過數據庫VIP
讀取的master
信息仍爲舊master
,這就使得本地數據庫failover
腳本認爲新master
出現了不一致,因而將以前postgresql
修改成master
的一系列配置文件又改回了standby
對應的配置文件,其中primary info
仍指向爲舊master
。這就致使沒有新的master
產生,舊的master
一直爲down
的狀態。而沒有master
節點, 數據庫則會進入只讀模式。
修改failover
腳本代碼邏輯,當本地配置文件與數據庫角色狀態不一致時,不會第一時間去修改本地recovery
文件。以前再加一層判斷:若是master
節點postgresql
服務還能正常訪問,再去修改recovery
文件。
客戶端的數據庫鏈接數超過pgpool
配置鏈接數上限。
Response
的狀況,須要添加try-catch
,在最終的finally
加上返回Response
的代碼;web
接口,即最後不走Response
的狀況,須要在程序最後額外添加關閉數據庫鏈接的代碼;from django.db import connections # 每個線程都有專屬的connections,把本線程名下的全部鏈接關閉。 connections.close_all()
Pgpool
配置文件中配置客戶端鏈接空閒最大時間爲300
秒:
client_idle_limit = 300 # Client is disconnected after being idle for that many seconds # (even inside an explicit transactions!) # 0 means no disconnection
該參數表示當一個客戶端在執行最後一條查詢後若是空閒到了client_idle_limit
秒數, 到這個客戶端的鏈接將被斷開。這裏配置爲300
秒,防止客戶端存在長時間的空閒鏈接佔用鏈接數。