使用PostgreSQL的libpq或JDBC,您可以通過簡單的配置實現自動故障轉移(failover)和讀寫分離。
背景信息
從PostgreSQL 10開始,libpq驅動層開始支持簡單的故障轉移,JDBC驅動層則支持簡單的故障轉移和負載均衡。
libpq是PostgreSQL的C應用程序接口,包含一組庫函數,允許客戶端程序將查詢請求發送給PostgreSQL后端服務器并接收這些查詢的結果。
JDBC(Java Database Connectivity)是Java語言中用來規范客戶端程序如何訪問數據庫的應用程序接口,在PostgreSQL中JDBC支持故障轉移和負載平衡(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()函數,判斷連接的數據庫是主實例數據庫的還是只讀實例數據庫,最終實現讀寫分離和故障轉移。