PostgreSQL Connection Poolers

数据库连接池(Connection Pooler)是一种用于管理和复用数据库连接的组件,它允许应用程序在需要与数据库建立连接时从连接池中获取连接,而非每次都创建新的连接,以减少连接创建和销毁的开销。今天我们介绍 PostgreSQL 数据库生态中常用的四个具有连接池能力的工具: PgBouncer、Pgpool-II、Odyssey、PgCat。 为什么需要连接池? 通常 DBA 会给 PostgreSQL 实例设置一个合理的 max_connections 值,当连接数超过这个值后会报 FATAL: remaining connection slots are reserved for ... 的错误。 有些人认为 PostgreSQL 不能处理太多连接是因为它的进程模型(为每个连接创建一个 backend 进程)会占用大量内存,但其实更重要的原因是 Snapshot scalability,当有大量连接(即使状态为 idle)时,获取快照信息(GetSnapshotData)成为了性能瓶颈。 PG 14 针对 Snapshot scalability 的问题进行了优化: Improving Postgres Connection Scalability: Snapshots,降低了 idle 状态连接对数据库性能的影响,因此我们现在可以把 max_connections 设置为一个较大的值(maybe 1000~5000?)。那这是否意味着我们不再需要连接池了?答案是否定的,我能想到的两个原因: 应用端大量的短连接频繁创建、删除有性能开销 微服务架构下存在大量应用,相对单体架构需要更多的连接,连接数依然可能耗尽 依据连接池运行的位置可简单将其分为两类,一种是存在于应用程序的 driver 里,如 pgjdbc-ng 库提供的 Connection Pool DataSource;另一种是单独部署的进程。后文介绍的四个连接池都归为第二类。 PgBouncer PgBouncer 是一款轻量的连接池解决方案,单线程、低开销(单个连接占用约 2kB 内存)、使用 libevent 事件驱动库来处理异步 I/O,功能简单,配置容易。 三种使用模式: 会话模式(Session pooling): 一个客户连接对应一个服务端连接,支持 PostgreSQL 所有特性,常用于多个数据库的连接映射(Connection Mapping) 事务模式(Transaction pooling): 一个服务器连接只在事务期间分配给客户端。当 PgBouncer 察觉到事务结束时,连接将被放回连接池中。这种模式会破坏几个基于会话的功能 语句模式(Statement pooling): 不允许多语句事务,意味着 autocommit 必须打开 事务模式不支持的特性有:...

November 5, 2023 · 3 min · 512 words · pg-x

Don't Use Table Inheritance

PostgreSQL 是一个对象关系数据库管理系统(object-relational database management system),如果你看维基百科的定义,ORDBMS 是一种拥有面向对象特性的关系型数据库,对象、类、继承(Inheritance)等概念直接在数据库 schema 和查询语言层面支持。 An object–relational database (ORD), or object–relational database management system (ORDBMS), is a database management system (DBMS) similar to a relational database, but with an object-oriented database model: objects, classes and inheritance are directly supported in database schemas and in the query language. 一定会有人认为 Inheritance 是 PostgreSQL 被称为 ORDBMS 的主要原因,比如 David Johnston(PG contributor) 在 slack 上的回复: 但 PostgreSQL 社区维护的 Don’t Do This 里提示了 Don’t use table inheritance,所以 Inheritance 一定不是 PostgreSQL 称作 ORDBMS 的唯一原因。ChatGPT 给出的答案我认为具有一定参考价值:...

October 29, 2023 · 2 min · 235 words · pg-x

Citus cheatsheet

Citus 常用运维命令集。

October 25, 2023 · 1 min · 2 words · pg-x

Patroni 核心代码解读

Patroni 是一个用 Python 实现,用来部署、管理和监控 PostgreSQL 高可用(HA)集群的开源工具。本文对 Patroni 代码的核心模块进行简要介绍。 术语澄清 自 Patroni 3.0 可以管理 Citus 之后,Patroni 涉及了更多的 Cluster 术语,我觉得有必要在这里澄清一下,以消除理解上的偏差。 Database cluster: 由一个 PG 实例管理的多个 databases,也称作 PostgreSQL Instance 或 PostgreSQL 节点 PostgreSQL cluster: 多个 PostgreSQL Instance 构成的一主多备的拓扑结构 Citus cluster: 一个角色为 Coordinator 的 PostgreSQL cluster 和多个角色为 Worker 的 PostgreSQL cluster 构成的 MPP 架构 Patroni cluster: 泛指 Patroni 管理的集群,PostgreSQL cluster 和 Citus cluster 都可叫作 Patroni cluster 从 Patroni 中 Cluster 类的定义也能看出 Patroni cluster 可以代表 PostgreSQL cluster 和 Citus cluster 两类集群,对于非 Citus 集群而言,下面结构的 workers 字段为 None。...

October 24, 2023 · 6 min · 1231 words · pg-x

PostgreSQL 中三个常用的 timeout 参数

最近在浏览 guc_table.c 时注意到 PostgreSQL 有很多与超时相关的参数可供用户设置,在这些参数中,我想介绍三个我认为最常用的参数: statement_timeout: Sets the maximum allowed duration of any statement. idle_in_transaction_session_timeout: Sets the maximum allowed idle time between queries, when in a transaction. idle_session_timeout: Sets the maximum allowed idle time between queries, when not in a transaction. statement_timeout PostgreSQL 中长事务往往会带来一些问题,比如 table bloat(由于旧版本记录不能及时回收)、占用资源(锁、内存)等,因此有些实例会设置一个合理的 statement_timeout 来自动杀死运行时间过长的查询。 postgres=# set session statement_timeout = '10s'; SET postgres=# select pg_sleep(1000); ERROR: canceling statement due to statement timeout postgres=# 但这个参数对于 idle in transaction 的 session 不起作用,比如:...

October 19, 2023 · 2 min · 312 words · pg-x

有多少种方法可以判断 PostgreSQL 实例的主备类型?

在一个典型的 PostgreSQL master-slave 部署中,如何能快速识别谁是主谁是备?本文介绍几种常见的方法。 这些方法可以粗略归为两类: 通过客户端连接到实例上 能够登录到实例运行的主机上 先看第一类: 1.1 pg_is_in_recovery() pg_is_in_recovery 是一个 PostgreSQL 内置的 UDF,用于确定当前数据库实例是否处于恢复模式。在 PostgreSQL 中,恢复模式是指数据库实例正在进行故障恢复或流复制时的工作状态。 既然能连到实例去执行 SQL,证明肯定不处于故障恢复状态,因此,如果查询返回 true,则表示数据库实例处于恢复模式,如果是 false,则表示数据库实例是主服务器。 postgres=# select pg_is_in_recovery(); pg_is_in_recovery ------------------- t (1 row) 当然,如果备机压根没有开启 hot_standby,通过连接时的报错即可知道它为备机: ➜ psql -p 5433 postgres psql: error: connection to server on socket "/tmp/.s.PGSQL.5433" failed: FATAL: the database system is not accepting connections DETAIL: Hot standby mode is disabled. 1.2 in_hot_standby in_hot_standby 是 pg14 引入的一个 INTERNAL 参数(不能被用户更改,只能由进程内部逻辑进行设置)。当实例处于 hot_standby 时该参数查询为 on,当实例被 promote 后,参数值为 off。...

October 9, 2023 · 3 min · 538 words · pg-x

如何获取 PostgreSQL 实例的创建时间?

PostgreSQL 实例在启动的时候会创建一个 postmaster.pid 文件,通过查看该文件的状态可以知道实例启动的时间,另外 pg_stat_activity 记录了辅助进程的状态,因此我们可以通过 select pg_stat_file('postmaster.pid') AS start_time;、 select MIN(backend_start) as start_time from pg_stat_activity; 或者 select pg_postmaster_start_time(); 来获取实例启动的时间(或者 ),但如何知道实例的创建时间呢? PostgreSQL 创建实例(initdb)的时候会写一个 global/pg_control 文件,这其中有一个字段 Database system identifier,在实例 bootstrap 的时候初始化该字段,且在实例之后的生命周期中不再变更。 void BootStrapXLOG(void) { ... gettimeofday(&tv, NULL); sysidentifier = ((uint64) tv.tv_sec) << 32; sysidentifier |= ((uint64) tv.tv_usec) << 12; sysidentifier |= getpid() & 0xFFF; ... /* Now create pg_control */ InitControlFile(sysidentifier); ... } 因此我们可以读取该字段,解析其高 32 位即可获取实例创建的时间: ➜ postgres_data pg_controldata . | head pg_control version number: 1300 Catalog version number: 202308241 Database system identifier: 7271454612807718361 Database cluster state: in production pg_control last modified: Mon Oct 2 12:56:26 2023 Latest checkpoint location: 0/2C0163A0 Latest checkpoint's REDO location: 0/2C016368 Latest checkpoint's REDO WAL file: 00000001000000000000002C Latest checkpoint's TimeLineID: 1 Latest checkpoint's PrevTimeLineID: 1 postgres=# select to_hex(7271454612807718361); to_hex ------------------ 64e96571ccc795d9 (1 row) postgres=# select '0x64e96571'::int; int4 ------------ 1693017457 (1 row) postgres=# select to_timestamp(1693017457) as cluster_init; to_timestamp ------------------------ 2023-08-26 10:37:37+08 (1 row) 或者用一条语句获取实例创建时间:...

October 5, 2023 · 1 min · 165 words · pg-x