Skip to content

PostgreSQL 读写分离架构优化

核心概念

读写分离是通过将数据库的读操作和写操作分离到不同的数据库实例上,以提高系统的并发处理能力和可用性。PostgreSQL 读写分离架构主要涉及以下核心概念:

  • 主从复制:通过流复制或逻辑复制实现主库数据向从库的同步
  • 负载均衡:使用中间件或应用层实现读请求的分发
  • 一致性保障:确保读操作能获取到最新的写数据
  • 故障切换:当主库故障时自动或手动切换到从库

配置方法

1. 主从复制配置

主库配置

sql
-- 1. 启用归档模式
ALTER SYSTEM SET wal_level = replica;
ALTER SYSTEM SET archive_mode = on;
ALTER SYSTEM SET archive_command = 'cp %p /path/to/archive/%f';
ALTER SYSTEM SET max_wal_senders = 10;
ALTER SYSTEM SET max_replication_slots = 10;

-- 2. 创建复制用户
CREATE ROLE repluser WITH REPLICATION LOGIN ENCRYPTED PASSWORD 'replpassword';

-- 3. 配置 pg_hba.conf 允许复制连接
-- 在 pg_hba.conf 中添加以下行:
-- host replication repluser 从库IP/32 md5

从库配置

bash
# 1. 停止从库服务
pg_ctl -D /path/to/data stop

# 2. 清空从库数据目录
rm -rf /path/to/data/*

# 3. 从主库基础备份
pg_basebackup -h 主库IP -p 5432 -U repluser -D /path/to/data -F p -X stream -R -P

# 4. 配置从库参数(可选)
# 在 postgresql.conf 中添加:
# hot_standby = on
# max_standby_archive_delay = 30s
# max_standby_streaming_delay = 30s

# 5. 启动从库服务
pg_ctl -D /path/to/data start

验证复制状态

sql
-- 在主库上查看复制状态
SELECT * FROM pg_stat_replication;

-- 在从库上查看复制状态
SELECT * FROM pg_stat_wal_receiver;

2. 读写分离中间件配置

使用 Pgpool-II 实现读写分离

txt
# 1. 安装 Pgpool-II
# Ubuntu/Debian:
apt-get install pgpool2

# CentOS/RHEL:
yum install pgpool-II-pg14

# 2. 配置 pgpool.conf
backend_hostname0 = '主库IP'
backend_port0 = 5432
backend_weight0 = 1
backend_flag0 = 'ALLOW_TO_FAILOVER'

backend_hostname1 = '从库1IP'
backend_port1 = 5432
backend_weight1 = 1
backend_flag1 = 'ALLOW_TO_FAILOVER'

backend_hostname2 = '从库2IP'
backend_port2 = 5432
backend_weight2 = 1
backend_flag2 = 'ALLOW_TO_FAILOVER'

# 启用负载均衡
load_balance_mode = on

# 设置读写分离规则
write_function_list = 'pg_catalog.setval,pg_catalog.nextval,pg_catalog.currval,pg_catalog.lastval,pg_catalog.set_config,pg_catalog.reset_config'

# 3. 配置 pcp.conf(用于管理)
# 添加:
# admin:md5密码哈希

# 4. 启动 Pgpool-II
systemctl start pgpool2

使用应用层实现读写分离

java
// 示例:Spring Boot 应用中使用读写分离
@Configuration
public class DataSourceConfig {
    
    @Bean(name = "masterDataSource")
    public DataSource masterDataSource() {
        // 主库数据源配置
    }
    
    @Bean(name = "slaveDataSource")
    public DataSource slaveDataSource() {
        // 从库数据源配置
    }
    
    @Bean
    public AbstractRoutingDataSource routingDataSource() {
        // 实现数据源路由逻辑
    }
}

3. 读写分离监控

sql
-- 监控主从延迟
SELECT now() - pg_last_xact_replay_timestamp() AS replication_delay FROM pg_stat_replication;

-- 监控连接分布(Pgpool-II)
SHOW pool_nodes;

最佳实践

生产环境配置建议

  1. 从库数量:根据读请求量配置合适的从库数量,建议2-4个从库
  2. 负载均衡策略:根据业务特点选择轮询、权重或最小连接数策略
  3. 主从延迟监控:设置延迟阈值告警,例如当延迟超过30秒时触发告警
  4. 读一致性处理
    • 对于强一致性要求的读请求,直接路由到主库
    • 使用会话级别的一致性控制,确保同一用户的读请求能获取到自己的写数据
  5. 定期切换测试:定期进行主从切换测试,确保故障切换机制可靠
  6. 备份策略:从库定期进行基础备份,减轻主库备份压力

性能优化建议

  1. 从库参数优化

    • hot_standby_feedback = on:防止从库查询被取消
    • max_worker_processes:根据CPU核心数调整
    • shared_buffers:从库可适当增加
  2. 中间件优化

    • 调整 Pgpool-II 的 num_init_childrenmax_pool 参数
    • 启用连接池复用,减少连接建立开销
  3. 应用层优化

    • 合理设计查询,避免大查询影响从库性能
    • 实现查询缓存,减少数据库访问压力

常见问题处理

  • 问题1:主从延迟增大 解决方法:

    • 检查主库是否有长事务,导致WAL无法及时归档
    • 检查网络带宽是否充足
    • 调整从库的 hot_standby_feedback 参数
    • 考虑使用并行复制(PostgreSQL 14+)
  • 问题2:从库查询报错 解决方法:

    • 检查从库的 hot_standby 参数是否开启
    • 调整 max_standby_archive_delaymax_standby_streaming_delay 参数
    • 对于复杂查询,考虑直接路由到主库
  • 问题3:Pgpool-II 频繁切换主库 解决方法:

    • 调整 health_check_timeouthealth_check_retry_delay 参数
    • 检查网络稳定性
    • 确保主库资源充足,避免响应超时

常见问题(FAQ)

Q1:如何选择合适的读写分离方案?

A1:根据业务规模和技术栈选择:

  • 小型应用:应用层读写分离
  • 中型应用:Pgpool-II 或 HAProxy
  • 大型应用:专业的数据库中间件(如 OceanBase、PolarDB-X)

Q2:读写分离会影响数据一致性吗?

A2:会。由于主从复制存在延迟,从库可能无法立即获取到主库的最新数据。解决方案包括:

  • 对于强一致性要求的请求,直接路由到主库
  • 使用会话绑定,同一用户的请求在一段时间内路由到同一实例
  • 实现读等待机制,确保获取到最新数据

Q3:如何处理主库故障?

A3:主库故障处理流程:

  1. 监控系统检测到主库故障
  2. 自动或手动将从库提升为主库
  3. 更新 Pgpool-II 或应用层配置,将写请求路由到新主库
  4. 重新配置其他从库连接到新主库
  5. 修复原主库后,将其作为从库重新加入集群

Q4:读写分离能提升多少性能?

A4:性能提升取决于读写比例和从库数量。一般来说:

  • 读请求占比越高,性能提升越明显
  • 从库数量越多,读请求处理能力越强
  • 通常可提升2-5倍的读处理能力

Q5:如何监控读写分离架构的健康状态?

A5:关键监控指标包括:

  • 主从复制延迟
  • 各实例的连接数和负载
  • Pgpool-II 或中间件的健康状态
  • 读请求分布情况
  • 主库和从库的磁盘、CPU、内存使用率