PanguHA双机热备系统部署指南
PanguHA双机热备软件获取
1.系统概述
PanguHA是一款专为Windows平台设计的双机热备集群解决方案,通过纯软件方式提供系统高可用性保障。该系统通常由两个节点组成:活动节点和备用节点。活动节点负责处理业务请求,而备用节点则作为备份,随时准备接管业务。当活动节点发生故障导致业务中断时,备用节点能够迅速检测并接替活动节点的工作,从而确保业务连续性或实现最小化中断。
2.运行环境
需安装.NET Framework 4.6.1或更高版本
3.软件部署
无需安装,直接解压后运行主程序即可。
4.功能详解
4.1 IP地址漂移
IP地址漂移功能主要用于实现Web应用、WebApi/Webservice、数据库等服务的故障转移。除了服务器的物理IP地址外,该软件允许配置一个虚拟IP地址,所有外部访问均通过此虚拟IP进行。配置完成后,虚拟IP绑定在主节点上;当主节点发生故障时,虚拟IP会自动漂移至备用节点继续提供服务。对于前端应用和数据库而言,这种切换是无感知的,因为它们仍然通过相同的虚拟IP进行访问。
主节点在以下情况下会触发IP漂移:
- 服务器宕机
- 网卡异常
- Web服务URL异常(需预先配置)
- Windows服务异常(需预先配置)
配置步骤:首先选择一台服务器作为主节点,打开软件并切换到【IP漂移】标签页,选择网络接口(确保该接口使用静态IP而非DHCP自动获取)。如有多个网卡,请选择需要配置虚拟IP的那个网段的网卡。
设置本机IP(例如192.168.56.101)和集群虚拟IP(例如192.168.56.103,注意路由器不应将该IP与MAC地址绑定)。虚拟IP必须与本机IP在同一网段,且未被其他设备使用。
添加对方服务器的IP地址。点击运行后,右下角显示"失败重试"可以忽略。等待一段时间后,左下角显示"主节点"即表示主节点配置成功,虚拟IP已绑定到当前节点。可通过ipconfig命令验证。对方节点显示红色是因为尚未运行热备软件。
心跳间隔和断联重试设置可调整主备切换速度,但不宜设置过小,否则可能因网络抖动导致频繁切换。【切换】按钮提供手动切换功能。【停止】按钮将停止心跳检测,不进行主备切换。
配置对方IP后,系统会检测对方热备软件的在线状态,异常时发送邮件提醒。防火墙需开放ICMP协议和默认端口9999。
如果发现端口已开放、软件已运行,但telnet不通,请检查防火墙是否阻止了HA应用。
对方IP状态颜色含义:
- 绿色:正常连接
- 红色:连接断开
- 灰色:未监测
添加本机URL可监测Web服务状态。当检测到Web服务状态码非200(30秒内重试3次)时,如果是在主节点,则进行切换并邮件提醒;如果是在备节点,则仅发送邮件提醒(防止主节点服务异常且无可用的备节点)。
配置Windows服务后,主节点检测到服务异常会先尝试重启(30秒内重试3次),重启失败则进行故障转移。备节点服务重启失败则发送邮件提醒。
配置备用节点服务器时,需注意两台服务器必须能互相ping通,且设置的集群虚拟IP必须相同。两台服务器应在同一网段。
配置完成后,可进行故障转移测试:关闭主节点服务器,观察备用节点是否升级为主节点(即虚拟IP是否漂移过来)。测试成功后,访问虚拟IP将指向备用节点,实现故障转移。
Web应用配置:如使用IIS,需在两台服务器上部署相同应用,并将绑定IP均设置为虚拟IP。其他Web服务器(如Tomcat)同理。若绑定虚拟IP导致服务启动失败,可改为绑定0.0.0.0。
4.2 进程管理
进程管理功能基于IP漂移,用于配置可执行文件(.exe)或Windows服务的切换。配置的进程和服务仅在主节点上运行,备用节点上则会被关闭。
操作步骤:假设已完成IP漂移配置,现需控制WindowsFormsApp1.exe程序的切换。只需在A、B两台服务器上对该程序进行配置即可。配置的进程将在主节点上运行(即使手动关闭也会自动重启),在备节点上则会被关闭(即使意外启动也会自动终止)。当主节点A宕机后,备用节点B升级为主节点,会自动开启配置的进程,实现进程切换。Windows服务的配置方法相同。
4.3 文件同步
注意事项:数据库文件的同步请谨慎使用此功能
文件同步功能基于IP漂移,该软件定位为文件服务器,因此需配合虚拟IP使用。配置前需先设置共享文件夹,并确保两台服务器能互相访问对方的共享文件夹路径。
在服务器A上配置A→B的同步设置,在服务器B上配置B→A的同步设置。虽然两台服务器都配置了同步路径并启动,但同步任务仅在主节点上运行。当主节点A宕机,备用节点B升级为主节点后,会开启同步任务。文件同步是实时的,右上角的镜像周期是为了防止备用节点意外修改或删除文件,确保备用节点的文件始终与主节点保持一致,并非指同步有5分钟延迟。
4.4 MySQL主从同步
注意事项:部署前请确保两个节点的结构和数据完全一致
视频教程请参考:
MySQL主从同步视频教程
该软件可快速部署MySQL双主复制,结合IP漂移功能,可构建MySQL高可用架构。操作步骤:切换到【MySQL主从】标签页,按照提示在A、B两台服务器上进行配置,然后重启MySQL服务。
在服务器A输入相关信息配置A主B从,点击部署按钮,右下角显示部署成功。在服务器B上输入相关信息配置B主A从,点击部署按钮,显示成功提示。
至此,MySQL双主复制搭建完成。此外,需确保mysql.user表内有%账号,以便虚拟IP可以访问MySQL。结合IP漂移功能,可实现服务器级别宕机的双机切换——当主节点A服务器宕机后,备用节点B升级为主节点,虚拟IP指向B节点,继续提供MySQL服务。
若在下方填写本机MySQL服务名称并开启健康检测,可实现数据库应用级别的宕机切换——当主节点A的MySQL服务故障时,备用节点B可检测到并转移虚拟IP,升级为主节点继续提供服务。同时,若备用节点的MySQL服务故障,则会发送邮件提醒。
4.5 MySQL/SQLServer/PostgreSQL数据同步
基本条件:
- 表必须有主键(复合主键也可)
- 任务初始化完成后,源端会自动添加C_PanguSyncSourceTimestamp时间戳字段,目标端会自动添加C_PanguSyncTargetTimestamp时间戳字段。这两个字段应由数据库自行维护,业务系统中不要主动写入或更新。
视频教程请参考:
MySQL/SQLServer/PostgreSQL数据同步视频教程
视频要点说明:
假设初始状态X为主节点,Y为备用节点。首先在主备节点准备数据库,表结构需完全一致(数据不需要一致)。然后在备用节点Y使用【清空备用节点数据脚本】或TRUNCATE语句(不要使用DELETE)清空数据。在Y节点切换到【数据同步】标签页,添加任务,选择库而非表,批量配置Y→X方向的任务并启动。遇到主键问题时,使用【批量主键创建脚本】或手动创建主键,待所有任务启动成功。然后在X主节点上切换到【数据同步】标签页,添加任务,选择库而非表,批量配置X→Y方向的任务并启动。遇到主键问题时,使用【批量主键创建脚本】或手动创建主键,待所有任务启动成功。此时会进行X→Y方向的初始数据全量同步,在全量同步阶段请在Y备用节点的【IP漂移】界面暂停,避免意外切换。全量同步完成后会进行增量同步,此时再在Y备用节点的【IP漂移】界面运行。
SQLServer清空备用节点数据脚本【参考视频】:
-- 替换为实际数据库名称
Use B;
DECLARE @DatabaseName NVARCHAR(128) = N'B';
-- 验证数据库是否存在
IF NOT EXISTS (SELECT 1 FROM sys.databases WHERE name = @DatabaseName)
BEGIN
RAISERROR(N'错误:数据库 "%s" 不存在!', 16, 1, @DatabaseName);
RETURN;
END;
-- 声明变量
DECLARE @SchemaName NVARCHAR(128),
@TableName NVARCHAR(128),
@FullTableName NVARCHAR(255),
@SqlStatement NVARCHAR(MAX);
-- 创建游标获取所有用户表
DECLARE tableCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT
s.name AS SchemaName,
t.name AS TableName
FROM
sys.databases db
INNER JOIN
sys.tables t ON db.name = @DatabaseName
INNER JOIN
sys.schemas s ON t.schema_id = s.schema_id
WHERE
db.name = @DatabaseName
AND t.type = 'U'; -- 只获取用户表
-- 开始处理
OPEN tableCursor;
FETCH NEXT FROM tableCursor INTO @SchemaName, @TableName;
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
-- 构造完整表名和SQL语句
SET @FullTableName = QUOTENAME(@SchemaName) + '.' + QUOTENAME(@TableName);
SET @SqlStatement = N'USE ' + QUOTENAME(@DatabaseName) + N';
TRUNCATE TABLE ' + @FullTableName;
-- 执行截断操作
EXEC sp_executesql @SqlStatement;
PRINT N'成功截断表: ' + @FullTableName;
END TRY
BEGIN CATCH
PRINT N'错误截断表: ' + @FullTableName + N' | 错误信息: ' + ERROR_MESSAGE();
END CATCH
FETCH NEXT FROM tableCursor INTO @SchemaName, @TableName;
END;
-- 清理资源
CLOSE tableCursor;
DEALLOCATE tableCursor;
PRINT N'操作完成!请检查输出信息确认结果。';
PostgreSQL清空备用节点数据脚本【参考视频】:
DO $$
DECLARE
table_name TEXT;
BEGIN
-- 获取所有用户表
FOR table_name IN
SELECT tablename
FROM pg_tables
WHERE schemaname = 'public'
LOOP
-- 清空表并重置序列
EXECUTE 'TRUNCATE TABLE ' || quote_ident(table_name) || ' RESTART IDENTITY CASCADE';
RAISE NOTICE '已清空表: %', table_name;
END LOOP;
END
$$;
MySQL清空备用节点数据脚本【参考视频】:
SET SESSION group_concat_max_len = 10000000;
-- 生成 TRUNCATE 语句
SELECT GROUP_CONCAT(
CONCAT('TRUNCATE TABLE `', table_name, '`')
SEPARATOR ';'
) INTO @truncate_sql
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_type = 'BASE TABLE';
-- 显示将要执行的语句
SELECT @truncate_sql AS 将要执行的语句;
1.查询出结果后,复制语句
-- 禁用外键检查
SET FOREIGN_KEY_CHECKS = 0;
2.刚才查询出来的语句
SET FOREIGN_KEY_CHECKS = 1;
【SQLServer批量主键创建脚本】(遍历某个库所有表,1.如果有主键则跳过。2.如果有自增列却没有主键,则将自增列设置为主键。3.如果没有自增列也没有主键,则自动创建名为_PanguId_类型为Bigint的自增主键):
DECLARE @DatabaseName NVARCHAR(128) = N'A' -- 替换为你的数据库名称
USE [A] -- 这里也需要替换为实际的数据库名称
DECLARE @TableSchema NVARCHAR(128)
DECLARE @TableName NVARCHAR(128)
DECLARE @ColumnName NVARCHAR(128)
DECLARE @Sql NVARCHAR(MAX)
DECLARE @PKName NVARCHAR(128)
-- 创建游标遍历所有用户表
DECLARE table_cursor CURSOR FOR
SELECT
TABLE_SCHEMA,
TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND TABLE_CATALOG = @DatabaseName
OPEN table_cursor
FETCH NEXT FROM table_cursor INTO @TableSchema, @TableName
WHILE @@FETCH_STATUS = 0
BEGIN
-- 检查表是否有主键
IF NOT EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
AND tc.TABLE_SCHEMA = @TableSchema
AND tc.TABLE_NAME = @TableName
AND tc.TABLE_CATALOG = @DatabaseName
)
BEGIN
-- 检查表是否有自增列
IF EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @TableSchema
AND TABLE_NAME = @TableName
AND TABLE_CATALOG = @DatabaseName
AND COLUMNPROPERTY(
OBJECT_ID(QUOTENAME(@TableSchema) + '.' + QUOTENAME(@TableName)),
COLUMN_NAME,
'IsIdentity'
) = 1
)
BEGIN
-- 获取自增列名
SELECT @ColumnName = COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @TableSchema
AND TABLE_NAME = @TableName
AND TABLE_CATALOG = @DatabaseName
AND COLUMNPROPERTY(
OBJECT_ID(QUOTENAME(@TableSchema) + '.' + QUOTENAME(@TableName)),
COLUMN_NAME,
'IsIdentity'
) = 1
-- 设置自增列为主键
SET @PKName = 'PK_' + @TableName
SET @Sql = N'ALTER TABLE ' + QUOTENAME(@TableSchema) + '.' + QUOTENAME(@TableName) +
N' ADD CONSTRAINT ' + QUOTENAME(@PKName) +
N' PRIMARY KEY (' + QUOTENAME(@ColumnName) + N')'
PRINT N'为表' + @TableSchema + '.' + @TableName + N' 设置自增列为主键: ' + @Sql
EXEC sp_executesql @Sql
END
ELSE
BEGIN
-- 创建_PanguId_自增主键
SET @PKName = 'PK_' + @TableName
SET @Sql = N'
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = @Schema
AND TABLE_NAME = @Table
AND COLUMN_NAME = ''_PanguId_'')
BEGIN
ALTER TABLE ' + QUOTENAME(@TableSchema) + '.' + QUOTENAME(@TableName) +
N' ADD _PanguId_ bigint IDENTITY(1,1) NOT NULL
END
ALTER TABLE ' + QUOTENAME(@TableSchema) + '.' + QUOTENAME(@TableName) +
N' ADD CONSTRAINT ' + QUOTENAME(@PKName) +
N' PRIMARY KEY (_PanguId_)'
PRINT N'为表' + @TableSchema + '.' + @TableName + N' 创建_PanguId_自增主键'
EXEC sp_executesql @Sql,
N'@Schema NVARCHAR(128), @Table NVARCHAR(128)',
@Schema = @TableSchema, @Table = @TableName
END
END
ELSE
BEGIN
PRINT N'表' + @TableSchema + '.' + @TableName + N' 已有主键,跳过处理'
END
FETCH NEXT FROM table_cursor INTO @TableSchema, @TableName
END
CLOSE table_cursor
DEALLOCATE table_cursor
【MySQL批量主键创建脚本】自动创建主键脚本(遍历某个库所有表,1.如果有主键则跳过。2.如果有自增列却没有主键,则将自增列设置为主键。3.如果没有自增列也没有主键,则自动创建Bigint类型名称为_PanguId_的自增主键):
下面脚本中的CALL AddMissingPrimaryKeys('A'); A替换为自己的数据库
DELIMITER $$
DROP PROCEDURE IF EXISTS AddMissingPrimaryKeys$$
CREATE PROCEDURE AddMissingPrimaryKeys(IN target_db VARCHAR(64))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tbl_name VARCHAR(64);
DECLARE auto_col_name VARCHAR(64) DEFAULT NULL;
DECLARE has_pk TINYINT DEFAULT 0;
DECLARE has_pangu_col TINYINT DEFAULT 0;
DECLARE cur CURSOR FOR
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = target_db
AND TABLE_TYPE = 'BASE TABLE';
-- 主循环的 NOT FOUND handler
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO tbl_name;
IF done THEN
LEAVE read_loop;
END IF;
-- 重置状态
SET auto_col_name = NULL;
SET has_pk = 0;
SET has_pangu_col = 0;
-- 检查是否有主键
SELECT COUNT(*) INTO has_pk
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = target_db
AND TABLE_NAME = tbl_name
AND CONSTRAINT_NAME = 'PRIMARY';
IF has_pk > 0 THEN
ITERATE read_loop;
END IF;
-- 👇 安全获取自增列:使用嵌套块隔离 NOT FOUND 👇
BEGIN
DECLARE no_auto_inc CONDITION FOR SQLSTATE '02000';
DECLARE CONTINUE HANDLER FOR NOT FOUND BEGIN END; -- 吃掉 NOT FOUND
SELECT COLUMN_NAME INTO auto_col_name
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = target_db
AND TABLE_NAME = tbl_name
AND EXTRA = 'auto_increment'
LIMIT 1;
END;
IF auto_col_name IS NOT NULL THEN
SET @sql = CONCAT(
'ALTER TABLE `', target_db, '`.`', tbl_name, '` ',
'ADD PRIMARY KEY (`', auto_col_name, '`);'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
ITERATE read_loop;
END IF;
-- 👇 安全检查 _PanguId_ 列是否存在(COUNT 不会触发 NOT FOUND,可不用嵌套)👇
SELECT COUNT(*) INTO has_pangu_col
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = target_db
AND TABLE_NAME = tbl_name
AND COLUMN_NAME = '_PanguId_';
IF has_pangu_col > 0 THEN
SET @sql = CONCAT(
'ALTER TABLE `', target_db, '`.`', tbl_name, '` ',
'MODIFY COLUMN `_PanguId_` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, ',
'ADD PRIMARY KEY (`_PanguId_`);'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
ITERATE read_loop;
END IF;
-- 新建 _PanguId_
SET @sql = CONCAT(
'ALTER TABLE `', target_db, '`.`', tbl_name, '` ',
'ADD COLUMN `_PanguId_` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ',
'ADD PRIMARY KEY (`_PanguId_`);'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
END$$
DELIMITER ;
-- 使用示例
CALL AddMissingPrimaryKeys('A');
DROP PROCEDURE IF EXISTS AddMissingPrimaryKeys;
4.6 邮件通知
如需邮件通知功能,请在【邮件通知】标签页进行相关配置。默认配置了163的SMTP服务器,也可设置其他邮箱服务器。发件人邮箱必须与SMTP服务器类型匹配,收件人可以是其他邮箱。授权码需登录相应邮箱设置。配置完成后,可先点击【邮件测试】确认邮件发送正常,然后点击【保存配置】。
以下情况会触发邮件通知:
- VIP切换时
- 添加对方IP后,主节点监测到对方IP连接异常时
- 添加本机Web服务URL后,备节点检测到Web服务异常时
- 添加本机Windows服务后,备节点检测到Windows服务异常时
- 开启MySQL健康检测后,备节点断联或同步异常时
- SQLServer数据同步异常停止任务时
邮件格式如下图所示。
5.开机自启动
- 在【IP漂移】点击运行成功后,会保存相关信息,下次打开软件将自动运行
- 在【进程管理】配置后,下次打开将自动运行
- 在【文件同步】配置后,下次打开将自动运行
- 在【MySQL主从配置】开启健康检测成功后,会保存MySQL相关设置,下次打开软件将自动开启健康检测
- 在【数据同步】添加任务后,下次打开将自动运行
- 将软件快捷方式放入启动文件夹(Win+R输入shell:startup)
- 或通过组策略设置(Win+R输入gpedit.msc,依次展开计算机配置→Windows设置→安全设置→本地策略→安全选项→用户账户控制:以管理员批准模式运行所有管理员,设置为已禁用)
6.清除配置
删除Config文件夹即可清除所有配置。