使用PostgreSQL的libpq或JDBC,您可以通過簡單的配置實現自動容錯移轉(failover)和讀寫分離。
背景資訊
從PostgreSQL 10開始,libpq驅動層開始支援簡單的容錯移轉,JDBC驅動層則支援簡單的容錯移轉和負載平衡。
libpq是PostgreSQL的C應用程式介面,包含一組庫函數,允許用戶端程式將查詢請求發送給PostgreSQL後端伺服器並接收這些查詢的結果。
JDBC(Java Database Connectivity)是Java語言中用來規範用戶端程式如何訪問資料庫的應用程式介面,在PostgreSQL中JDBC支援容錯移轉和Server Load Balancer(Load Balance)。
libpq實現自動容錯移轉和讀寫分離
通過libpq函數串連多個資料庫,當出現故障時會自動切換到可用的資料庫。
命令
postgresql://[user[:password]@][netloc][:port][,...][/dbname][?param1=value1&...]
樣本
如下樣本為串連1個RDS PostgreSQL主執行個體資料庫和對應的2個唯讀執行個體資料庫,只要確保至少有一個資料庫可用,讀請求就不會失敗。
postgres://pgm-bpxxx1.pg.rds.aliyuncs.com:3433,pgm-bpxxx2.pg.rds.aliyuncs.com:3433,pgm-bpxxx3.pg.rds.aliyuncs.com:3433/postgres?target_session_attrs=any
target_session_attrs表示允許串連到指定狀態的資料庫,取值:
any:預設值,表示允許串連到任意資料庫,會從所有配置的資料庫中隨機播放一個嘗試串連,如果串連的資料庫出現故障導致串連斷開,會嘗試串連其他資料庫,從而實現容錯移轉。
read-write:只會串連到支援讀寫的資料庫,即從第一個資料庫開始嘗試串連,如果串連後發現不支援讀寫,則會中斷連線,然後嘗試串連第二個資料庫,以此類推,直至串連到支援讀寫的資料庫。
更多libpq的使用方法和參數說明請參見Connection Strings。
您可以在應用程式中結合pg_is_in_recovery()函數,判斷串連的資料庫是主執行個體資料庫的還是唯讀執行個體資料庫,最終實現讀寫分離和容錯移轉,樣本如下:
Python樣本
$ cat pg_conn.py import psycopg2 conn = psycopg2.connect(database="postgres",host="pgm-bpxxx1.pg.rds.aliyuncs.com,pgm-bpxxx2.pg.rds.aliyuncs.com,pgm-bpxxx3.pg.rds.aliyuncs.com", user="testxxx", password="xxxxxx", port="3433", target_session_attrs="read-write") cur = conn.cursor() cur.execute("select pg_is_in_recovery(), pg_postmaster_start_time()") row = cur.fetchone() print "recovery =",row[0] print "time =",row[1] $ python pg_conn.py recovery = False time = 2020-07-09 15:33:57.79001+08
說明上述樣本中的print文法僅適用於Python 2,如使用的Python版本為Python 3,請將print部分修改為如下內容:
print("recovery =", row[0]) print("time =", row[1])
PHP樣本
# cat pg_conn.php <?php $conn = pg_connect("host=pgm-bpxxx1.pg.rds.aliyuncs.com,pgm-bpxxx2.pg.rds.aliyuncs.com,pgm-bpxxx3.pg.rds.aliyuncs.com port=3433 dbname=postgres user=testxxx password=xxxxxx target_session_attrs=read-write") or die("Could not connect"); $status = pg_connection_status($conn); if ($status === PGSQL_CONNECTION_OK) { print "Connection status ok\n"; } else { print "Connection status bad\n"; } $sql = pg_query($conn, "select pg_is_in_recovery()"); while ($row = pg_fetch_row($sql)) { echo "Recovery-status: $row[0]\n"; } ?> $ php -f pg_conn.php Connection status ok Recovery-status: f Server: xxx.xxx.xx.xx
JDBC實現讀寫分離和自動容錯移轉
您可以在串連URL中定義多個資料庫,並用逗號分隔,驅動程式將嘗試按順序串連到它們中的每一個,直到串連成功。如果沒有成功,會返回串連異常報錯。
命令
jdbc:postgresql://node1,node2,node3/accounting?targetServerType=preferSlave&loadBalanceHosts=true
樣本
jdbc:postgresql://pgm-bpxxx1.pg.rds.aliyuncs.com:3433,pgm-bpxxx2.pg.rds.aliyuncs.com:3433,pgm-bpxxx3.pg.rds.aliyuncs.com:3433/accounting?targetServerType=preferSlave&loadBalanceHosts=true
參數說明如下:
targetServerType表示允許串連到指定狀態的資料庫,取值:
any:任何資料庫。
master:主要資料庫。
slave:從資料庫。
preferSlave:優先從資料庫,如果沒有從資料庫才串連到主要資料庫。
說明區別資料庫主從的方式是通過查詢資料庫是否允許寫入,允許寫入的判斷為主要資料庫,不允許寫入的判斷為從資料庫。
loadBalanceHosts表示嘗試串連資料庫的順序,取值:
False:預設值,按命令內順序串連資料庫。
True:隨機串連資料庫。
為實現讀寫分離,您需要在配置JDBC時配置2個資料來源,1個設定targetServerType=master,一個設定targetServerType=preferSlave。需要寫操作時,指定master的資料來源,需要讀操作時,指定preferSlave的資料來源。如果需要判斷資料來源類型,您可以結合pg_is_in_recovery()函數,判斷串連的資料庫是主執行個體資料庫的還是唯讀執行個體資料庫,最終實現讀寫分離和容錯移轉。