在postgresql发行版中只包含两个客户端接口: libpq 和 ecpg
- libpq is included because it is the primary c language interface, and because many other client interfaces are built on top of it.
- ecpg is included because it depends on the server-side sql grammar, and is therefore sensitive to changes in postgresql itself.
其他语言客户端接口:
name | language | comments | website |
---|---|---|---|
dbd::pg | perl | perl dbi driver | |
jdbc | java | type 4 jdbc driver | |
libpqxx | c | c interface | |
node-postgres | javascript | node.js driver | |
npgsql | .net | .net data provider | |
pgtcl | tcl | - | |
pgtclng | tcl | - | |
pq | go | pure go driver for go’s database/sql | |
psqlodbc | odbc | odbc driver | |
psycopg | python | db api 2.0-compliant |
至于 c# 语言和 .net 是啥关系,这里就不提了,百度一搜一大把。
npgsql 是一个用于 postgresql 的开源 ado.net 数据提供程序,它允许用 c#、visual basic、f# 编写的程序访问 postgresql 数据库服务器。它是用 100% c# 代码实现的,是免费的并且是开源的。
npgsql 也不用单独下载,直接在 visual studio 2019(.net 的编程工具) 就能直接安装。
npgsql的官方手册:
测试这个还挺麻烦,需要下载安装.net开发工具 visual studio 2019
下载地址:
新建项目
选择 windows 窗体应用,对于我这种小白,不适合直接搞代码,整个窗体拖一拖还是挺舒服
可以使用 visual stdio 2019 连接 postgresql 数据库
本次测试用不到这个,仅作为知识点,visual stdio 2019 中并未自带 postgresql 的连接功能,我们需要手动配置
还需要关闭程序
关闭程序后执行配置,点击 modify
在项目中添加 npgsql,这个是必须的,为了在代码中引用npgsql(using npgsql;) 实现对数据库的操作功能
右击项目名,选择管理 nuget 程序包
在窗体中拖动添加控件,按钮控件(button),标签控件(label),列表框控件(listbox)
为按钮控件和标签控件设置 name 和 text 属性,为列表框控件设置 name 属性
为按钮控件设置单击(click)事件
在源代码中引用 npgsql(using npgsql;)
在源代码里按钮的单击(click)事件中加入以下代码
// 编写数据库连接串
string connstring = "host=192.168.58.10;port=5432;username=postgres;password=postgres;database=postgres";
try
{
// 创建数据库连接
using (npgsqlconnection conn = new npgsqlconnection(connstring))
{
// 打开数据库连接
conn.open();
// 定义查询sql
string sql = "select inet_server_addr(),pg_is_in_recovery(),current_database(),current_user";
// 创建 npgsqldataadapter 类的对象
npgsqldataadapter sda = new npgsqldataadapter(sql, conn);
// 创建 dataset 类的对象
dataset ds = new dataset();
// 使用 npgsqldataadapter 对象 sda 将查询结果填充到 dataset 对象 ds 中
sda.fill(ds);
// 设置列表控件的数据源(datasource)属性
ip_b.datasource = ds.tables[0];
recover_b.datasource = ds.tables[0];
db_b.datasource = ds.tables[0];
user_b.datasource = ds.tables[0];
// 在列表控件中显示列值
ip_b.displaymember = ds.tables[0].columns[0].tostring();
recover_b.displaymember = ds.tables[0].columns[1].tostring();
db_b.displaymember = ds.tables[0].columns[2].tostring();
user_b.displaymember = ds.tables[0].columns[3].tostring();
// 释放资源,关闭数据库连接
ds.dispose();
sda.dispose();
conn.close();
conn.dispose();
}
}
catch (exception ex)
{
messagebox.show("查询失败!" ex.message);
}
测试运行
c# 连接数据库思路和代码的学习受益于:
npgsql 也提供了故障转移和负载均衡的功能,但是这个功能在 npgsql 6.0 中引入,该版本仍处于测试阶段。
npgsql 最新的版本信息:
npgsql 关于故障转移和负载均衡的介绍:
visual studio 2019 上下载不到 npgsql 6.0 的版本,需要在windows cmd中使用 dotnet 命令指定版本下载:
# cd 到项目文件夹中
c:\users\administrator# cd c:\users\administrator\source\repos\pg_conn\pg_conn
c:\users\administrator\source\repos\pg_conn\pg_conn# dotnet add pg_conn.csproj package npgsql --version 6.0.0-rc.2
多台服务器和故障转移
npgsql 允许连接字符串中指定多个服务器,如下所示:
host=server1:port,server2:port;username=test;password=test
默认情况下,npgsql 将尝试按照指定的顺序连接到服务器。
# 192.168.58.10是主节点,192.168.58.11/12是只读从节点
# 按顺序连接,当192.168.58.10不可用时,连接192.168.58.11
string connstring = "host=192.168.58.10:5432,192.168.58.11:5432,192.168.58.12:5432;username=postgres;password=postgres;database=postgres";
# 关闭主库 192.168.58.10 ,不进行主备切换
[root@pgtest1 ~]# patronictl pause
success: cluster management is paused
[root@pgtest1 ~]# su - postgres
last login: sat nov 6 17:02:07 cst 2021 on pts/0
[postgres@pgtest1 ~]$ pg_ctl stop
waiting for server to shut down..... done
server stopped
[postgres@pgtest1 ~]$
[postgres@pgtest1 ~]$ patronictl list
--------- --------------- --------- --------- ---- -----------
| member | host | role | state | tl | lag in mb |
cluster: pg_cluster (7025023477017500881) -- ---- -----------
| pgtest1 | 192.168.58.10 | replica | stopped | | unknown |
| pgtest2 | 192.168.58.11 | replica | running | 26 | 0 |
| pgtest3 | 192.168.58.12 | replica | running | 26 | 0 |
--------- --------------- --------- --------- ---- -----------
maintenance mode: on
此时连到只读库 192.168.58.11,且多次 点击测试 创建数据库连接,连接的都是 192.168.58.11,如果是只读业务还好,此时不能进行写业务,npgsql 又提供了target session attributes参数指定服务器类型。
指定服务器类型 target session attributes
# 192.168.58.10是主节点,192.168.58.11/12是只读从节点
# 关闭主库 192.168.58.10 ,不进行主备切换的情况下,以下连接报错,找不到 primary
string connstring = "host=192.168.58.10:5432,192.168.58.11:5432,192.168.58.12:5432;username=postgres;password=postgres;database=postgres;target session attributes=primary";
在真实的故障转移场景中,如果主库出现故障,备库通常会提升为新的主服务器。但是,原来的主库可能会恢复并承担备用角色,此时服务器将切换角色,并且 npgsql 将尽可能继续连接原来的主库。为了缓解这种情况,也可以告诉 npgsql 希望连接到哪种服务器类型:
# 192.168.58.10是主节点,192.168.58.11/12是只读从节点
# 关闭主库进行主备切换,当192.168.58.10不可用时,连接到切换后的主库 192.168.58.12(primary)
# npgsql 只返回到 primary 的连接,无论 primary 位于 connstring 的主机列表中的哪个位置,192.168.58.12处于最后位置,它的角色是primary,所以就没有连192.168.58.11。
string connstring = "host=192.168.58.10:5432,192.168.58.11:5432,192.168.58.12:5432;username=postgres;password=postgres;database=postgres;target session attributes=primary";
[root@pgtest1 ~]# patronictl resume
success: cluster management is resumed
[root@pgtest1 ~]# patronictl list
--------- --------------- --------- --------- ---- -----------
| member | host | role | state | tl | lag in mb |
cluster: pg_cluster (7025023477017500881) -- ---- -----------
| pgtest1 | 192.168.58.10 | leader | running | 27 | |
| pgtest2 | 192.168.58.11 | replica | running | 27 | 0 |
| pgtest3 | 192.168.58.12 | replica | running | 27 | 0 |
--------- --------------- --------- --------- ---- -----------
[root@pgtest1 ~]# systemctl stop patroni
[root@pgtest1 ~]# patronictl list
--------- --------------- --------- --------- ---- -----------
| member | host | role | state | tl | lag in mb |
cluster: pg_cluster (7025023477017500881) -- ---- -----------
| pgtest1 | 192.168.58.10 | replica | stopped | | unknown |
| pgtest2 | 192.168.58.11 | replica | running | 27 | 0 |
| pgtest3 | 192.168.58.12 | leader | running | 28 | |
--------- --------------- --------- --------- ---- -----------
读写分离
有这种情况,应用程序的某些部分只需要从数据库中读取数据,而其他部分则需要写入数据。如果您有一台或多台备用服务器,npgsql 可以向这些服务器发送只读查询,以减少主服务器的负载。虽然上述故障转移设置提高了可靠性,但负载均衡这种技术提高了性能。
# 写读业务
host=server1:port,server2:port;username=test;password=test;target session attributes=primary";
# 只读业务
host=server1:port,server2:port;username=test;password=test;target session attributes=prefer-standby
使用 prefer-standby,只要至少有一个备用服务器可用,npgsql 就会返回与该服务器的连接。但是,如果所有备用服务器都关闭(或已用完它们的max pool size设置),则将返回到主服务器的连接。
target session attributes 支持以下选项:
option | description |
---|---|
any | any successful connection is acceptable. |
primary | server must not be in hot standby mode (pg_is_in_recovery() must return false). |
standby | server must be in hot standby mode (pg_is_in_recovery() must return true). |
prefer-primary | first try to find a primary server, but if none of the listed hosts is a primary server, try again in any mode. |
prefer-standby | first try to find a standby server, but if none of the listed hosts is a standby server, try again in any mode. |
read-write | session must accept read-write transactions by default (that is, the server must not be in hot standby mode and the default_transaction_read_only parameter must be off). |
read-only | session must not accept read-write transactions by default (the converse). |
# 192.168.58.10是主节点,192.168.58.11/12是只读从节点
# 在主备库正常的情况下,prefer-standby 控制连接只读备库,多个只读备库的情况下,也是按主机列表顺序连接,多次“点击测试”,只连一个只读备库192.168.58.11
string connstring = "host=192.168.58.10:5432,192.168.58.11:5432,192.168.58.12:5432;username=postgres;password=postgres;database=postgres;target session attributes=prefer-standby";
[root@pgtest1 ~]# patronictl list
--------- --------------- --------- --------- ---- -----------
| member | host | role | state | tl | lag in mb |
cluster: pg_cluster (7025023477017500881) -- ---- -----------
| pgtest1 | 192.168.58.10 | leader | running | 29 | |
| pgtest2 | 192.168.58.11 | replica | running | 29 | 0 |
| pgtest3 | 192.168.58.12 | replica | running | 29 | 0 |
--------- --------------- --------- --------- ---- -----------
负载均衡
我们已经了解了如何根据要执行的工作负载类型选择服务器。但是,在上面的例子中,npgsql 仍然尝试根据连接字符串中指定的主机顺序返回连接;这将负载集中在单个主服务器和可能的单个辅助服务器上,并且不会在多个相同类型的服务器之间平衡负载。
您可以在连接字符串中指定 load balance hosts=true 以指示 npgsql 在所有服务器之间进行负载平衡,通过以循环方式返回连接:
# 写读业务
host=server1:port,server2:port;username=test;password=test;target session attributes=primary";
# 只读业务
host=server1:port,server2:port,server3:port;username=test;password=test;load balance hosts=true;target session attributes=prefer-standby
# 192.168.58.10是主节点,192.168.58.11/12是只读从节点
# 在主备库正常的情况下,prefer-standby 控制连接只读备库,load balance hosts 控制只读备库之间的负载均衡
string connstring = "host=192.168.58.10:5432,192.168.58.11:5432,192.168.58.12:5432;username=postgres;password=postgres;database=postgres;load balance hosts=true;target session attributes=prefer-standby";
[root@pgtest1 ~]# patronictl list
--------- --------------- --------- --------- ---- -----------
| member | host | role | state | tl | lag in mb |
cluster: pg_cluster (7025023477017500881) -- ---- -----------
| pgtest1 | 192.168.58.10 | leader | running | 29 | |
| pgtest2 | 192.168.58.11 | replica | running | 29 | 0 |
| pgtest3 | 192.168.58.12 | replica | running | 29 | 0 |
--------- --------------- --------- --------- ---- -----------
再点击一个点击测试 创建数据库连接,就返回另一个只读备库的连接,但是在我的测试中负载均衡load balance hosts=true不咋好使,偶尔能连另一个只读备库,不想jdbc的负载均衡那样丝滑,可能是刚出来的技术,还有待改进。
# 192.168.58.10是主节点,192.168.58.11/12是只读从节点
# 停到一个只读备库,多次连接始终返回另一个只读库的连接
string connstring = "host=192.168.58.10:5432,192.168.58.11:5432,192.168.58.12:5432;username=postgres;password=postgres;database=postgres;load balance hosts=true;target session attributes=prefer-standby";
[root@pgtest2 ~]# systemctl stop patroni
[root@pgtest1 ~]# patronictl list
--------- --------------- --------- --------- ---- -----------
| member | host | role | state | tl | lag in mb |
cluster: pg_cluster (7025023477017500881) -- ---- -----------
| pgtest1 | 192.168.58.10 | leader | running | 29 | |
| pgtest2 | 192.168.58.11 | replica | stopped | | unknown |
| pgtest3 | 192.168.58.12 | replica | running | 29 | 0 |
--------- --------------- --------- --------- ---- -----------
# 192.168.58.10是主节点,192.168.58.11/12是只读从节点
# 停到所有只读备库,返回主库的连接
[root@pgtest3 ~]# systemctl stop patroni
[root@pgtest1 ~]# patronictl list
--------- --------------- -------- --------- ---- -----------
| member | host | role | state | tl | lag in mb |
cluster: pg_cluster (7025023477017500881) - ---- -----------
| pgtest1 | 192.168.58.10 | leader | running | 29 | |
--------- --------------- -------- --------- ---- -----------