Skip to content

PostgreSQL 手动切换流程

手动切换概述

什么是手动切换

手动切换(Manual Switchover)是指在数据库管理员的控制下,将主库的角色切换到备库,同时将原主库转换为备库的操作。这种操作通常在以下场景中使用:

  • 主库计划维护
  • 主库硬件升级
  • 数据中心迁移
  • 负载均衡调整
  • 灾备演练

手动切换与故障转移的区别

特性手动切换故障转移
触发方式手动执行自动触发
主库状态正常运行已故障
数据完整性确保数据一致可能丢失部分数据
切换时间可控不可控
风险较高

准备工作

在执行手动切换前,需要完成以下准备工作:

  1. 检查主备状态

    • 确保主备库复制状态正常
    • 检查复制延迟,确保延迟在可接受范围内
    • 验证备库已应用所有WAL日志
  2. 备份重要数据

    • 执行主库全量备份
    • 备份关键配置文件
    • 确保备份可恢复
  3. 通知相关团队

    • 通知应用团队准备切换
    • 通知监控团队关注切换过程
    • 建立切换沟通渠道
  4. 准备切换工具

    • 确保psql工具可用
    • 准备切换脚本
    • 准备验证脚本

手动切换执行步骤

1. 检查复制状态

在主库上执行以下命令,检查复制状态:

sql
-- 查看主库复制状态
SELECT 
    application_name AS 备库名称,
    client_addr AS 备库IP,
    state AS 复制状态,
    sync_state AS 同步状态,
    pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS 回放延迟_bytes,
    now() - replay_lag AS 时间延迟
FROM pg_stat_replication;

-- 检查主库WAL位置
SELECT pg_current_wal_lsn() AS 主库当前LSN;

在备库上执行以下命令,检查备库状态:

sql
-- 检查备库是否处于恢复状态
SELECT pg_is_in_recovery();

-- 查看备库恢复进度
SELECT 
    last_wal_receive_lsn AS 最后接收LSN,
    last_wal_replay_lsn AS 最后回放LSN,
    last_wal_replay_time AS 最后回放时间
FROM pg_stat_wal_receiver;

-- 检查备库是否已应用所有WAL
SELECT pg_last_wal_replay_lsn() AS 备库当前LSN;

2. 停止主库写入操作

在执行切换前,需要停止主库的写入操作,确保所有WAL日志都已传输到备库:

方法1:使用pg_ctl stop --mode=fast

bash
# 在主库上执行
pg_ctl stop -D /var/lib/pgsql/14/data --mode=fast

方法2:使用SQL命令

sql
-- 在主库上执行
SELECT pg_wal_replay_resume();
SELECT pg_ctl('main', 'stop', 'fast');

3. 验证备库已应用所有WAL

在备库上执行以下命令,验证备库已应用所有WAL日志:

sql
-- 检查备库恢复状态
SELECT pg_is_in_recovery();

-- 查看备库最后回放LSN
SELECT pg_last_wal_replay_lsn() AS 备库当前LSN;

-- 确保备库已接收并应用所有WAL
-- 对比主库和备库的LSN,确保两者一致

4. 提升备库为主库

在备库上执行以下命令,将备库提升为主库:

PostgreSQL 12+版本

bash
# 在备库上执行
pg_ctl promote -D /var/lib/pgsql/14/data

PostgreSQL 9.6-11版本

bash
# 在备库上执行
pg_ctl promote -D /var/lib/pgsql/9.6/data

使用SQL命令(所有版本)

sql
-- 在备库上执行
SELECT pg_promote(wait_seconds => 60, write_promote_file => true);

5. 验证新主库状态

在提升后的主库上执行以下命令,验证主库状态:

sql
-- 检查是否为主库(返回false)
SELECT pg_is_in_recovery();

-- 查看主库状态
SELECT 
    pg_current_wal_lsn() AS 当前LSN,
    pg_is_in_recovery() AS 是否为主库,
    current_database() AS 当前数据库;

-- 检查监听状态
SELECT * FROM pg_stat_activity WHERE backend_type = 'walsender';

6. 将原主库转换为备库

1. 清理原主库数据目录

bash
# 停止原主库服务
pg_ctl stop -D /var/lib/pgsql/14/data --mode=fast

# 清理数据目录(保留配置文件)
rm -rf /var/lib/pgsql/14/data/*

2. 从新主库创建基础备份

bash
# 使用pg_basebackup创建基础备份
pg_basebackup -h 新主库IP -p 5432 -U replication -D /var/lib/pgsql/14/data -Fp -Xs -P -R

3. 配置原主库为备库

编辑/var/lib/pgsql/14/data/postgresql.auto.conf文件,确保包含以下配置:

primary_conninfo = 'host=新主库IP port=5432 user=replication password=replication_pass application_name=原主库名称'

4. 启动原主库

bash
# 启动原主库服务
pg_ctl start -D /var/lib/pgsql/14/data

7. 验证新的主备关系

在新主库上执行以下命令,验证原主库已转换为备库:

sql
-- 查看新的复制状态
SELECT 
    application_name AS 备库名称,
    client_addr AS 备库IP,
    state AS 复制状态,
    sync_state AS 同步状态,
    pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS 回放延迟_bytes
FROM pg_stat_replication;

在原主库(现在是备库)上执行以下命令,验证备库状态:

sql
-- 检查是否为备库(返回true)
SELECT pg_is_in_recovery();

-- 查看备库恢复进度
SELECT 
    last_wal_receive_lsn AS 最后接收LSN,
    last_wal_replay_lsn AS 最后回放LSN,
    last_wal_replay_time AS 最后回放时间
FROM pg_stat_wal_receiver;

应用切换

1. 更新应用连接配置

  • 更新应用数据库连接字符串,指向新主库
  • 更新连接池配置
  • 更新监控配置

2. 测试应用连接

bash
# 使用psql测试连接
psql -h 新主库IP -p 5432 -U appuser -d mydb

# 执行简单查询测试
SELECT 1;
INSERT INTO test_table (name) VALUES ('switchover_test');
SELECT * FROM test_table WHERE name = 'switchover_test';

3. 逐步恢复流量

  • 先恢复部分应用流量
  • 监控系统性能和稳定性
  • 确认无问题后,恢复全部流量

切换验证

1. 数据一致性验证

sql
-- 在新主库上执行
SELECT count(*) FROM important_table;

-- 在原主库(备库)上执行
SELECT count(*) FROM important_table;

-- 确保两个库的计数一致

2. 复制状态验证

sql
-- 在新主库上执行
SELECT 
    application_name,
    client_addr,
    state,
    sync_state,
    pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS 延迟_bytes
FROM pg_stat_replication;

-- 确保所有备库状态正常,延迟在可接受范围内

3. 应用功能验证

  • 验证核心业务功能
  • 验证读写操作正常
  • 验证事务完整性
  • 验证报表查询正常

4. 监控指标验证

  • 检查CPU使用率
  • 检查内存使用率
  • 检查磁盘I/O
  • 检查网络连接数
  • 检查复制延迟

回滚方案

如果切换过程中出现问题,需要执行回滚操作。以下是回滚步骤:

1. 停止新主库写入

sql
-- 在新主库上执行,禁止写入
REVOKE ALL ON DATABASE mydb FROM public;

2. 恢复原主库

bash
# 停止原主库(现在是备库)
pg_ctl stop -D /var/lib/pgsql/14/data --mode=fast

# 恢复原主库备份
pg_restore -h localhost -p 5432 -U postgres -d mydb /path/to/full_backup.sql

# 启动原主库
pg_ctl start -D /var/lib/pgsql/14/data

3. 重新配置备库

bash
# 停止新主库
pg_ctl stop -D /var/lib/pgsql/14/data --mode=fast

# 清理数据目录
rm -rf /var/lib/pgsql/14/data/*

# 从原主库创建基础备份
pg_basebackup -h 原主库IP -p 5432 -U replication -D /var/lib/pgsql/14/data -Fp -Xs -P -R

# 启动备库
pg_ctl start -D /var/lib/pgsql/14/data

4. 恢复应用连接

  • 更新应用连接配置,指向原主库
  • 恢复应用流量
  • 验证系统正常运行

最佳实践

1. 制定详细的切换计划

  • 编写详细的切换脚本
  • 定义清晰的切换步骤和责任人
  • 制定明确的回滚方案
  • 安排切换时间窗口

2. 进行切换演练

  • 在测试环境中进行切换演练
  • 记录切换时间和问题
  • 优化切换流程
  • 培训相关人员

3. 监控切换过程

  • 使用监控工具跟踪切换过程
  • 记录关键指标变化
  • 建立实时沟通渠道
  • 及时处理异常情况

4. 控制切换时间

  • 尽量在业务低峰期执行切换
  • 限制切换时间窗口
  • 优化切换步骤,减少切换时间
  • 准备应急方案

5. 文档记录

  • 记录切换过程的详细信息
  • 记录切换中遇到的问题和解决方案
  • 记录切换前后的系统状态
  • 更新相关文档

常见问题与解决方案

1. 切换后复制延迟持续增长

问题现象

  • 新主库和备库之间的复制延迟持续增长
  • 备库无法跟上主库的写入速度

解决方案

  • 检查备库的系统资源使用情况
  • 调整备库的PostgreSQL参数
  • 考虑使用更强大的硬件
  • 优化主库的写入模式

2. 备库提升失败

问题现象

  • 执行pg_promote()失败
  • 备库无法转换为主库

解决方案

  • 检查备库的恢复状态
  • 验证备库已应用所有WAL日志
  • 检查备库的权限设置
  • 查看备库日志,分析具体错误

3. 应用连接失败

问题现象

  • 应用无法连接到新主库
  • 出现连接超时或认证失败

解决方案

  • 检查新主库的监听配置
  • 验证pg_hba.conf配置
  • 检查防火墙设置
  • 验证应用连接字符串

4. 数据不一致

问题现象

  • 新主库和备库数据不一致
  • 应用查询到错误数据

解决方案

  • 执行全量数据校验
  • 从备份恢复不一致的数据
  • 重新初始化备库
  • 检查复制配置

版本差异注意事项

PostgreSQL 9.6

  • 不支持pg_promote()函数的wait_seconds参数
  • 需要使用recovery.conf配置文件
  • 不支持write_promote_file参数

PostgreSQL 10-11

  • 支持pg_promote()函数
  • 开始支持recovery.signal文件
  • 不支持wait_seconds参数

PostgreSQL 12+

  • 支持完整的pg_promote()函数参数
  • 完全使用recovery.signal替代recovery.conf
  • 支持并行复制
  • 支持更详细的复制状态监控

总结

手动切换是PostgreSQL高可用架构中的重要操作,需要谨慎计划和执行。通过遵循本文档中的步骤和最佳实践,可以确保切换过程的安全可靠,减少对业务的影响。

在实际生产环境中,建议:

  1. 定期进行切换演练,熟悉切换流程
  2. 建立完善的监控和告警机制
  3. 制定详细的切换计划和回滚方案
  4. 与相关团队保持良好沟通
  5. 记录切换过程,持续优化切换流程

通过合理的规划和执行,可以确保PostgreSQL手动切换的成功,提高系统的可用性和可靠性。