PostgreSQL的读写分离是通过2部分实现的
- 主从集群(可以一主多从)
- Pgpool-II 代理来分发读写操作到不同服务器
Pgpool-II 功能强大,还可实现一个表/数据库分发到不同服务器上等等,此处不做讨论
环境
三台服务器(可以把以下3个服务安装到一台,此处三台仅仅是为表达清晰)
- Pgpool-II , IP: 192.168.1.10
- PostgreSQL 主服务器, IP: 192.168.1.11
- PostgreSQL 从服务器, IP: 192.168.1.12
集群环境搭建
安装PostgreSQL
分别在主从服务器(192.168.1.11, 192.168.1.12) 上安装PostgreSQL, apt install postgresql
配置
以下操作主从两个服务器都需要
设置可以从网络访问
修改 postgresql.conf 文件(如果apt安装且版本12,在/etc/postgresql/12/main/目录下)
更改 listen_addresses 修改为*或者自己的IP地址
设置主从复制用户访问
修改 pg_hba.conf,在末尾增加一行:
1 | host replication all 192.168.1.1/24 trust |
这行是信任本地网络上所有复制用操作的连接
在从服务器上设置
在从服务器(192.168.1.12)上配置复制, 修改 postgresql.conf 文件,找到 primary_conninfo, hot_standby, wal_level 并修改他们
1 | primary_conninfo = 'host=192.168.1.11 port=5432 user=postgres password=' |
这几项不设置也行,下面的pg_basebackup命令会在main目录下生成相应的配置
192.168.1.11是主数据库服务器IP,postgres是连接用户名,password是密码,因为我设置了信任所有本地网络用户,所以这里没密码,这行要根据自己的环境修改。
停止从服务器上的postgresql服务
1 | systemctl stop postgresql |
进入数据库所在目录(如果ubuntu, apt安装12版,在 /var/lib/postgresql/12),并删除main目录,之后运行pg_basebackup命令从主服务器拷贝数据库文件,最后别忘了把新的main目录改成postgresql的用户
1 | cd /var/lib/postgrewsql/12 |
如果pg_basebackup命令成功,会出现类似提示
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/4000028 on timeline 1
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_8071"
32514/32514 kB (100%), 1/1 tablespace
pg_basebackup: write-ahead log end point: 0/4000100
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: base backup completed
并且在main目录下有一个 standby.signal 文件
之后启用postgresql
1 | systemctl start postgresql |
这时,可以用psql连接到从服务器,进行以下测试了,正确的结果应该是查询的sql都没问题,更新的sql不能执行了,会提示事务是只读的。
也可以执行select pg_is_in_recovery();
应该返回T,表示服务器处于恢复模式
其他配置(和主从无关)
仅仅为主从可忽略此部分
postgresql.conf
- max_connections 默认为100,可根据需要增大最大连接数;从服务器的最大连接数不能小于主服务器,否则会启动失败
- shared_buffers 在独立的数据库服务器上一般设置为物理内存的1/4
- work_mem增大则对单个SQL的排序等效果明显
pg_hba.conf
可根据需要设置允许网络上某个用户连接及连接方式,例如把本地网络连接都允许且设置为trust,则可免去每次都输入密码的麻烦
负载均衡,Pgpool-II 搭建
以下操作都在 Pgpool-II (192.168.1.10)服务器上操作
安装
1 | apt install pgpool2 |
配置
修改 /etc/pgpool2/pgpool.conf 文件
数据库节点配置
默认代理端口号是5433,可根据需要修改
增加2个节点,找到 backend_hostname0部分,按照下面内容修改
1 | backend_hostname0 = '192.168.1.11' |
replication_mode 要设置成off。如果设置on,是由pgpool做复制操作,它会把所有更改SQL发送到每个节点,每个节点都执行一份,当有节点离线时,不会自动在上线后重发,需要设置很多东西,比较麻烦,所以还是用上面的postgresql自己内置的replication机制。
load_balance_mode 可是实现查询操作的负载均衡,如果off,所有sql都在backend0执行,如果on则根据 backend_weight权重比例来分配查询sql
master_slave_mode 指定服务器采用主从模式,backend0为主
pgpool 管理用配置
pgpool通过一系列pcp命令来维护/管理各个backend node(节点),默认管理端口时9898,如需修改在pgpool.conf内修改 pcp_port, 也可修改pcp_listen_addresses来允许远程管理
首先需要给pcp管理创建一个用户,通过修改pcp.conf实现,例如增加一行
1 | pcp:ac5c74b64b4b8352ef2f181affb5ac2a |
则增加了一个用户,用户名pcp,密码sql(密码存储的是md5值)
backend node 的状态
节点的状态维护并不是默认自动的,需要使用pcp命令来管理
除了backend0外,其他新增节点默认是 unused 状态,需要用pcp_attach_node改变状态
1 | pcp_attach_node -U pcp -p 9898 -h 127.0.0.1 1 |
pcp_attach_node 用于把节点状态变为可用,最后一个1是节点序号,在pgpool.conf设置backend时的需要,也可以用sql命令show pool_nodes
查到。
成功执行pcp_attach_node后,如果连接节点没问题,节点状态会变更为 up,否则为down
每个down/unused状态不会自动变为up
up也不会自动变为down,这在分发sql时就会出现问题,把sql分发到了已经停止服务的节点不是我们想要的,为了避免需要配置 health check
在pgpool.conf内做如下配置
1 | health_check_period = 10 #单位秒 |
需要把用户名、密码、数据等配制成自己环境所需,之后重启,pgpool就可以检查每个节点的状态是否依旧在线了,如果已经连接不到,则会自动变成down状态
(down, unused) => up 通过 pcp_attach_node
pgpool 配置中的2个参数
num_init_children
num_init_children 这个项目是启动多少个线程来接收pgpool的客户端(一般是我门开发的程序)的连接,当连接数超过这个值时,客户端就会等待直到有连接释放出来或者超时。
如果不使用pgpool时,直接连接postgresql,与postgresql的max_connections类似(在这个数目内都能正常连接,超过这个数就不正常了,区别是超过这个数后,连接postgresql的自动断了,连接pgpool的会进入等待队列)
默认值:100
如果自己的程序中使用了连接池,那么连接池的最大连接数不要超过这个值。
max_pool
这个有点特殊,和postgresql中没有对应的项目。它是每一个连接(num_init_children的每个线程)可以对应的往pgpool的 backend node的连接池数,如果使用多个数据库,这个一般设置成常用的数据库数目。让每个线程都有到每个数据库的连接池,这样速度最快。但要注意 num_init_children * max_pool 应该小于等于 backend node的max_connections
默认值:4
如果服务器上仅仅有一个数据库是用于生产的,那么改成1是个不错的选择。
常见问题
ERROR: canceling statement due to conflict with recovery
ERROR: canceling statement due to conflict with recovery
Detail: User query might have needed to see row versions that must be removed.
原因
产生这个错误的原因是:sql在从服务器上执行,但执行时间较长,执行过程中从服务器从主服务器同步数据,更改了sql中的数据。
解决办法
以下几个办法都行
- 优化sql,缩短执行时间,这是最好的办法
- 设置 hot_standby_feedback = on
- 增大 max_standby_archive_delay max_standby_streaming_delay 两个参数的值
- 把查询放到主服务器执行
2和3都有些缺点,2会增加主服务器负担,3会加大从服务器和主服务器之间数据差异。
法4则失去了负载均衡的价值。