如果您只有一分钟,请记住这张图:
flowchart TB
subgraph App["💻 应用代码"]
A1["// 只需知道服务名,不用关心具体实例<br/>db.connect('postgres://user@pg-test:5433/mydb') // 读写服务<br/>db.connect('postgres://user@pg-test:5434/mydb') // 只读服务"]
end
App --> HAProxy
subgraph HAProxy["🔀 HAProxy 服务层"]
direction LR
S1[":5433 primary<br/>读写业务"]
S2[":5434 replica<br/>只读查询"]
S3[":5436 default<br/>管理操作"]
S4[":5438 offline<br/>ETL分析"]
end
HAProxy --> Cluster
subgraph Cluster["🐘 底层集群(对应用透明)"]
direction LR
C1["pg-test-1<br/>(Primary)"] --> C2["pg-test-2<br/>(Replica)"] --> C3["pg-test-3<br/>(Offline)"]
end
Value["✅ 价值:主库故障?自动切换,应用无需改代码。读写分离?改端口即可,无需改逻辑。"]
核心价值:应用只需连接"服务",不用知道具体实例在哪。主库切换、从库扩容、故障恢复——对应用完全透明。
本章内容
| 章节 | 说明 | 核心问题 |
|---|---|---|
| 服务抽象 | 服务的定义、类型与配置 | 什么是服务?有哪些类型? |
| 接入方式 | DNS、VIP、端口多种接入方式 | 应用如何连接数据库? |
为什么需要服务
直连 IP 的问题
flowchart TB
subgraph Problems["❌ 硬编码 IP 的困境"]
Config["应用配置:db.host = '10.10.10.11:5432'"]
P1["❌ 主库故障<br/>• 10.10.10.11 挂了,新主库在 10.10.10.12<br/>• 需要改配置、重启应用<br/>• 凌晨3点被叫醒改配置"]
P2["❌ 读写分离<br/>• 想让读请求走从库,需要改代码<br/>• 每增加一个从库都要改配置<br/>• 负载均衡逻辑自己写"]
P3["❌ 扩容困难<br/>• 加了新从库,应用不知道<br/>• 要一个个通知应用改配置<br/>• 运维负担重"]
end
服务抽象的解决方案
flowchart TB
subgraph Solutions["✅ 服务抽象的优势"]
Config2["应用配置:db.host = 'pg-test:5433'"]
S1["✅ 故障透明<br/>• 主库故障?HAProxy 自动路由到新主库<br/>• 应用无需改配置,无需重启<br/>• 继续睡觉,系统自己处理"]
S2["✅ 读写分离简单<br/>• 读写用 :5433,只读用 :5434<br/>• 改端口就完成读写分离<br/>• 无需修改业务代码"]
S3["✅ 扩容透明<br/>• 加从库?HAProxy 自动发现并加入负载均衡<br/>• 应用完全无感知<br/>• 运维和开发解耦"]
end
四种默认服务
每个 PostgreSQL 集群自动创建四种服务:
flowchart TB
subgraph Services["🐘 pg-test 集群服务"]
subgraph Primary[":5433 pg-test-primary 读写服务"]
PR1["用途:核心业务读写、OLTP<br/>路由:→ 主库 Pgbouncer → 主库 PostgreSQL<br/>目标:有且仅有一个(当前主库)"]
end
subgraph Replica[":5434 pg-test-replica 只读服务"]
RP1["用途:只读查询、读扩展、报表<br/>路由:→ 从库 Pgbouncer → 从库 PostgreSQL(负载均衡)<br/>目标:所有从库(无从库时回落到主库)"]
end
subgraph Default[":5436 pg-test-default 直连服务"]
DF1["用途:DDL、管理操作、大事务<br/>路由:→ 主库 PostgreSQL(绕过连接池)<br/>特点:不经过 Pgbouncer,适合需要持久连接的场景"]
end
subgraph Offline[":5438 pg-test-offline 离线服务"]
OF1["用途:ETL、数据分析、慢查询<br/>路由:→ 离线库 PostgreSQL(不影响在线业务)<br/>目标:标记为 offline 或 pg_offline_query=true 的实例"]
end
end
服务对比
| 服务 | 端口 | 目标 | 连接池 | 典型用途 |
|---|---|---|---|---|
primary |
5433 | 主库 | ✅ | 核心业务读写 |
replica |
5434 | 从库 | ✅ | 只读查询、报表 |
default |
5436 | 主库 | ❌ | DDL、管理操作 |
offline |
5438 | 离线库 | ❌ | ETL、分析查询 |
接入方式
DNS 接入(推荐)
通过集群域名访问,最简洁的方式:
# 读写业务
psql postgres://app_user:password@pg-test:5433/mydb
# 只读查询
psql postgres://app_user:password@pg-test:5434/mydb
# 管理操作
psql postgres://dba_user:password@pg-test:5436/mydb
# ETL 分析
psql postgres://etl_user:password@pg-test:5438/mydb
VIP 接入
通过虚拟 IP 访问,适合传统应用:
# VIP 自动绑定到主库节点
psql postgres://app_user:password@10.10.10.3:5433/mydb
直连实例
直接访问特定实例,适合调试:
# 通过实例域名
psql postgres://app_user:password@pg-test-1:5433/mydb
# 通过 IP 直连
psql postgres://app_user:password@10.10.10.11:5432/mydb
端口规划
flowchart TB
subgraph Ports["📋 端口分配"]
subgraph DBLayer["🐘 数据库层"]
DB1["5432 PostgreSQL 直连端口"]
DB2["6432 Pgbouncer 连接池端口"]
end
subgraph ServiceLayer["🔀 服务层(HAProxy)"]
SL1["5433 primary 服务(读写,经连接池)"]
SL2["5434 replica 服务(只读,经连接池)"]
SL3["5436 default 服务(直连,绕过连接池)"]
SL4["5438 offline 服务(离线,绕过连接池)"]
end
subgraph MgmtPorts["⚙️ 管理端口"]
MP1["8008 Patroni REST API(健康检查)"]
MP2["9101 HAProxy 管理页面"]
end
end
连接池考量
何时使用连接池(5433/5434)
flowchart TB
subgraph PoolScenario["✅ 适合连接池的场景"]
PS1["• 高频短连接:Web 应用的 API 请求"]
PS2["• 连接数多:数百上千个客户端连接"]
PS3["• 连接复用:减少数据库连接开销"]
PS4["📋 使用端口:5433(读写)/ 5434(只读)"]
end
何时绕过连接池(5436/5438)
flowchart TB
subgraph DirectScenario["⚠️ 需要绕过连接池的场景"]
DS1["• DDL 操作:CREATE TABLE、ALTER TABLE"]
DS2["• 大事务:长时间运行的事务"]
DS3["• Session 变量:SET 命令、临时表"]
DS4["• 管理操作:备份、复制槽管理"]
DS5["📋 使用端口:5436(直连主库)/ 5438(离线库)"]
end
实际场景
标准业务接入
# 应用配置
database:
# 主连接 - 读写业务
primary:
url: postgres://app_user:password@pg-prod:5433/appdb
pool_size: 20
# 只读连接 - 查询业务
replica:
url: postgres://app_user:password@pg-prod:5434/appdb
pool_size: 50
ETL 数据抽取
# 使用离线服务,避免影响在线业务
pg_dump -h pg-prod -p 5438 -U etl_user appdb > backup.sql
DBA 管理操作
# 使用直连服务执行 DDL
psql -h pg-prod -p 5436 -U dba_user -c "CREATE INDEX ..."
故障切换流程
flowchart TB
subgraph Normal["✅ 正常状态"]
N1["应用 → pg-test:5433 → HAProxy → pg-test-1 (Primary)"]
end
Normal --> |pg-test-1 故障| Failover
subgraph Failover["🔄 故障切换过程"]
F1["1. Patroni 检测到主库故障"]
F2["2. pg-test-2 提升为新主库"]
F3["3. HAProxy 健康检查发现角色变化"]
F4["4. 流量自动路由到 pg-test-2"]
F1 --> F2 --> F3 --> F4
end
Failover --> After
subgraph After["✅ 故障后"]
A1["应用 → pg-test:5433 → HAProxy → pg-test-2 (Primary)"]
A2["🎉 应用无需修改连接串,无需重启"]
end
接下来
深入了解服务的细节:
相关话题: