PostgreSQL on Kubernetes 面试题
30 道题- 分类
- 数据库
- 题目数
- 30 道
1 PostgreSQL on Kubernetes 的 Operator 生态总览
答案:
PostgreSQL on Kubernetes 由四款主流 Operator 构成生态,分别代表不同的架构理念与设计哲学。
| Operator | 维护方 | GitHub Stars | 核心架构 | 许可证 |
|---|---|---|---|---|
| CloudNativePG | EDB | 10k+ | Kubernetes Native Operator | Apache 2.0 |
| StackGres | OnGres | 2k+ | All-in-One 平台 | AGPL v3 |
| Zalando Postgres Operator | Zalando | 5k+ | Spilo + Patroni + etcd | MIT |
| Crunchy Data PGO | Crunchy Data | 4k+ | Kubernetes Native Operator | Apache 2.0 |
设计哲学对比:
graph TD
subgraph CloudNativePG
A1["纯 Kubernetes 原生设计"]
A2["无外部依赖(无 Patroni/etcd)"]
A3["Instance Manager 直接管理 PG"]
A4["PVC 直接挂载,无 Operator 层"]
end
subgraph Zalando
B1["Spilo 镜像封装 Patroni"]
B2["依赖 etcd 作为 DCS"]
B3["成熟的 HA 方案(Patroni)"]
B4["需要 Operator + ConfigMap"]
end
选型依据:
| 场景 | 推荐 Operator |
|---|---|
| 纯 Kubernetes 环境,追求原生体验 | CloudNativePG |
| 需要完整平台体验(连接池 / 备份 / 监控一体化) | StackGres |
| 已有 etcd 基础设施,偏好 Patroni HA | Zalando |
| Red Hat OpenShift 环境 | Crunchy Data PGO |
| 最小外部依赖,快速部署 | CloudNativePG |
2 CloudNativePG 的架构与核心设计理念
答案:
CloudNativePG 以 Kubernetes 原生资源模型管理 PostgreSQL 集群,摒弃传统 DBA 工具链的外部依赖,将 PostgreSQL 生命周期完全融入 Kubernetes 控制循环。
核心架构:
graph TD
K8sAPI["Kubernetes API Server"]
K8sAPI -->|"Watch"| Controller["CloudNativePG Controller Manager"]
Controller --> ClusterCtl["Cluster Controller"]
Controller --> BackupCtl["Backup Controller"]
Controller --> ScheduledCtl["ScheduledBackup Controller"]
Controller -->|"管理"| PGPod["PostgreSQL Pod"]
PGPod --> IM["Instance Manager"]
IM --> Lifecycle["Lifecycle Manager"]
IM --> WAL["WAL Archiver"]
IM --> BackupMgr["Backup Manager"]
PGPod --> PGEngine["PostgreSQL 16/17(Primary/Standby)"]
PGPod --> PVC["~~~ PVC(PGDATA + WAL)~~~"]
设计理念:
| 原则 | 说明 |
|---|---|
| No External Dependencies | 不依赖 Patroni / etcd / Stolon,Operator 直接管理 PostgreSQL 进程 |
| Instance Manager | 每个 Pod 内运行 sidecar 进程,负责生命周期、WAL 归档、备份 |
| Declarative State | 通过 Cluster CRD 声明期望状态,Controller 驱动调和 |
| PVC Separation | PGDATA 和 WAL 分别挂载独立 PVC,实现 I/O 隔离 |
| Native Kubernetes RBAC | 通过 ServiceAccount 管理云存储凭证,不依赖 Secret 硬编码 |
| Immutable Infrastructure | 配置变更通过 Pod 重建实现,遵循不可变基础设施原则 |
Instance Manager 职责:
Instance Manager 是每个 PostgreSQL Pod 中的关键组件,负责:PostgreSQL 进程生命周期管理(启动 / 停止 / 信号处理)、WAL 归档到 S3/GCS/Azure Blob、Streaming Replication 主备切换、备份调度与执行、Prometheus 指标暴露。
3 CloudNativePG 的 Cluster CRD 详解
答案:
Cluster CRD 是 CloudNativePG 的核心资源,完整定义 PostgreSQL 集群的拓扑、配置、存储、备份策略。
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-production
spec:
# 实例数量 (Primary + Standby)
instances: 3
# PostgreSQL 镜像与版本
imageName: ghcr.io/cloudnative-pg/postgresql:16.4
# Primary Pod 调度策略
primaryUpdateStrategy: unsupervised
# 存储配置
storage:
size: 100Gi
storageClass: premium-rwo
# WAL 独立存储
walStorage:
size: 20Gi
storageClass: premium-rwo
# PostgreSQL 参数
postgresql:
parameters:
shared_buffers: "4GB"
max_connections: "500"
pg_stat_statements.max: "10000"
log_min_duration_statement: "1s"
# 用于初始化数据库的引用
bootstrap:
initdb:
database: appdb
owner: appuser
secret:
name: pg-credentials
# 监控
monitoring:
enablePodMonitor: true
# 备份
backup:
barmanObjectStore:
destinationPath: s3://pg-backups/production
endpointURL: https://s3.cn-north-1.amazonaws.com.cn
s3Credentials:
accessKeyId:
name: s3-credentials
key: access_key_id
secretAccessKey:
name: s3-credentials
key: secret_access_key
wal:
compression: gzip
encryption: AES256
retentionPolicy: "30d"
关键字段说明:
| 字段 | 说明 | 建议值 |
|---|---|---|
instances | 期望的实例总数 | 生产环境 >= 3 |
primaryUpdateStrategy | Primary 切换策略 | unsupervised(推荐)/ supervised |
storage.size | PGDATA 卷大小 | 根据数据量预估 + 30% 余量 |
walStorage.size | WAL 卷大小 | 按 max_wal_size * 3 计算 |
bootstrap | 初始化数据源 | initdb / recovery / pg_basebackup |
postgresql.parameters | PostgreSQL 配置参数 | 映射 postgresql.conf |
backup.barmanObjectStore | 备份目标配置 | 兼容 Barman Cloud 的对象存储 |
enableSuperuserAccess | 是否启用 superuser | 生产环境设为 false |
logLevel | PostgreSQL 日志级别 | 默认 info,调试用 debug5 |
affinity | Pod 亲和性 / 反亲和性 | 强制跨节点 / 跨可用区分布 |
4 CloudNativePG 的 WAL 归档与 Point-In-Time Recovery
答案:
CloudNativePG 的 Instance Manager 持续将 WAL 段归档到对象存储,支持基于时间点或 WAL 位置的 PITR 恢复。
WAL 归档流程:
sequenceDiagram
participant PG as PostgreSQL Process
participant IM as Instance Manager
participant OS as Object Storage
PG->>PG: 生成 WAL 段(16MB default)
PG->>IM: archive_command
IM->>IM: 压缩 + 加密
IM->>OS: 上传 WAL 段
OS-->>IM: 确认写入
IM-->>PG: 归档成功
Note over PG,OS: .partial WAL(每 5min 或达到 10MB 时)
PG->>IM: partial upload
IM->>OS: 上传 .partial
PITR 恢复配置:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-restored
spec:
instances: 3
storage:
size: 100Gi
bootstrap:
recovery:
source: pg-production-backup-20250801
# 恢复到指定时间点
recoveryTarget:
targetTime: "2025-08-01T14:30:00+08:00"
# 或在恢复后自动提升为 Primary
exclusive: false
# 也可以恢复到指定事务
# recoveryTarget:
# targetXID: "12345678"
externalClusters:
- name: pg-production-backup-20250801
barmanObjectStore:
destinationPath: s3://pg-backups/production
endpointURL: https://s3.cn-north-1.amazonaws.com.cn
s3Credentials:
accessKeyId:
name: s3-credentials
key: access_key_id
secretAccessKey:
name: s3-credentials
key: secret_access_key
WAL 归档关键参数:
| 参数 | 说明 | 生产建议 |
|---|---|---|
wal.compression | WAL 段压缩算法 | gzip(兼顾速度与比例) |
wal.encryption | WAL 段加密算法 | AES256 |
wal.maxParallel | 并发上传线程数 | 4 |
wal.encryptionPassphrase | 加密密钥的 Secret 引用 | 独立 Secret |
retentionPolicy | 备份保留策略 | 30d 或 "COUNT 7" |
PITR 执行时机:
- 人为误操作数据(DROP TABLE / DELETE 无 WHERE / UPDATE 错误)
- 重大版本升级前的回滚保障
- 数据一致性校验失败
- 灾难恢复演练
5 CloudNativePG 的 Backup CRD 与 S3/GCS 备份
答案:
CloudNativePG 通过 Backup CRD 和 ScheduledBackup CRD 管理全量备份,底层基于 Barman Cloud 实现,支持 S3 / GCS / Azure Blob Storage。
Backup CRD 定义:
apiVersion: postgresql.cnpg.io/v1
kind: Backup
metadata:
name: pg-manual-backup-20250801
spec:
cluster:
name: pg-production
# 备份方法:barmanObjectStore 或 volumeSnapshot
method: barmanObjectStore
# barmanObjectStore 配置继承自 Cluster CRD
ScheduledBackup CRD:
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: pg-daily-backup
spec:
# Cron 表达式
schedule: "0 2 * * *"
# 是否立即执行一次
immediate: true
# 挂起调度
suspend: false
cluster:
name: pg-production
# 备份保留策略(覆盖 Cluster 级别设置)
backupOwnerReference: self
备份存储后端配置矩阵:
# S3 兼容存储
s3Credentials:
accessKeyId:
name: s3-credentials
key: access_key_id
secretAccessKey:
name: s3-credentials
key: secret_access_key
region:
name: s3-credentials
key: region
# Google Cloud Storage
googleCredentials:
applicationCredentials:
name: gcs-credentials
key: credentials.json
# Azure Blob Storage
azureCredentials:
connectionString:
name: azure-credentials
key: connection_string
storageAccount:
name: azure-credentials
key: storage_account
备份方法对比:
| 方法 | 原理 | 适用场景 | 恢复方式 |
|---|---|---|---|
barmanObjectStore | pg_basebackup 的热备上传到对象存储 | 常规生产备份 | PITR 恢复(支持时间点) |
volumeSnapshot | 基于 CSI 快照 | K8s 同集群快速恢复 | 快照克隆(不支持 PITR) |
备份验证策略:
| 验证方法 | 说明 |
|---|---|
| 自动验证 | Operator 验证备份文件完整性与可恢复性 |
| 定期恢复演练 | 通过 bootstrap.recovery 从备份创建新集群 |
| 备份监控 | Prometheus 指标 cnpg_backup_status 监控备份成功率 |
6 CloudNativePG 的自动故障转移与 Leader 选举
答案:
CloudNativePG 不依赖 Patroni 或 etcd,直接在 Operator 层与 Instance Manager 之间协同完成故障检测与 Leader 切换。
故障检测与切换流程:
时间线:T+0s ~ T+50s
Operator (Controller) Instance Manager (Primary Pod) Standby Pods
│ │ │
T+0 │ Liveness Probe 探测 Primary │ │
│───────────────────────────────────────►│ │
│◄─────────── 无响应 ────────────────────│ │
│ │ │
T+5 │ 确认 Primary 不健康 │ │
│ │ │
T+10 │ 查询 Standby 复制状态 │ │
│────────────────────────────────────────────────────────────────►│
│◄─── LSN 位置 + 复制延迟 ─────────────────────────────────────────│
│ │ │
T+15 │ 选择 LSN 最超前的 Standby │ │
│ │ │
T+20 │ 发起 pg_ctl promote ───────────────────────────────────────────►│
│ │ Promote │
T+25 │ 更新 Pod Label │ │
│ role: primary → standby │ │
│ role: standby → primary │ role: │
│ │ primary │
T+30 │ 更新 Service Endpoint │ │
│ -rw → 新 Primary │ │
│ │ │
T+35 │ 其他 Standby 重新连接新 Primary │ │
│────────────────────────────────────────────────────────────────►│
│ │◄── streaming repl ────────│
T+40 │ 旧 Primary 重启为 Standby │ │
│ │ │
T+50 │ 集群恢复完整状态 │ │
选举策略:
| 策略 | 说明 |
|---|---|
| Best-Effort 切换 | 选择 LSN 最高、延迟最小的 Standby |
| 同步复制优先级 | 优先选择 synchronous_standby_names 中的 Standby |
| Failover 限制 | failoverDelay 控制两次切换的最小间隔 |
| PrimaryUpdateStrategy | unsupervised:自动切换;supervised:需手动确认 |
不可用时间的控制参数:
| 参数 | 说明 | 默认值 |
|---|---|---|
startDelay | 启动延迟等待 | 3600s |
stopDelay | Smart Shutdown 超时 | 1800s |
smartStopTimeout | Fast Shutdown 前等待 | — |
failoverDelay | 两次 Failover 最小间隔 | 0s |
switchoverDelay | 计划内切换后允许下次切换的延迟 | 0s |
replicationSlots.highAvailability.enabled | 启用高可用复制槽 | true |
Readiness Probe 与 Liveness Probe 策略:
spec:
postgresql:
parameters:
# Liveness: 检查 PG 进程是否存活
# Readiness: 检查 PG 是否接受连接
# 生产环境建议在 init 阶段放宽探针
pg_ctl_timeout: "3600"
7 CloudNativePG 的 Read-Only Replica 扩展
答案:
CloudNativePG 基于 PostgreSQL Physical Streaming Replication 实现只读副本,通过 Service 自动路由读写分离流量。
读写分离架构:
graph TD
subgraph K8s["Kubernetes Service"]
RW["pg-cluster-rw<br/>(读写 Service)<br/>--> Primary Pod"]
RO["pg-cluster-ro<br/>(只读 Service)<br/>--> Standby Pods"]
end
RW -->|"路由"| Primary["Primary<br/>(Read/Write)"]
RO -->|"路由"| Standby["Standby-1 / Standby-2<br/>(Read-Only)"]
Primary -->|"Streaming Replication"| Standby
Service 自动生成规则:
| Service 名称 | 类型 | 路由目标 |
|---|---|---|
<cluster>-rw | ClusterIP | Primary Pod(标签 role=primary) |
<cluster>-ro | ClusterIP | Standby Pods(标签 role=replica) |
<cluster>-r | ClusterIP | 所有 Pod(Primary + Standby) |
<cluster>-any | ClusterIP | 任意 Pod |
扩缩容操作:
# 从 3 实例扩展至 5 实例
kubectl patch cluster pg-production \
--type merge -p '{"spec":{"instances":5}}'
# 查看实例状态
kubectl get cluster pg-production -o jsonpath='{.status.instancesStatus}'
# Cluster CRD 扩缩容配置
spec:
instances: 5 # 从 3 修改为 5
# 控制 Rolling Update 期间不可用实例上限
maxParallelWalSender: 4
复制延迟控制:
| 参数 | 说明 | 建议值 |
|---|---|---|
max_slot_wal_keep_size | 复制槽可保留的 WAL 最大值 | -1(直至磁盘满) |
wal_keep_size | 为 Standby 保留的 WAL 最小值 | 0(改用 replication slot) |
hot_standby_feedback | Standby 反馈长事务信息 | on |
max_standby_streaming_delay | Standby 允许的最大复制延迟 | 30s |
双服务读写分离应用接入:
# 应用配置示例
spring:
datasource:
write:
url: jdbc:postgresql://pg-production-rw:5432/appdb
read:
url: jdbc:postgresql://pg-production-ro:5432/appdb
8 CloudNativePG 的 Rolling Update 与在线升级
答案:
CloudNativePG 通过 Rolling Update 策略实现 PostgreSQL 的在线升级,确保更新过程中始终有 Primary 实例接受写入请求。
Rolling Update 流程:
graph LR
subgraph S1["Step 1: 升级 Standby(C)"]
PA1["Primary(A)<br/>v16.2"]
SB1["Standby(B)<br/>v16.2"]
SC1["Standby(C)<br/>v16.4 (升级)"]
end
subgraph S2["Step 2: 升级 Standby(B)"]
PA2["Primary(A)<br/>v16.2"]
SB2["Standby(B)<br/>v16.4 (升级)"]
SC2["Standby(C)<br/>v16.4"]
end
subgraph S3["Step 3: Switchover"]
SA3["Standby(A)<br/>v16.2 (降级)"]
PB3["Primary(B)<br/>v16.4 (提升)"]
SC3["Standby(C)<br/>v16.4"]
end
subgraph S4["Step 4: 升级旧 Primary(A)"]
SA4["Standby(A)<br/>v16.4 (升级)"]
PB4["Primary(B)<br/>v16.4"]
SC4["Standby(C)<br/>v16.4"]
end
S1 --> S2 --> S3 --> S4
更新触发方式:
# 方式一:修改 imageName 触发 Rolling Update
spec:
imageName: ghcr.io/cloudnative-pg/postgresql:16.4 # 从 16.2 升级
# 方式二:更新 PostgreSQL 参数
spec:
postgresql:
parameters:
shared_buffers: "8GB" # 参数变更重启时滚动更新
# 方式三:更新 Operator 版本支持新特性
控制更新策略:
| 参数 | 说明 | 默认值 |
|---|---|---|
primaryUpdateStrategy | unsupervised:自动完成 Switchover;supervised:需手动发起 | unsupervised |
primaryUpdateMethod | restart:重启 Primary 更新;switchover:先切换再更新 | switchover |
maxParallelWalSender | 并行 WAL Sender 数量上限 | — |
smartShutdownTimeout | Smart Shutdown 超时 | — |
小版本 vs 大版本升级:
| 升级类型 | 方法 | 说明 |
|---|---|---|
| 小版本(16.2 → 16.4) | 修改 imageName 自动 Rolling Update | Operator 自动执行 |
| 大版本(15 → 16) | pg_upgrade 模式 | 需配置 bootstrap.pg_upgrade |
9 CloudNativePG 的 Physical Streaming Replication
答案:
CloudNativePG 基于 PostgreSQL 物理流复制实现主备数据同步,支持异步与同步复制两种模式。
复制架构:
graph LR
subgraph Primary["Primary Pod"]
PG1["PostgreSQL Primary"]
Writer["WAL Writer"]
Sender1["WAL Sender"]
Sender2["WAL Sender"]
PVC1["~~~ WAL PVC ~~~"]
end
subgraph Standby["Standby Pod"]
PG2["PostgreSQL Standby"]
Receiver["WAL Receiver"]
Applier["WAL Apply"]
Startup["Startup Process"]
PVC2["~~~ WAL PVC ~~~"]
end
Writer -->|"Write"| Sender1
Writer -->|"Write"| Sender2
Sender1 -->|"TCP 5432"| Receiver
Sender2 -->|"TCP 5432"| Applier
Receiver --> Applier
Applier --> Startup
同步复制配置:
spec:
instances: 3
postgresql:
parameters:
# 同步复制:至少 1 个 Standby 确认 WAL 写入
synchronous_standby_names: "ANY 1 (*)"
# 同步模式
synchronous:
# 同步实例数量
numberOfInstances: 1
# 是否启用同步复制
enabled: true
# 当同步 Standby 不可用时,降低同步约束
# 防止写入阻塞导致整个集群不可用
复制模式对比:
| 模式 | synchronous_standby_names | 数据安全性 | 写入延迟 | 容灾能力 |
|---|---|---|---|---|
| 异步 | 空 | 可能丢失 WAL 未同步的事务 | 低 | 依赖 WAL 归档恢复 |
| 同步(1 Standby) | ANY 1 (*) | 至少 1 个 Standby 确认 | 中 | 1 个 AZ 故障数据不丢 |
| 同步(2 Standby) | ANY 2 (*) | 至少 2 个 Standby 确认 | 高 | 2 个 AZ 故障数据不丢 |
| 同步(指定 Standby) | FIRST 1 (standby-1) | 指定 Standby 必须确认 | 中 | 特定 Standby 异常阻塞写入 |
复制延迟监测:
-- 查询复制延迟(字节)
SELECT
application_name,
pg_wal_lsn_diff(sent_lsn, write_lsn) AS write_lag_bytes,
pg_wal_lsn_diff(write_lsn, flush_lsn) AS flush_lag_bytes,
pg_wal_lsn_diff(flush_lsn, replay_lsn) AS replay_lag_bytes,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS total_lag_bytes
FROM pg_stat_replication;
10 CloudNativePG 的 PVC 扩展与存储管理
答案:
CloudNativePG 将 PGDATA 和 WAL 分离到独立 PVC,通过 Kubernetes 存储抽象实现动态扩容、快照备份与灾难恢复。
存储架构:
graph TD
subgraph Pod["PostgreSQL Pod"]
PGDATA["PGDATA<br/>/var/lib/postgresql/data"]
PGWAL["PG_WAL<br/>/var/lib/postgresql/wal"]
end
PGDATA -->|"目录"| Base["base/ (数据文件)"]
PGDATA -->|"符号链接"| WalLink["pg_wal/"]
PGDATA -->|"目录"| Stat["pg_stat/ (统计信息)"]
PGDATA --> PVC1["PVC: pgdata-cluster-n<br/>StorageClass: premium-rwo"]
PGWAL -->|"目录"| Archive["archive_status/"]
PGWAL -->|"文件"| WALFiles["*.wal"]
PGWAL --> PVC2["PVC: wal-cluster-n<br/>StorageClass: premium-rwo"]
PVC 扩容:
spec:
storage:
size: 200Gi # 从 100Gi 扩容至 200Gi
storageClass: premium-rwo
# 扩容策略
resizeInUseVolumes: true # 允许在线扩容(需 CSI 驱动支持)
walStorage:
size: 50Gi # WAL 独立扩容
storageClass: premium-rwo
StorageClass 选择策略:
| 场景 | StorageClass | 特点 |
|---|---|---|
| 高性能 OLTP | premium-rwo (SSD/NVMe) | 低延迟 IOPS,适合 PGDATA |
| WAL 分离 | premium-rwo (SSD) | WAL 顺序写,独立磁盘避免争抢 |
| 低成本归档 | standard-rwo (HDD) | 归档数据,成本优先 |
| 快速恢复 | CSI 快照(snapshot.storage.k8s.io) | VolumeSnapshot 秒级克隆 |
| 跨 AZ 复制 | CSI 同步复制卷 | 3 AZ 数据同步 |
存储扩容流程:
1. 修改 Cluster CRD storage.size
2. Operator 更新 StatefulSet volumeClaimTemplate
3. 对于已存在的 PVC:
- Controller Expand:CSI 驱动增加卷容量
- Node Expand:kubelet 调整文件系统大小
- Operator 发送 SIGHUP 给 PG 进程(如果配置了 online resize)
4. 对于新 Pod:直接创建新容量 PVC
VolumeSnapshot 快速恢复:
# 从快照恢复集群
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-from-snapshot
spec:
storage:
size: 100Gi
bootstrap:
initdb:
import:
type: snapshot
snapshot:
name: pg-data-snapshot-20250801
存储容量告警:
spec:
storage:
size: 100Gi
# 当存储使用率达到阈值告警
# Operator 通过 Prometheus 指标暴露存储状态
11 StackGres 的架构与 All-in-One 理念
答案:
StackGres 是 OnGres 开发的企业级 PostgreSQL 平台 Operator,提供数据库、连接池、备份、监控、日志的一体化方案。
All-in-One 架构:
graph TD
subgraph Operator["StackGres Operator"]
SGCluster["SGCluster Controller"]
SGDistributed["SGDistributed Logs Ctl"]
SGScript["SGScript Controller"]
end
Operator -->|"管理"| ClusterPod["StackGres Cluster Pod"]
subgraph ClusterPod["StackGres Cluster Pod"]
subgraph Patroni["Container: patroni"]
PGCore["PostgreSQL Engine (custom build)"]
PatroniHA["Patroni HA Manager"]
Extensions["内建扩展 (150+ extensions)"]
end
subgraph Envoy["Container: envoy (Sidecar)"]
Pool["连接池"]
Traffic["流量管理"]
end
subgraph Util["Container: postgres-util"]
Backup["pgBackRest / WAL-G 备份"]
Exporter["postgres_exporter"]
Fluent["fluent-bit (日志采集)"]
end
PVC["~~~ PVC ~~~"]
end
核心 CRD 一览:
| CRD | 职责 |
|---|---|
SGCluster | 定义 PostgreSQL 集群的拓扑与配置 |
SGInstanceProfile | CPU / 内存资源配置模板 |
SGPostgresConfig | PostgreSQL 参数配置模板 |
SGPoolingConfig | PgBouncer 连接池配置 |
SGBackupConfig | 备份策略与保留策略配置 |
SGDistributedLogs | 分布式日志采集与查询 |
SGScript | SQL 脚本管理与执行 |
SGDbOps | 数据库运维操作(Vacuum / Repack / Benchmark) |
SGCluster 示例:
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
name: pg-production
spec:
instances: 3
postgres:
version: "16.2"
flavor: "babelfish" # 可选:babelfish / postgresql
pods:
persistentVolume:
size: 100Gi
storageClass: premium-rwo
sgInstanceProfile: profile-production
configurations:
sgPostgresConfig: pgconfig-oltp
sgPoolingConfig: poolconfig-default
sgBackupConfig: backupconfig-daily
distributedLogs:
retention: 7d
maxQueueSize: 100MB
prometheusAutobind: true
12 StackGres 的 Envoy Sidecar 连接池
答案:
StackGres 在每个 PostgreSQL Pod 中以 Envoy Sidecar 容器提供连接池,实现应用到数据库的透明连接管理,无需额外部署 PgBouncer 实例。
Envoy Sidecar 架构:
graph LR
subgraph App["Application Pod"]
App2["Application<br/>jdbc:postgresql://cluster:5432"]
end
subgraph SGPod["StackGres Cluster Pod"]
subgraph Envoy2["Container: envoy"]
Filter["PostgreSQL Filter<br/>- 连接池<br/>- 查询路由<br/>- TLS 终止"]
end
subgraph Patroni2["Container: patroni"]
PG2["PostgreSQL<br/>(localhost:5432)"]
end
Filter -->|"转发"| PG2
end
App2 -->|"TCP 5432"| Filter
Envoy 与 PgBouncer 对比:
| 维度 | Envoy Sidecar (StackGres) | PgBouncer (独立部署) |
|---|---|---|
| 部署模型 | Sidecar,与 PG 同 Pod | 独立 Pod 或 Server |
| 配置管理 | SGPoolingConfig CRD | ConfigMap 或配置文件 |
| 连接模式 | Transaction Pooling | Session / Transaction / Statement |
| 负载均衡 | 内建支持 | 依赖 Service |
| TLS | Envoy 原生 TLS | PgBouncer 需额外配置 |
| 监控 | Prometheus 指标 | 通过 exporter 暴露 |
| 查询路由 | 基于 SQL 前缀路由 | 不直接支持 |
SGPoolingConfig 配置:
apiVersion: stackgres.io/v1
kind: SGPoolingConfig
metadata:
name: poolconfig-production
spec:
pgBouncerPgbouncerIni:
pgbouncer:
pool_mode: transaction
max_client_conn: 1000
default_pool_size: 50
reserve_pool_size: 10
reserve_pool_timeout: 5.0
max_db_connections: 200
max_user_connections: 50
server_idle_timeout: 600
query_wait_timeout: 60
client_idle_timeout: 0
stats_period: 60
连接池关键参数说明:
| 参数 | 说明 | 建议值 |
|---|---|---|
pool_mode | 连接池模式 | transaction(推荐) |
max_client_conn | 最大客户端连接数 | 1000 |
default_pool_size | 每用户 / 每数据库连接池大小 | 25~50 |
reserve_pool_size | 预留连接数 | 5~10 |
server_idle_timeout | 服务端空闲连接超时 | 600s |
13 StackGres 的 Babelfish / Patroni / PgBouncer 集成
答案:
StackGres 在基础镜像中预置了 Babelfish(SQL Server 兼容层)、Patroni(HA 管理)和 PgBouncer(连接池),实现开箱即用的企业级功能集成。
集成架构:
graph TD
subgraph Image["StackGres Custom PostgreSQL Image"]
subgraph Engine["PostgreSQL Engine"]
Babelfish["Babelfish (SQL Server)"]
Ext150["150+ Extensions (预装)"]
Plugins["TimescaleDB / PostGIS / etc"]
end
subgraph Patroni3["Patroni"]
LeaderElection["Leader 选举"]
AutoFailover["自动 Failover"]
DCS["DCS 集成"]
RestAPI["REST API"]
end
subgraph PgBouncer["PgBouncer"]
Pool2["连接池"]
QueryRoute["查询路由"]
RateLimit["限流"]
end
end
Babelfish 集成:
Babelfish 是 AWS 开源的 SQL Server 协议兼容层,使 PostgreSQL 能够接受 TDS 协议连接,运行 SQL Server 的 T-SQL 语法。
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
name: pg-babelfish
spec:
postgres:
version: "16.2"
flavor: "babelfish" # 启用 Babelfish
instances: 3
Babelfish 启用后,PostgreSQL 可同时接受 PostgreSQL Wire Protocol(端口 5432)和 TDS Protocol(端口 1433)连接,实现 SQL Server 应用的无改造迁移。
Patroni 集成:
StackGres 使用 Patroni 进行 HA 管理,Patroni 通过 Kubernetes API 作为 DCS(而非 etcd),避免引入外部依赖。
graph TD
subgraph Patroni4["Patroni"]
DCS2["DCS: Kubernetes Endpoints/ConfigMap"]
LeaderLock["Leader Lock: ConfigMap"]
MemberRegistry["Member Registry: Endpoints"]
Config2["Configuration: ConfigMap"]
RestAPI2["REST API: http://localhost:8008"]
GetLeader["GET /leader"]
GetReplica["GET /replica"]
PostSwitchover["POST /switchover"]
PostRestart["POST /restart"]
end
DCS2 --> LeaderLock
DCS2 --> MemberRegistry
DCS2 --> Config2
RestAPI2 --> GetLeader
RestAPI2 --> GetReplica
RestAPI2 --> PostSwitchover
RestAPI2 --> PostRestart
PgBouncer 集成:
PgBouncer 以 Sidecar 形式部署,通过 SGPoolingConfig 声明式配置,支持与 Patroni 协同——Failover 后 PgBouncer 自动重新连接到新的 Primary。
三者协同工作流程:
- 连接建立:客户端连接至 PgBouncer 端口,PgBouncer 复用数据库连接至 Patroni 管理的 PostgreSQL 实例。
- 故障切换:Patroni 检测 Primary 故障并触发 Leader 选举,PgBouncer 检测到连接断开后查询 Patroni REST API 获取新 Primary 地址并重连。
- 读写路由:通过 PgBouncer 配置将读请求路由至 Standby,写请求路由至 Primary(需应用侧配合)。
14 StackGres 的垂直 / 水平扩缩容
答案:
StackGres 通过 SGInstanceProfile 和 SGCluster.instances 分别管理垂直与水平扩缩容,支持在线调整与计划内变更。
垂直扩缩容(CPU / Memory):
apiVersion: stackgres.io/v1
kind: SGInstanceProfile
metadata:
name: profile-production
spec:
cpu: "8"
memory: 32Gi
# 修改 SGInstanceProfile 后,Operator 按 Rolling Update 方式
# 逐个 Pod 重建,使用新资源配置
kubectl patch sginstanceprofile profile-production \
--type merge -p '{"spec":{"cpu":"16","memory":"64Gi"}}'
水平扩缩容(Instance 数量):
apiVersion: stackgres.io/v1
kind: SGCluster
metadata:
name: pg-production
spec:
instances: 5 # 从 3 增至 5
扩缩容操作分类:
| 操作类型 | CRD 字段 | 执行方式 | 服务中断 |
|---|---|---|---|
| CPU/Memory 扩容 | SGInstanceProfile | Rolling Update(逐个 Pod 重建) | 无(用 ReadOnly Service 路由) |
| 磁盘扩容 | SGCluster.pods.persistentVolume.size | 在线扩容(需 CSI 支持) | 无 |
| 实例增加 | SGCluster.instances | 创建新 Pod,自动加入集群 | 无 |
| 实例减少 | SGCluster.instances | 删除 Pod | 仅当删除 Primary 时有短暂中断 |
仅扩容不重建:
StackGres 支持通过 SGDbOps 资源执行指定实例的扩缩容,控制执行时间窗口:
apiVersion: stackgres.io/v1
kind: SGDbOps
metadata:
name: restart-instance-2
spec:
op: restart
sgCluster: pg-production
maxRetries: 1
timeout: 3600
# 仅操作指定 Pod
pods:
- pg-production-2
Scheduling 控制:
spec:
sgInstanceProfile: profile-production
pods:
scheduling:
nodeSelector:
node-type: database
tolerations:
- key: "database"
operator: "Equal"
value: "true"
effect: "NoSchedule"
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- zone-a
- zone-b
15 Zalando Postgres Operator 的架构
答案:
Zalando Postgres Operator 由 Zalando 开发维护,基于 Spilo 镜像与 Patroni HA 方案,依赖 etcd 作为分布式协调服务。
整体架构:
graph TD
K8sAPI["Kubernetes API Server"]
K8sAPI -->|"Watch postgresql CRD"| Operator["Zalando Postgres Operator"]
subgraph Operator["Zalando Postgres Operator"]
Sync["Sync Controller"]
Connection["Connection Pooler"]
Rolling["Rolling Update Manager"]
end
Operator -->|"创建 StatefulSet / Service / Secret"| SpiloPod["Spilo Pod"]
subgraph SpiloPod["Spilo Pod (PostgreSQL)"]
subgraph Patroni5["Patroni"]
HA_Manager["HA Manager"]
RestAPI3["REST API"]
CallbackScript["Callback Script"]
end
subgraph PG["PostgreSQL"]
PrimaryStandby["Primary / Standby"]
Basebackup["pg_basebackup"]
WALArch["WAL-E / WAL-G 归档"]
end
subgraph Etcd["etcd Cluster (独立部署或通过 Operator 集成)"]
LeaderLock2["Leader 锁"]
ConfigStore["配置存储"]
MemberDiscovery["成员发现"]
end
end
Patroni5 -->|"DCS Connection"| Etcd
核心组件职责:
| 组件 | 职责 |
|---|---|
| Operator | 监听 PostgreSQL CRD,创建/更新/删除 StatefulSet、Service、ConfigMap、Secret |
| Spilo | 封装 Patroni + PostgreSQL 的 Docker 镜像,内建备份工具链 |
| Patroni | HA 管理器:Leader 选举、自动 Failover、配置管理、REST API |
| etcd | 分布式协调后端(DCS),存储集群状态、Leader 锁、配置 |
| WAL-E / WAL-G | WAL 归档与备份工具,将 WAL 和 Base Backup 上传至 S3 / GCS |
PostgreSQL CRD 示例:
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: pg-production
spec:
teamId: "sre"
numberOfInstances: 3
postgresql:
version: "16"
parameters:
shared_buffers: "4GB"
max_connections: "500"
volume:
size: 100Gi
storageClass: premium-rwo
patroni:
ttl: 30
loop_wait: 10
retry_timeout: 10
resources:
requests:
cpu: 4
memory: 8Gi
limits:
cpu: 8
memory: 16Gi
16 Zalando Operator 的 Spilo 镜像与 Patroni 集成
答案:
Spilo 是 Zalando 维护的 PostgreSQL Docker 镜像,将 Patroni、PostgreSQL 核心、备份工具链、监控 exporter 等打包为一体。
Spilo 镜像分层结构:
graph TD
subgraph Spilo["Spilo Docker Image"]
subgraph AppLayer["Application Layer"]
Patroni6["Patroni (HA Manager)"]
WALG2["WAL-G (Backup & WAL Archive)"]
PgBackRest["pgBackRest (可选)"]
PgExporter["postgres_exporter"]
PgBouncer2["PgBouncer (可选)"]
Configure["configure_spilo.py (启动脚本)"]
end
subgraph EngineLayer["PostgreSQL Engine"]
PG16["PostgreSQL 12/13/14/15/16/17"]
Contrib["贡献扩展 (Contrib Extensions)"]
PL["PL/Python / PL/Perl / PL/Tcl"]
Plugins2["TimescaleDB / PostGIS / pg_cron"]
end
subgraph BaseLayer["Base OS Layer"]
OS["Ubuntu / Debian"]
Python["Python 3.x"]
end
end
AppLayer --> EngineLayer --> BaseLayer
Patroni 启动流程:
configure_spilo.py (启动入口)
│
├── 1. 检测角色(Primary / Standby)
│ ├── 已有数据目录 → Standby
│ └── 无数据目录 → 检查 DCS 是否有 Leader
│ ├── 有 Leader → pg_basebackup 拉取数据
│ └── 无 Leader → initdb 初始化
│
├── 2. 配置 Patroni
│ ├── 注入 postgresql 参数
│ ├── 设定 DCS 连接(etcd / Kubernetes / ZooKeeper)
│ └── 设定 REST API 监听
│
├── 3. 启动 Patroni
│ └── Patroni 管理 PostgreSQL 进程生命周期
│
└── 4. Patroni 守护循环
├── 每 loop_wait 秒检查 PostgreSQL 健康
├── 通过 DCS 维护 Leader 锁
└── Leader 故障时触发 Failover
Spilo 环境变量配置:
| 环境变量 | 说明 | 示例值 |
|---|---|---|
SCOPE | 集群名称 | pg-production |
PGVERSION | PostgreSQL 主版本 | 16 |
WAL_S3_BUCKET | WAL 归档 S3 桶 | s3://pg-wal-archive |
WAL_GCS_BUCKET | WAL 归档 GCS 桶 | gs://pg-wal-archive |
BACKUP_SCHEDULE | 备份 Cron 表达式 | 00 02 * * * |
BACKUP_NUM_TO_RETAIN | 备份保留数量 | 7 |
USE_PA16 | 是否使用 pgAudit 1.6 | true |
Patroni REST API 端点:
| 端点 | 方法 | 说明 |
|---|---|---|
/leader | GET | 返回当前 Leader 信息 |
/replica | GET | 返回 Standby 延迟信息 |
/switchover | POST | 计划内主备切换 |
/restart | POST | 重启 PostgreSQL |
/reinitialize | POST | 重新初始化 Standby |
/patroni | GET | Patroni 状态信息 |
/history | GET | 切换历史记录 |
17 Zalando Operator 的 etcd 作为 DCS
答案:
Zalando Postgres Operator 依赖 etcd 作为分布式一致性存储(DCS),存储集群 Leader 锁、配置和成员信息。
etcd 在 HA 架构中的作用:
graph TD
subgraph Etcd2["etcd Cluster"]
Root["/service/scope/"]
Root --> Leader2["leader - Leader 锁(TTL 续约)"]
Root --> Members["members/ - 成员注册与状态"]
Members --> Member0["member-0"]
Members --> Member1["member-1"]
Root --> Config3["config/ - 集群配置"]
Config3 --> PGParams["postgresql.parameters"]
Config3 --> LoopWait["patroni.loop_wait"]
Root --> Init["initialize - 初始化标记"]
Root --> History["history - 切换历史"]
Root --> Optime["optime/ - 最近 Leader 墙钟时间"]
Optime --> LeaderTime["leader"]
end
Leader 选举与 Lock 机制:
时间线:Patroni 守护循环
T+0s Standby: GET /service/scope/leader → 存在(Primary-0)
T+1s Primary-0: PUT /service/scope/leader (renew TTL=30s)
--- Primary-0 故障 ---
T+30s 租约过期,Leader Key 自动删除
T+31s Standby-1: GET /service/scope/leader → 不存在
T+32s Standby-1: PUT /service/scope/leader (CAS, value=Standby-1)
→ 写入成功 → Standby-1 提升为 Primary
T+33s Standby-2: PUT /service/scope/leader (CAS)
→ 写入失败(已被 Standby-1 持有)
T+34s Standby-1: pg_ctl promote → 完成提升
etcd 部署模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 独立 etcd Cluster | 专用 etcd 集群,3/5 节点 | 多 PostgreSQL 集群共享,生产推荐 |
| K8s 内嵌 etcd | 使用 K8s API Server 的 etcd | PoC / 开发环境 |
| Operator 管理 etcd | Zalando Operator 自带 etcd Operator | 简化部署 |
| ZooKeeper / Consul | Patroni 同样支持 | 已有基础设施 |
etcd DCS vs Kubernetes DCS(CloudNativePG):
| 维度 | etcd DCS | Kubernetes DCS |
|---|---|---|
| 额外组件 | 需独立部署 etcd(3~5 节点) | 无(利用 K8s API) |
| 故障域 | 依赖 etcd 可用性 | 依赖 K8s API Server 可用性 |
| Leader 锁 | etcd Key TTL + CAS | ConfigMap / Endpoints |
| 配置管理 | etcd Key/Value | ConfigMap / Secret |
| 运维复杂度 | 需管理 etcd 集群 | 无额外运维 |
| 适用规模 | 大规模(数百 PG 集群共享 etcd) | 小到中规模 |
18 PostgreSQL 在 K8s 上的备份策略
答案:
PostgreSQL on Kubernetes 的备份体系由物理备份、WAL 归档和快照三种方式组成,分别适用于不同恢复目标。
备份工具对比:
| 工具 | 备份类型 | 存储后端 | 恢复能力 | Operator 集成 |
|---|---|---|---|---|
| WAL-G | 物理 + WAL | S3 / GCS / Azure / Swift / FS | PITR(任意时间点) | Zalando / StackGres |
| pgBackRest | 物理 + WAL | S3 / GCS / Azure / NFS / POSIX | PITR + 增量 / 差异备份 | CloudNativePG / StackGres(Barman Cloud) |
| Barman Cloud | 物理 + WAL | S3 / GCS / Azure | PITR(barmanObjectStore) | CloudNativePG 原生支持 |
| pg_basebackup | 物理全量 | POSIX / tar / 流 | 全量恢复 | 基础工具 |
| Volume Snapshot | 块级别快照 | CSI Snapshot | 快照点恢复 | CloudNativePG(VolumeSnapshot) |
WAL-G 备份流程:
graph TD
Start["pg_backup_start() 标记开始"]
Start --> FullBackup["全量备份<br/>- pg_basebackup<br/>- 或 tar 打包"]
FullBackup --> Push["WAL-G push<br/>上传至对象存储"]
Start --> Stop["pg_backup_stop() 标记结束"]
Stop --> WalPush["WAL-G wal-push<br/>增量 WAL 上传"]
Push --> Stop
NoteRight["Note: archive_command 持续推送 WAL<br/>→ WalPush"]
CloudNativePG 备份配置:
# 全量备份 + WAL 归档
spec:
backup:
barmanObjectStore:
destinationPath: s3://pg-backups/production
endpointURL: https://s3.cn-north-1.amazonaws.com.cn
s3Credentials:
accessKeyId:
name: s3-credentials
key: access_key_id
secretAccessKey:
name: s3-credentials
key: secret_access_key
wal:
compression: gzip
encryption: AES256
data:
compression: gzip
encryption: AES256
# 立即执行一次 Checkpoint 保证一致性
immediateCheckpoint: false
retentionPolicy: "30d"
备份策略金字塔:
graph TD
L1["异地灾备<br/>跨区域备份<br/>S3/GCS 跨区域复制<br/>每周 1 次 / 保留 3 个月"]
L2["快照<br/>Volume Snapshot<br/>每天 1 次 / 保留 7 天"]
L3["全量 + WAL<br/>barmanObjectStore / WAL-G<br/>PITR 恢复<br/>每天全量 + 持续 WAL / 保留 30 天"]
L1 --> L2 --> L3
备份验证闭环:
| 验证步骤 | 频率 | 自动化方式 |
|---|---|---|
| 备份文件完整性校验 | 每次备份后 | Operator 自动验证 |
| 恢复至临时集群 | 每周 | bootstrap.recovery / CronJob |
| PITR 恢复测试 | 每月 | 指定时间点恢复至测试集群 |
| 数据一致性校验 | 恢复后 | pg_checksums / amcheck |
19 PostgreSQL 的复制槽(Replication Slot)管理
答案:
Replication Slot 是 PostgreSQL 保证 Standby 不丢失 WAL 的机制,在 Kubernetes 动态环境中需特别关注 WAL 堆积和 Failover 后的槽位处理。
复制槽类型:
| 类型 | 用途 | 创建方式 |
|---|---|---|
| Physical Slot | Streaming Replication | pg_create_physical_replication_slot() |
| Logical Slot | 逻辑复制 / CDC | pg_create_logical_replication_slot() |
| Temporary Slot | 临时 Base Backup | pg_basebackup -S(用完即删) |
复制槽生命周期与 WAL 保留:
Primary 上的 WAL 保留行为:
有活跃 Replication Slot
WAL 保留到所有 Slot 的 restart_lsn 之后
→ 风险:Standby 长时间宕机导致 Primary WAL 堆积磁盘满
无 Replication Slot
WAL 保留受 wal_keep_size 限制
→ 风险:Standby 延迟超过 wal_keep_size 后无法追上,需重新 pg_basebackup
CloudNativePG 复制槽管理:
spec:
replicationSlots:
# 启用高可用复制槽(每个 Standby 自动创建)
highAvailability:
enabled: true
# Slot 前缀
slotPrefix: _cnpg_
# 当 Standby 被删除时自动清理对应 Slot
synchronizeReplicas: true
# 更新间隔
updateInterval: 30
WAL 堆积的预防措施:
| 参数 | 说明 | 建议值 |
|---|---|---|
max_slot_wal_keep_size | 单个 Slot 可保留的 WAL 最大大小 | -1(不限制,靠监控兜底) |
wal_keep_size | 为所有 Standby 保留的 WAL 总量 | 0(使用 Slot 管理) |
max_wal_size | 自动 Checkpoint 时 WAL 总量上限 | 4GB(避免单个 WAL 段堆积) |
archive_timeout | 强制 WAL 段切换间隔 | 300s(5min) |
监控与告警:
-- 监控复制槽 WAL 延迟
SELECT
slot_name,
active,
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS retention_bytes,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retention_pretty
FROM pg_replication_slots
WHERE NOT temporary;
Failover 后的 Slot 清理流程:
1. 旧 Primary 故障
2. 新 Primary 提升(Standby 上的 Slot 变为可用)
3. 旧 Primary 恢复为 Standby
4. 新 Primary 创建与新 Standby 对应的 Slot
5. 旧 Primary 上的旧 Slot 通过 synchronizeReplicas: true 自动清理
20 PostgreSQL 的 Vacuum 与 AutoVacuum 在 K8s 下的调优
答案:
Vacuum 是 PostgreSQL MVCC 机制的核心维护操作,在容器化环境需要针对资源限制、I/O 节流和调度策略进行专门调优。
MVCC 与 Vacuum 机制回顾:
graph TD
Update["UPDATE 操作"]
Update --> OldTuple["旧版本元组 (Dead Tuple)<br/>标记为无效"]
Update --> NewTuple["新版本元组 (Live Tuple)<br/>新插入"]
OldTuple --> Vacuum["VACUUM: 回收 Dead Tuple 空间"]
NewTuple --> Vacuum
Vacuum --> FSM["更新 FSM (空闲空间映射)"]
Vacuum --> VM["更新 VM (可见性映射)"]
Vacuum --> Freeze["冻结事务 ID (防止回卷)"]
AutoVacuum 核心参数:
# postgresql.conf / Cluster CRD parameters
autovacuum = on
# 触发阈值
autovacuum_vacuum_threshold = 50 # 最小 Dead Tuple 数
autovacuum_vacuum_scale_factor = 0.1 # 表大小的 10% Dead Tuple
autovacuum_analyze_threshold = 50
autovacuum_analyze_scale_factor = 0.05
# 成本限制(I/O 节流)
autovacuum_vacuum_cost_delay = 2ms # 每次 Cost 到达后暂停
autovacuum_vacuum_cost_limit = 200 # 每次暂停前的 Cost 上限
# Worker 并发
autovacuum_max_workers = 5 # 最大同时运行的 Worker 数
# 大表优化
autovacuum_vacuum_insert_threshold = 1000 # Insert-Only 表触发阈值
autovacuum_vacuum_insert_scale_factor = 0.1 # Insert-Only 表触发比例
K8s 环境下的特殊考量:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| VACUUM 被 OOM Kill | maintenance_work_mem 过高 + K8s memory limit | maintenance_work_mem <= memory limit 的 25% |
| I/O 争抢导致延迟 | VACUUM 产生大量 I/O,与其他查询竞争 | 降低 cost_limit,提高 cost_delay |
| 长事务阻塞 VACUUM | 存在未提交事务,VACUUM 无法回收旧版本 | 监控 oldest xmin,idle_in_transaction_session_timeout = 300s |
| Insert-Only 表膨胀 | 默认 VACUUM 只由 UPDATE/DELETE 触发 | PG13+ 设置 autovacuum_vacuum_insert_threshold |
| 容器重启 VACUUM 中断 | 重启后 Autovacuum 重新调度 | 设置 autovacuum_naptime = 30s 缩短调度间隔 |
K8s 环境下 Containerd / Docker 的特殊问题:
OverlayFS 等容器存储驱动可能导致大量 VACUUM FULL 操作性能下降。建议:
- PGDATA 挂载 PVC 而非容器层,避免 Copy-on-Write 开销
- 使用
local-ssd或高性能 CSI 驱动减少 I/O 放大 - 避免在容器镜像层存储数据库文件
Autovacuum 监控 SQL:
-- 查看表膨胀比例
SELECT
schemaname || '.' || relname AS table_name,
n_dead_tup,
n_live_tup,
round(n_dead_tup * 100.0 / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_ratio,
last_vacuum,
last_autovacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC
LIMIT 20;
21 PostgreSQL 的连接池配置(PgBouncer / Odyssey)
答案:
PostgreSQL 采用进程模型(每连接一个 Backend Process),在微服务和云原生架构中必须引入连接池来缓解连接数压力。
连接池架构位置:
graph TD
subgraph Apps["Application Pods (100+ instances)"]
App1["App-1"]
App2["App-2"]
App3["App-N"]
end
Apps -->|"大量短连接"| PgBouncer3["PgBouncer Pod<br/>(Connection Pool)<br/>Sidecar 或独立 Pod<br/><br/>Client Pool: 1000<br/>Server Pool: 50"]
PgBouncer3 -->|"少量长连接"| PG3["PostgreSQL Pod<br/>max_connections: 200"]
PgBouncer vs Odyssey 对比:
| 维度 | PgBouncer | Odyssey |
|---|---|---|
| 开发语言 | C | C |
| 连接模式 | Session / Transaction / Statement | Transaction |
| 多线程 | 单线程(多进程) | 多线程 |
| TLS | 支持,需编译选项 | 内建支持,性能更好 |
| SCRAM 认证 | 支持 | 支持 |
| 查询路由 | 不支持 | 支持(根据 SQL 规则路由到读/写) |
| 事务池限流 | 不支持 | 支持(per-user / per-database) |
| 存储后端 | 文件 | 文件 + PostgreSQL(集中管理) |
| 性能 | 极轻量,适合高并发 | 更强扩展性,适合复杂路由 |
PgBouncer 核心配置:
[databases]
* = host=localhost port=5432
[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
# 连接池模式
pool_mode = transaction
# 连接数控制
max_client_conn = 1000
default_pool_size = 25
min_pool_size = 5
reserve_pool_size = 10
reserve_pool_timeout = 5.0
max_db_connections = 0
max_user_connections = 0
# 超时与生命周期
server_idle_timeout = 600
server_lifetime = 3600
server_connect_timeout = 15
client_idle_timeout = 0
query_wait_timeout = 60
query_timeout = 0
idle_transaction_timeout = 0
# 管理
admin_users = admin
stats_users = stats
stats_period = 60
K8s 部署模式对比:
| 模式 | 优点 | 缺点 |
|---|---|---|
| Sidecar(与 App 同 Pod) | 低延迟,无额外网络跳,服务发现简单 | 池化收益分散,每 Pod 独立维护连接池 |
| DaemonSet | 每节点一个,资源开销固定 | 应用需配置 Node IP |
| 独立 Service | 池化效果好,统一管理 | 额外网络跳,需高可用 |
| StackGres Envoy | 与 PG Pod Sidecar,零额外运维 | 仅 StackGres 生态 |
连接池模式选择:
| 模式 | 行为 | 适用场景 |
|---|---|---|
session | 一个客户端连接对应一个服务端连接,持续整个会话 | 需要 SET / LISTEN / 游标等会话状态 |
transaction | 仅在事务期间占用服务端连接 | 无状态短连接,推荐 |
statement | 仅在单条语句期间占用连接 | 极端场景,不支持多语句事务 |
22 PostgreSQL 的监控与 Prometheus
答案:
PostgreSQL on Kubernetes 通过 postgres_exporter 或 CloudNativePG / StackGres 内建指标暴露,将运行指标接入 Prometheus 体系。
监控架构:
graph TD
subgraph Stack["Prometheus Stack"]
Prometheus["Prometheus<br/>(Scrape + TSDB)"]
Grafana["Grafana<br/>(Dashboard + Alert)"]
Prometheus --> Grafana
end
Prometheus -->|"Scrape /metrics"| Exporter2["postgres_exporter<br/>:9187/metrics<br/>Sidecar 容器"]
Exporter2 -->|"SQL Query"| PG4["PostgreSQL"]
subgraph PG4["PostgreSQL"]
PgStatStatements["pg_stat_statements"]
PgStatActivity["pg_stat_activity"]
PgStatReplication["pg_stat_replication"]
end
CloudNativePG 原生 Prometheus 集成:
spec:
monitoring:
enablePodMonitor: true
# 自定义 PodMonitor 标签
podMonitor:
labels:
app: prometheus
additionalLabels:
team: sre
CloudNativePG Instance Manager 内建 Prometheus 端点:
- 端口 9187,路径
/metrics - 暴露 PostgreSQL 运行指标、备份状态、复制延迟
StackGres Prometheus 集成:
spec:
prometheusAutobind: true
StackGres 自动创建 PodMonitor,并预置 Grafana Dashboard。
关键监控指标:
| 指标类别 | 关键指标 | 阈值建议 |
|---|---|---|
| 连接 | pg_stat_activity count | > 80% max_connections 告警 |
| 复制延迟 | pg_stat_replication replay_lag | > 30s 告警 |
| 长事务 | pg_stat_activity xact_start | > 5min 告警 |
| 锁等待 | pg_locks granted=false | > 10s 告警 |
| 慢查询 | pg_stat_statements mean_exec_time | > 1s 告警 |
| Dead Tuple | pg_stat_user_tables n_dead_tup | > 10M 或 > 20% 告警 |
| WAL 堆积 | pg_replication_slots restart_lsn | > max_wal_size 告警 |
| 磁盘使用 | cnpg_pgdata_size / PVC | > 80% 告警 |
pg_stat_statements 配置:
spec:
postgresql:
parameters:
shared_preload_libraries: "pg_stat_statements"
pg_stat_statements.track: "all"
pg_stat_statements.max: "10000"
pg_stat_statements.track_utility: "off"
核心 PromQL 告警规则:
# 连接数告警
- alert: HighConnections
expr: |
pg_stat_database_numbackends{datname="appdb"}
/ on(instance) group_left
pg_settings_max_connections * 100 > 80
for: 5m
# 复制延迟告警
- alert: ReplicationLag
expr: pg_stat_replication_replay_lag_bytes > 104857600 # 100MB
for: 2m
# 长事务告警
- alert: LongRunningTransaction
expr: pg_stat_activity_max_tx_duration_seconds > 300
for: 1m
23 PostgreSQL 的 pgAudit 审计配置
答案:
pgAudit 是 PostgreSQL 的审计扩展,记录 DDL、DML、ROLE 等操作的详细日志,在 K8s 环境中通过 shared_preload_libraries 和 Cluster CRD 参数启用。
pgAudit 架构:
graph TD
Query["查询执行"]
Query --> PgAudit["pgAudit Extension"]
subgraph PgAudit["pgAudit Extension"]
EventFilter["Event Class Filter<br/>READ/WRITE/ROLE/DDL"]
ObjectFilter["Object Filter<br/>table/role/schema..."]
AuditWriter["Audit Log Writer<br/>(csvlog / syslog)"]
EventFilter --> ObjectFilter --> AuditWriter
end
AuditWriter --> LogOutput["log_statement / log_destination"]
CloudNativePG 中的 pgAudit 配置:
spec:
postgresql:
parameters:
# 预加载 pgAudit
shared_preload_libraries: "pg_stat_statements, pgaudit"
# 审计配置
pgaudit.log: "write, ddl, role"
pgaudit.log_catalog: "off"
pgaudit.log_level: "log"
pgaudit.log_parameter: "on"
pgaudit.log_relation: "on"
pgaudit.log_rows: "off"
pgaudit.log_statement_once: "off"
# 角色级审计
pgaudit.role: "auditor"
# 日志输出
log_destination: "csvlog"
logging_collector: "on"
log_directory: "pg_log"
log_filename: "postgresql-%a.log"
log_rotation_age: "1d"
log_rotation_size: "0"
pgAudit 日志分类:
| 类 | 审计范围 | 生产配置 |
|---|---|---|
read | SELECT / COPY TO | 按需启用(量大,可能影响性能) |
write | INSERT / UPDATE / DELETE / COPY FROM / TRUNCATE | 始终启用 |
function | 函数调用和 DO 块 | 按需启用 |
role | GRANT / REVOKE / CREATE/ALTER/DROP ROLE | 始终启用 |
ddl | 除 ROLE 外的所有 DDL | 始终启用 |
misc | DISCARD / FETCH / CHECKPOINT 等 | 按需启用 |
misc_set | SET 命令 | 按需启用 |
审计日志采集架构(K8s 环境):
graph TD
PA["pgAudit"]
CSV["csvlog → /var/lib/postgresql/data/pg_log/"]
PVC["PVC (PGDATA)"]
Sidecar["Sidecar: fluent-bit / filebeat"]
Tail["tail pg_log/*.csv"]
Parse["解析 CSV → JSON"]
Backend["Elasticsearch / Loki / Kafka"]
PA --> CSV --> PVC
CSV --> Sidecar --> Tail --> Parse --> Backend
审计日志集中采集(Loki + Promtail):
# Promtail DaemonSet 或 Sidecar 采集 PostgreSQL 日志
scrape_configs:
- job_name: postgresql-audit
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_cnpg_io_cluster]
action: keep
regex: pg-production
pipeline_stages:
- regex:
expression: '^(?P<audit_type>AUDIT):.*STATEMENT:(?P<statement>.*)'
- labels:
audit_type:
24 PostgreSQL 的逻辑复制与 CDC
答案:
PostgreSQL 逻辑复制(Logical Replication)基于发布 / 订阅模型,以表为单位选择性同步数据变更,是实现 CDC 的核心机制。
物理复制 vs 逻辑复制:
| 维度 | Physical Replication | Logical Replication |
|---|---|---|
| 复制粒度 | 整个实例(块级别) | 表级别(行级别) |
| DDL 同步 | 自动同步 | 不同步 |
| 跨版本复制 | 不可跨大版本 | 可跨版本 |
| 写入能力 | Standby 为只读 | 订阅端可写 |
| 复制协议 | WAL Streaming | Logical Decoding + WAL Sender |
| 使用场景 | HA / 读写分离 / 灾备 | CDC / 数据迁移 / 大版本升级 |
逻辑复制架构:
graph LR
subgraph Publisher[Publisher 源端]
A[WAL Decoder<br/>pgoutput]
B[Publication<br/>- orders<br/>- payments]
A --> B
Slot1[Logical Replication Slot<br/>orders_slot] -.-> A
end
subgraph Subscriber[Subscriber 目标端]
C[Logical Replay Worker]
D[Subscription<br/>- orders_sub<br/>CONNECTION ...]
C --> D
Slot2[Logical Replication Slot<br/>目标端内部] -.-> C
end
A -->|TCP 5432| C
发布与订阅配置:
-- 源端:创建发布
CREATE PUBLICATION orders_pub FOR TABLE orders, payments
WITH (publish = 'insert,update,delete');
-- 目标端:创建订阅
CREATE SUBSCRIPTION orders_sub
CONNECTION 'host=pg-source-rw dbname=appdb user=replicator password=xxx'
PUBLICATION orders_pub
WITH (
copy_data = true,
create_slot = true,
enabled = true,
synchronous_commit = 'off'
);
CloudNativePG 下的逻辑复制部署:
逻辑复制部署在 Kubernetes 上可创建独立的 Cluster 实例,通过发布 / 订阅实现跨集群数据同步,或用作大版本升级的中间跳板。
# 源集群(Publisher)
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-source
spec:
instances: 3
postgresql:
parameters:
wal_level: "logical"
max_replication_slots: "10"
max_wal_senders: "10"
# 目标集群(Subscriber)
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-target
spec:
instances: 3
postgresql:
parameters:
wal_level: "logical"
max_replication_slots: "10"
max_logical_replication_workers: "8"
max_worker_processes: "16"
CDC 工具链对比:
| 工具 | 原理 | 输出格式 | 适用场景 |
|---|---|---|---|
| Debezium | Kafka Connect + pgoutput | Kafka / JSON / Avro | 实时数据管道 |
| pg_recvlogical | 原生逻辑解码客户端 | 自定义 | 轻量 CDC |
| WAL2JSON | Logical Decoding 插件,输出 JSON | JSON | Debezium 后端 |
| pglogical | 扩展插件,兼容 9.4+ | 向下兼容逻辑复制 | 旧版本迁移 |
逻辑复制注意事项:
| 问题 | 说明 | 解决 |
|---|---|---|
| Sequence 不同步 | 逻辑复制不复制 Sequence | 迁移后手动更新 setval() |
| DDL 变更 | ALTER TABLE 不会传播 | 需在两端分别执行 |
| Large Object | 不支持 | 避免使用或单独迁移 |
| 冲突检测 | 订阅端写入可能与复制数据冲突 | 设置订阅为 origin=none |
| 复制槽膨胀 | 订阅端断连导致 WAL 堆积 | 监控 pg_replication_slots |
25 PostgreSQL 的多租户隔离方案
答案:
PostgreSQL on Kubernetes 提供从数据库级到集群级的多层次多租户隔离方案。
隔离方案对比:
graph TD
subgraph L0[Level 0: Schema 隔离]
PG0[PostgreSQL Instance]
PG0 --> TA[tenant_a]
PG0 --> TB[tenant_b]
end
subgraph L1[Level 1: Database 隔离]
PG1[PostgreSQL Instance]
PG1 --> DA[db_tenant_a]
PG1 --> DB[db_tenant_b]
end
subgraph L2[Level 2: Cluster 隔离]
CA[Cluster A<br/>namespace-A]
CB[Cluster B<br/>namespace-B]
end
subgraph L3[Level 3: Operator / Node 隔离]
OA[Operator-A<br/>NodeGroup-A]
OB[Operator-B<br/>NodeGroup-B]
end
L0 --> L1 --> L2 --> L3
| Level | 方案 | 隔离度 | 成本 |
|---|---|---|---|
| 0 | Schema 隔离 | 最低 | 最低 |
| 1 | Database 隔离 | 中 | 低 |
| 2 | Cluster 隔离 | 高 | 中 |
| 3 | Operator/Node 隔离 | 最高 | 最高 |
各方案详细对比:
| 维度 | Schema 隔离 | Database 隔离 | Cluster 隔离 | Operator/Node 隔离 |
|---|---|---|---|---|
| 资源隔离 | 无 | 部分(pg_stat_database) | 完全(CPU/Memory/IO) | 物理级别 |
| 连接隔离 | 无 | 中等(每 Database 连接数) | 完全独立连接池 | 物理级别 |
| 安全隔离 | 低(跨 Schema 查询可绕过) | 中等(跨 Database 不能直接访问) | 高(独立网络策略) | 物理级别 |
| 性能隔离 | 低(共享 Buffer Pool) | 中等(共享 shared_buffers) | 高(独立 Buffer Pool) | 完全隔离 |
| 备份粒度 | 全实例 | Database 级别(pg_dump) | Cluster 级别 | Cluster 级别 |
| 故障爆炸半径 | 整个实例 | 整个实例 | 单个集群 | 最小 |
| 运维成本 | 低 | 低 | 中 | 高 |
| 适用于 | 内部工具 / 开发环境 | 小型 SaaS | 生产环境 SaaS | 金融 / 安全合规 |
CloudNativePG 多租户部署最佳实践:
# 生产环境:每租户一个 Cluster(Namespace 隔离)
apiVersion: v1
kind: Namespace
metadata:
name: tenant-acme-db
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-acme
namespace: tenant-acme-db
spec:
instances: 3
storage:
size: 50Gi
storageClass: premium-rwo
postgresql:
parameters:
shared_buffers: "2GB"
max_connections: "200"
resources:
requests:
cpu: "2"
memory: 4Gi
limits:
cpu: "4"
memory: 8Gi
资源配额与限制策略:
# Namespace 级别 ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
name: tenant-acme-quota
namespace: tenant-acme-db
spec:
hard:
requests.cpu: "8"
requests.memory: 32Gi
requests.storage: 500Gi
persistentvolumeclaims: "5"
26 PostgreSQL 的跨可用区 / 跨区域部署
答案:
PostgreSQL on Kubernetes 通过节点亲和性、拓扑分布约束和复制拓扑实现跨可用区(AZ)和跨区域高可用部署。
跨 3 AZ 高可用拓扑:
graph TD
subgraph Region[Region: cn-north-1]
subgraph AZ_A[Availability Zone A]
N1[Node-1]
P[Primary<br/>同步复制]
N1 --> P
end
subgraph AZ_B[Availability Zone B]
N3[Node-3]
S1[Standby-1<br/>同步确认]
N3 --> S1
end
subgraph AZ_C[Availability Zone C]
N5[Node-5]
S2[Standby-2<br/>异步复制]
N5 --> S2
OS[S3/GCS 对象存储<br/>WAL Archive<br/>+ BaseBackup]
end
P -->|WAL| S1
S1 -->|WAL| S2
S2 -->|WAL| OS
end
CloudNativePG 跨 AZ 配置:
spec:
instances: 3
# 拓扑分布约束:Pod 强制分布到不同 Zone
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
cnpg.io/cluster: pg-production
# 节点亲和性:仅调度到数据库专用节点
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/database
operator: Exists
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
cnpg.io/cluster: pg-production
topologyKey: kubernetes.io/hostname
# 同步复制:至少 1 个 Standby 确认
postgresql:
synchronous:
numberOfInstances: 1
同步复制与延迟权衡:
跨 AZ 同步复制延迟模型:
Primary (AZ-A) ──WAL──► Standby-1 (AZ-B, 同步)
│ │
│ RTT: ~2ms │ 确认返回: 2ms
│ │
│◄──── 同步确认 ──────────┘
│
│ 事务提交延迟 = 本地写入 + 2ms RTT
Primary (AZ-A) ──WAL──► Standby-2 (AZ-C, 异步)
│ RTT: ~2ms
│ 不等待确认
| 配置 | 数据安全性 | 写入延迟 | 适用场景 |
|---|---|---|---|
| 异步(所有 Standby) | 可能丢失事务 | 最低 | 读多写少 / 非关键业务 |
| 同步 1 个 Standby | 至少 1 个 Standby 确认 | +2~5ms | 一般 OLTP |
| 同步 2+ Standby | 多 AZ 确认 | +4~10ms | 金融 / 核心交易 |
| 跨 Region 异步 | 可能丢失近期事务 | +50~200ms | 异地灾备 |
跨区域灾备(DR):
# Region-B 灾备集群,通过 WAL 归档恢复
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-dr-region-b
spec:
instances: 3
bootstrap:
recovery:
source: pg-production-wal-archive
externalClusters:
- name: pg-production-wal-archive
barmanObjectStore:
destinationPath: s3://pg-dr-backups/production
endpointURL: https://s3.cn-south-1.amazonaws.com.cn
s3Credentials:
accessKeyId:
name: s3-dr-credentials
key: access_key_id
secretAccessKey:
name: s3-dr-credentials
key: secret_access_key
27 PostgreSQL Operator 对比矩阵
答案:
四款主流 PostgreSQL Operator 在架构、功能、运维复杂度方面存在显著差异。
综合对比矩阵:
┌─────────────────────┬──────────────┬──────────────┬─────────────┬──────────────┐
│ 维度 │ CloudNativePG│ StackGres │ Zalando │ PGO (Crunchy)│
├─────────────────────┼──────────────┼──────────────┼─────────────┼──────────────┤
│ 维护方 │ EDB │ OnGres │ Zalando │ Crunchy Data │
│ 许可证 │ Apache 2.0 │ AGPL v3 │ MIT │ Apache 2.0 │
│ 首次发布 │ 2021 │ 2020 │ 2017 │ 2022 (v5) │
├─────────────────────┼──────────────┼──────────────┼─────────────┼──────────────┤
│ 外部依赖 │ 无 │ 无 │ etcd │ 无 │
│ HA 方案 │ 自研 Manager │ Patroni │ Patroni │ Patroni │
│ DCS │ K8s API │ K8s API │ etcd │ K8s API │
│ 连接池 │ 需自行部署 │ Envoy Sidecar│ PgBouncer CR│ PgBouncer CR │
│ 备份方案 │ Barman Cloud │ pgBackRest │ WAL-G │ pgBackRest │
│ 备份目标 │ S3/GCS/Azure │ S3/GCS/Azure │ S3/GCS │ S3/GCS/Azure │
├─────────────────────┼──────────────┼──────────────┼─────────────┼──────────────┤
│ PITR │ ✓ │ ✓ │ ✓ │ ✓ │
│ Volume Snapshot │ ✓ │ ✓ │ ✗ │ ✓ │
│ 自动 Failover │ ✓ │ ✓ │ ✓ │ ✓ │
│ 计划内 Switchover │ ✓ │ ✓ │ ✓ │ ✓ │
│ Synchronous Repl │ ✓ │ ✓ │ ✓ │ ✓ │
│ Read Replicas │ ✓ │ ✓ │ ✓ │ ✓ │
│ WAL 独立 PVC │ ✓ │ ✗ │ ✗ │ ✓ │
│ TLS 证书自动管理 │ ✓ │ ✓ │ ✗(手动) │ ✓ │
│ PgBouncer 集成 │ ✗ │ ✓(Envoy) │ ✓(CRD) │ ✓(CRD) │
│ Babelfish │ ✗ │ ✓ │ ✗ │ ✗ │
│ 150+ Extensions │ ✗ │ ✓ │ ✗ │ ✗ │
│ 大版本升级 │ pg_upgrade │ pg_upgrade │ pg_upgrade │ pg_upgrade │
├─────────────────────┼──────────────┼──────────────┼─────────────┼──────────────┤
│ Prometheus 集成 │ PodMonitor │ PodMonitor │ 手动 │ PodMonitor │
│ Grafana Dashboard │ 社区提供 │ 预置 │ 社区提供 │ 预置 │
│ pgAudit │ ✓ │ ✓ │ ✓ │ ✓ │
├─────────────────────┼──────────────┼──────────────┼─────────────┼──────────────┤
│ 运维复杂度 │ 低 │ 中 │ 高 │ 低 │
│ 学习曲线 │ 平缓 │ 中等 │ 陡峭 │ 平缓 │
│ 社区活跃度 │ 非常高 │ 中等 │ 中等 │ 中等 │
│ K8s 原生程度 │ 极高 │ 高 │ 中 │ 高 │
│ PostgreSQL 版本支持 │ 12-17 │ 12-17 │ 12-17 │ 12-17 │
│ 扩展点支持 │ 中 │ 高(150+) │ 中 │ 中 │
└─────────────────────┴──────────────┴──────────────┴─────────────┴──────────────┘
选型决策树:
是否已有 etcd 基础设施?
├── 是 → 考虑 Zalando Operator
└── 否 →
是否需要 Babelfish / 150+ Extensions / 一体化平台?
├── 是 → 考虑 StackGres
└── 否 →
是否使用 Red Hat OpenShift?
├── 是 → 考虑 Crunchy Data PGO
└── 否 → 考虑 CloudNativePG(K8s 原生,最小依赖)
各 Operator 的最佳适配场景:
| Operator | 最佳适配 |
|---|---|
| CloudNativePG | 标准化 K8s 集群、追求最小外部依赖、需要快速部署 |
| StackGres | 需要全套平台体验、SQL Server 迁移、多扩展需求 |
| Zalando | 已有 etcd 运维能力、大规模 PG 集群管理、复杂网络拓扑 |
| PGO | OpenShift 环境、Red Hat 生态集成、企业级安全需求 |
28 PostgreSQL 的容器化性能调优
答案:
PostgreSQL 在容器化环境下需要对 shared_buffers、WAL 配置、Huge Pages 和 K8s 资源限制进行协同调优。
关键内存参数调优:
spec:
postgresql:
parameters:
# shared_buffers:数据库缓存,推荐物理内存的 25%
# K8s 容器环境中,以 resource.limits.memory 为基准
shared_buffers: "4GB" # 假设 limit=16Gi → 25%
# effective_cache_size:查询规划器估计的系统缓存
effective_cache_size: "12GB" # 75% of 16Gi
# work_mem:单次排序/哈希操作内存
work_mem: "64MB" # (total - shared_buffers) / (max_connections * 2)
# maintenance_work_mem:VACUUM/CREATE INDEX 内存
maintenance_work_mem: "1GB" # limit 的 10%,OOM Kill 风险边界
# WAL 配置
max_wal_size: "4GB" # 自动 Checkpoint 触发阈值
min_wal_size: "1GB" # 保留的最小 WAL 空间
wal_buffers: "64MB" # WAL 写入缓冲区
# 检查点配置
checkpoint_timeout: "15min"
checkpoint_completion_target: "0.9" # 摊平 I/O 峰值,90% 时间内完成
# 随机页访问开销(容器环境使用 SSD 时降低)
random_page_cost: "1.1" # SSD 设为 1.0-1.5,HDD 保持 4.0
effective_io_concurrency: "200" # NVMe SSD
# Autovacuum 调优(参见 Q20)
autovacuum_max_workers: "5"
autovacuum_vacuum_cost_limit: "2000" # SSD 可适当提高
shared_buffers 与 K8s 内存限制的关系:
| 参数 | 建议计算方式 |
|---|---|
shared_buffers | resource.limits.memory 的 25%,上限 8GB |
effective_cache_size | resource.limits.memory 的 75% |
work_mem | (limits.memory - shared_buffers) / (max_connections * 2) |
maintenance_work_mem | limits.memory 的 10%,上限 2GB |
wal_buffers | shared_buffers 的 1/32,默认 64MB |
Huge Pages 配置:
# Node 级别启用 Huge Pages(需要 kubelet 支持)
# /etc/default/kubelet: --feature-gates=HugePageStorageMediumSize=true
spec:
postgresql:
parameters:
huge_pages: "on"
resources:
limits:
hugepages-2Mi: "2Gi" # shared_buffers 映射到 Huge Pages
Huge Pages 将 shared_buffers 映射到大页内存,减少 TLB miss,提升 OLTP 场景 5%~15% 的吞吐量。需注意:Huge Pages 内存在容器重启前不可回收,必须精确计算。
WAL 配置调优:
spec:
# 分离 WAL 存储,避免与数据文件的 I/O 竞争
walStorage:
size: 20Gi
storageClass: premium-rwo # 与 PGDATA 使用不同 StorageClass 更佳
postgresql:
parameters:
wal_level: "replica"
wal_compression: "on" # WAL 页压缩,减少写入量
wal_log_hints: "on" # 支持 pg_rewind
full_page_writes: "on" # 保证 WAL 页完整性
# Checkpoint I/O 节流
max_wal_size: "4GB"
min_wal_size: "2GB"
checkpoint_timeout: "15min"
checkpoint_completion_target: "0.9"
K8s 资源限制最佳实践:
| 资源 | requests | limits | 说明 |
|---|---|---|---|
| CPU | 实际平均使用量的 80% | 实际峰值的 120% | 避免 CPU Throttle 导致查询延迟毛刺 |
| Memory | limits = requests(Guaranteed QoS) | 同 requests | 避免 OOM Kill,不计 shared_buffers 外的 OS Cache |
| Disk IOPS | 通过 StorageClass 指定 | — | OLTP 建议 >= 3000 IOPS |
| Network | — | — | AZ 内 RTT < 1ms |
性能验证 SQL:
-- 验证 shared_buffers 命中率
SELECT
(blks_hit * 100.0 / NULLIF(blks_hit + blks_read, 0)) AS cache_hit_ratio
FROM pg_stat_database
WHERE datname = current_database();
-- 目标:> 99%
-- 验证 Checkpoint 频率
SELECT
checkpoints_timed,
checkpoints_req,
(checkpoints_req * 100.0 / NULLIF(checkpoints_timed + checkpoints_req, 0)) AS req_ratio
FROM pg_stat_bgwriter;
-- 目标:req_ratio < 10%(大部分为定时 Checkpoint 而非 WAL 触发)
29 PostgreSQL 的大版本升级
答案:
PostgreSQL 大版本升级(如 15 → 16)在 Kubernetes 环境中有 pg_upgrade、逻辑复制和导入导出三种策略。
三种升级策略对比:
| 策略 | 停机时间 | 数据完整性 | 复杂度 | 回滚能力 |
|---|---|---|---|---|
| pg_upgrade(原地升级) | 分钟级(与数据量相关) | 二进制兼容验证 | 中 | 依赖事先备份回滚 |
| 逻辑复制(在线迁移) | 接近零(秒级切换) | 逐表校验 | 高 | 保留旧集群,可随时切回 |
| pg_dump / pg_restore | 小时级(与数据量相关) | 数据完整性校验 | 低 | 保留导出文件 |
CloudNativePG pg_upgrade 升级配置:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-v16
spec:
instances: 3
imageName: ghcr.io/cloudnative-pg/postgresql:16.4
bootstrap:
pg_upgrade:
# 引用原集群的 PVC,或备份恢复位置
sourceCluster:
name: pg-v15
# pg_upgrade 模式
import:
type: snapshot # 或 type: backup
pg_upgrade 流程:
1. 创建新版本的 Pod(init 容器执行 pg_upgrade --check)
2. pg_upgrade --check 验证兼容性(扩展、数据类型、编码)
3. 如果验证通过:
a. 停止旧集群(或从快照创建克隆)
b. 运行 pg_upgrade --link(硬链接模式,快速升级)
c. 启动新版本 PostgreSQL
d. VACUUM ANALYZE 更新统计信息
4. 如果验证失败:回滚到旧集群
逻辑复制在线迁移流程:
graph TD
P1[Phase 1: 搭建目标集群]
SRC[源 15.x<br/>Production]
TGT[目标 16.x<br/>New]
P1 --> SRC
P1 --> TGT
P2[Phase 2: 全量数据同步<br/>CREATE SUBSCRIPTION<br/>copy_data = true]
SRC -->|全量复制| TGT
P3[Phase 3: 增量追赶<br/>持续同步至延迟接近 0]
TGT -->|变更数据| P3
SRC -->|变更数据| P3
P4[Phase 4: 切换]
P3 --> P4
P4 --> S1[停止应用写入源集群]
S1 --> S2[确认复制延迟为 0]
S2 --> S3[切换应用连接到目标集群]
S3 --> S4[DROP SUBSCRIPTION 目标端]
S4 --> S5[清理源集群]
大版本升级检查清单:
| 检查项 | 命令 / 方法 | 必须通过 |
|---|---|---|
| 扩展兼容性 | SELECT * FROM pg_available_extensions; 对比目标版本 | 是 |
| 数据类型变更 | 检查 Release Notes 中的 incompatible changes | 是 |
pg_upgrade --check | 在非生产环境完整执行 | 是 |
reindex 必要性 | 检查索引是否需要重建(collation 变更等) | 建议 |
| 备份验证 | 从最新备份恢复至临时集群验证 | 是 |
| 应用兼容性 | 新版本驱动 + ORM 适配测试 | 是 |
| 回滚方案 | 保留旧的 PVC / 备份 / 逻辑复制源集群 | 是 |
升级后统计信息更新:
-- 升级后更新优化器统计信息
VACUUM ANALYZE;
-- 重建因升级可能失效的索引
REINDEX DATABASE appdb;
30 PostgreSQL on Kubernetes 生产环境最佳实践
答案:
生产环境部署 PostgreSQL on Kubernetes 需从高可用、备份、安全、性能、监控五个维度建立完整的运维规范。
一、高可用配置规范:
spec:
instances: 3 # 生产最小 3 实例(1 Primary + 2 Standby)
# 同步复制:至少 1 个 Standby 确认
postgresql:
synchronous:
numberOfInstances: 1
# Pod 反亲和:强制分布到不同节点
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
cnpg.io/cluster: pg-production
topologyKey: kubernetes.io/hostname
# 拓扑分布:强制分布到不同 AZ
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
二、备份策略规范:
| 层级 | 策略 | 保留期 | 存储 |
|---|---|---|---|
| WAL 归档(连续) | 持续推送到 S3 | 与全量备份对齐 | S3 Standard / Standard-IA |
| 全量备份(每日) | CronJob triggered Backup | 30 天 | S3 Standard |
| Volume 快照(每 6h) | CSI Snapshot | 7 天 | K8s 集群内 |
| 跨区域复制 | S3 Cross-Region Replication | 90 天 | 异地 S3 |
三、安全加固规范:
spec:
# 禁用 superuser 访问
enableSuperuserAccess: false
# TLS 证书自动管理
certificates:
serverTLSSecret: pg-tls-cert
clientCASecret: pg-client-ca
postgresql:
parameters:
# 强制加密连接
ssl: "on"
ssl_min_protocol_version: "TLSv1.3"
# 密码加密
password_encryption: "scram-sha-256"
# 审计日志(参见 Q23)
shared_preload_libraries: "pg_stat_statements, pgaudit"
pgaudit.log: "write, ddl, role"
# 连接安全
log_connections: "on"
log_disconnections: "on"
# 事务安全
idle_in_transaction_session_timeout: "300s"
statement_timeout: "30s" # 应用自定义,此为兜底
lock_timeout: "10s"
四、性能基线规范:
| 参数 | 推荐基线 | 说明 |
|---|---|---|
shared_buffers | 25% of mem limit | 上限 8GB |
effective_cache_size | 75% of mem limit | — |
max_connections | 200(配合 PgBouncer) | 每连接 ~10MB 内存 |
work_mem | 64MB / connection | 根据并发调整 |
maintenance_work_mem | 1GB | 避免 OOM |
max_wal_size | 4GB | — |
checkpoint_timeout | 15min | 避免频繁 Checkpoint |
random_page_cost | 1.1(SSD) | NVMe 可降至 1.0 |
autovacuum_max_workers | 5 | 高并发 OLTP |
| WAL StorageClass | 独立 SSD / NVMe | 解除 PGDATA 的 WAL I/O 干扰 |
五、监控告警规范:
| 告警项 | 条件 | 严重级别 |
|---|---|---|
| 集群不健康 | cnpg_cluster_healthy != 1 | Critical |
| 连接数 > 80% | pg_stat_database_numbackends / max_connections > 0.8 | Warning |
| 复制延迟 > 30s | pg_stat_replication_replay_lag > 30 | Warning |
| WAL 堆积 > 10GB | cnpg_pg_wal_size > 10GB | Warning |
| 磁盘 > 80% | cnpg_pgdata_size / pvc_size > 0.8 | Critical |
| 备份失败 | 最近一次备份状态 != completed | Critical |
| 长事务 > 5min | xact_age > 300 | Warning |
| Dead Tuple > 20% | n_dead_tup / (n_dead_tup + n_live_tup) > 0.2 | Warning |
六、运维操作 SOP:
| 操作 | 步骤 | 窗口 |
|---|---|---|
| 计划内 Switchover | kubectl cnpg promote <cluster> <standby> | 业务低峰 |
| 扩缩容实例 | kubectl patch cluster <name> --type merge -p '{"spec":{"instances":5}}' | 业务低峰 |
| 小版本升级 | 修改 imageName,触发 Rolling Update | 业务低峰 |
| 大版本升级 | pg_upgrade 模式,事先在测试集群验证 | 维护窗口 |
| 存储扩容 | 修改 storage.size,CSI 在线扩容 | 无需停服 |
| PITR 恢复 | 从备份恢复到新集群,验证后切换应用 | 紧急窗口 |
| 手动 Failover | 模拟 Primary 故障,验证自动 Failover 流程 | 定期演练 |
七、资源规划公式:
集群实例数:
生产:>= 3(至少覆盖 3 AZ)
预发布:>= 2
开发:1
存储容量:
PVC Size = 预估 12 个月数据量 × 1.5(增长余量)
CPU:
requests ≥ 实际平均使用量 × 1.2
limits ≥ 实际峰值 × 1.3
内存:
requests = limits(Guaranteed QoS)
总量 = shared_buffers + (max_connections × work_mem) + maintenance_work_mem + OS reserve(512MB)
八、禁止事项:
- 禁止在 PostgreSQL Pod 中执行
kill -9终止 Postgres 进程 - 禁止在生产环境直接修改 Pod 内的
postgresql.conf,应通过 Cluster CRDpostgresql.parameters统一管理 - 禁止在没有备份验证的情况下执行
pg_upgrade - 禁止
VACUUM FULL在生产高峰期执行 - 禁止在生产环境使用
fsync=off - 禁止在未配置
idle_in_transaction_session_timeout的情况下运行长连接应用