使用客户端驱动程序实现故障转移和读写分离
从PostgreSQL 10(libpq.so.5.10)开始,libpq驱动层开始支持故障转移和读写分离,JDBC驱动层则支持读写分离、故障转移和负载均衡。
PostgreSQL客户端连接程序向下兼容,对于RDS for PostgreSQL 9.5及9.6版本,使用新版本的libpq驱动程序也可以实现故障转移。
本章节中故障转移指的是读业务的故障转移。
- libpq是PostgreSQL的C应用程序接口,包含一组库函数,允许客户端程序将查询请求发送给PostgreSQL后端服务器并接收这些查询的结果。
- JDBC是Java语言中用来规范客户端程序如何访问数据库的应用程序接口,在PostgreSQL中JDBC支持故障转移和负载均衡。
驱动 |
读写分离 |
负载均衡 |
故障转移 |
---|---|---|---|
libpq驱动 |
√ |
× |
√ |
JDBC驱动 |
√ |
√ |
√ |
postgresql://[user[:password]@][netloc][:port][,...][/dbname][?param1=value1&...]
示例:连接1个RDS for PostgreSQL主实例数据库和对应的2个只读实例数据库,只要确保至少有一个数据库可用,读请求就不会失败。
postgres://<instance_ip>:<instance_port>,<instance_ip>:<instance_port>,<instance_ip>:<instance_port>/<database_name>?target_session_attrs=any
参数 |
说明 |
取值样例 |
---|---|---|
<instance_ip> |
数据库的主机IP。 |
如果通过内网连接,“instance_ip”是主机IP,即“概览”页面该实例的“内网地址”。 如果通过连接了公网的设备访问,“instance_ip”为该实例已绑定的“弹性公网IP”。 |
<instance_port> |
数据库端口。 |
默认5432,当前端口,参考“概览”页面该实例的“数据库端口”。 |
<database_name> |
数据库名,即需要连接的数据库名。 |
默认的管理数据库是postgres,可根据业务实际情况填写数据库名。 |
target_session_attrs |
允许连接到指定状态的数据库。 |
|
更多libpq的使用方法和参数说明请参见Connection Strings。
您还可以在应用程序中结合pg_is_in_recovery()函数,判断连接的数据库是主实例数据库(结果为“f”表示主数据库)还是只读实例数据库,进而实现读写分离。
使用Python代码的示例如下(psycopg2使用的为libpq):
// 认证用的用户名和密码直接写到代码中有很大的安全风险,建议在配置文件或者环境变量中存放(密码应密文存放,使用时解密),确保安全。 // 本示例以用户名和密码保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量(环境变量名称请根据自身情况进行设置)EXAMPLE_USERNAME_ENV和EXAMPLE_PASSWORD_ENV。 import psycopg2 import os username = os.getenv("EXAMPLE_USERNAME_ENV") password = os.getenv("EXAMPLE_PASSWORD_ENV") conn = psycopg2.connect(database=<database_name>,host=<instance_ip>, user=username, password=password, port=<instance_port>, target_session_attrs="read-write") cur = conn.cursor() cur.execute("select pg_is_in_recovery()") row = cur.fetchone() print("recovery =", row[0])
JDBC实现故障转移和读写分离
您可以在连接URL中定义多个数据库(主机和端口),并用逗号分隔,驱动程序将尝试按顺序连接到它们中的每一个,直到连接成功。如果没有成功,会返回连接异常报错。
jdbc:postgresql://node1,node2,node3/${database}?targetServerType=preferSecondary&loadBalanceHosts=true
示例:
jdbc:postgresql://<instance_ip>:<instance_port>,<instance_ip>:<instance_port>,<instance_ip>:<instance_port>/<database_name>?targetServerType=preferSecondary&loadBalanceHosts=true
JDBC连接实例java实现代码请参考:通过JDBC连接RDS for PostgreSQL实例。
参数 |
说明 |
取值样例 |
---|---|---|
targetServerType |
允许连接到指定状态的数据库。 |
|
loadBalanceHosts |
尝试连接数据库的顺序。 |
|
区别数据库主从的方式是通过查询数据库是否允许写入,允许写入数据的判断为主数据库,不允许写入数据的判断为从数据库。参考libpq实现故障转移和读写分离中通过pg_is_in_recovery()函数来判断,结果为“f”表示为主数据库。
为实现读写分离,需要在配置JDBC时设置2个数据源,首先设置targetServerType=primary,用于写操作。另一个可以根据以下情况进行设置:
- 有一个只读实例,为实现高可用设置targetServerType=preferSecondary,用于读操作。假设主实例IP为10.1.1.1,只读实例IP为10.1.1.2。
jdbc:postgresql://10.1.1.2:5432,10.1.1.1:5432/${database}?targetServerType=preferSecondary
- 有两个以上只读实例,可设置targetServerType=any,用于读操作。假设只读实例IP分别为10.1.1.2、10.1.1.3。
jdbc:postgresql://10.1.1.2:5432,10.1.1.3:5432/${database}?targetServerType=any&loadBalanceHosts=true