云计算百科
云计算领域专业知识百科平台

DBSync:基于 C# WinForm 的轻量级多节点数据库同步工具

摘要:在日常运维需要将核心数据库的配置表、基础字典表定时同步到多个异地节点的从属服务器上。使用 MySQL 原生的 Master-Slave 复制不仅配置繁琐,而且在跨公网、多节点复杂拓扑下容易断连。为此,笔者基于 C# WinForm 独立开发了一款轻量级数据库同步工具——DBSync。它支持多设备配置、可视化编辑、全局源数据库设定及自定义轮询间隔。

一、 开发背景与痛点

在管理多个异地部署的局域网服务器群(如通过 Tailscale 组网的多台 Linux 节点)时,经常遇到以下痛点:

  • 基础数据不一致:用户的配置表(如 c_user_config)、系统菜单(c_menu_management)发生变更时,需要手动去多台机器上导 SQL。
  • 原生同步太重:多台节点如果全部强行配成主从模式,不仅消耗性能,一旦网络波动,维护成本极高。
  • 缺乏可视化管理:运维人员希望能一眼看到每个节点的同步状态、上次执行结果以及具体的同步日志。
  • 针对这些刚需,我决定用 C# (.NET Framework) 搭配经典的 WinForm 窗体快速构建一个轻量级、可视化的同步工具。

    二、 软件核心功能与界面设计

    DBSync 在UI设计上遵循了“直观、无多余操作”的极简极客风格,主要包含以下三个核心看板:

    1. 主控制台(设备状态看板)

    主界面上方为功能操作栏,包含 新增、编辑、删除、保存、刷新 等标准控制,以及一键 启动 / 中断 同步的核心开关。

    列表中清晰展示了所有受控设备的网络状态:

    • 设备名:如 L10_1 到 L10_8 分组。
    • 数据库地址:支持本地(127.0.0.1)及跨网段的公网/内网 IP(如 192.168.100.151)。
    • 执行状态/执行结果:动态刷新当前线程是处于“空闲”还是“正在同步”,并实时记录同步时间。

    2. 设备快捷编辑

    选中列表中的节点点击“编辑”,会弹出轻量级的悬浮编辑气泡。

    在这里可以快速为单台设备指定独立的执行间隔(单位:秒)以及是否作为主节点的勾选框,极大提升了多节点下的微调效率。

    3. 系统配置(源数据库与全局策略)

    点击主界面的“设置”,即可进入整款软件的“大脑”——系统配置面板。

    配置主要分为两部分:

    • 源数据库配置:设定作为数据源的“主机IP”,并提供直观的 连接 测试按钮。下方可自由勾选需要同步的核心业务表(例如系统默认支持的 c_user_config、c_language_config 等)。
    • 全局配置:暴露出原生的 连接字符串(Connection String),便于高级用户微调端口、用户名、密码或连接超时参数。同时支持设置全局循环间隔及勾选“循环执行”。

    执行状态与日志展示

    三、 核心硬核技术实现(C# 逻辑架构)

    为了实现无卡顿的流畅体验,DBSync 的底层采用了多线程/异步轮询机制,确保在大批量数据读写时,WinForm 界面依然能够自由拖动、刷新日志。

    private async void btnStart_Click(object sender, EventArgs e)
    {
    LogHelper.WriteLog("开始执行数据同步");
    btnStart.Enabled = false;
    btnStop.Enabled = true;
    this.dataGridView1.Enabled = false;
    try
    {
    await Task.Run(() =>
    {
    if (this.dataGridView1.DataSource is List<DBTaskDTO> data)
    {
    taskInvoke = new TaskInvokeHelper(UpdateTaskInfo);
    var main = data.FirstOrDefault(p => p.IsMain);
    if (main == null)
    {
    MessageBox.Show("没有主机,无法同步");
    }
    var list = data.Where(p => p.IsCheck && !p.IsMain).ToList();
    if (list == null || list.Count == 0)
    {
    MessageBox.Show("未选中需要同步的从机");
    }
    SQLHelper.ConnectionString = Constant.ConnectionString.Replace("{IP}", main.IP);
    taskInvoke.Work(list);
    }
    });
    }
    catch (Exception ex)
    {
    MessageBox.Show("运行出错:" + ex.Message);
    }
    finally
    {
    btnStart.Enabled = true;
    btnStop.Enabled = false;
    this.dataGridView1.Enabled = true;
    LogHelper.WriteLog("数据同步执行结束");
    }
    }

    线程调用逻辑实现:

    public void Work(List<DBTaskDTO> list)
    {
    //按配置间隔循环执行
    do
    {
    foreach (var item in list)
    {
    if (IsStop)
    {
    IsStop = false;
    item.ExecuteResult = "同步数据提前终止";
    UpdateInfo?.Invoke(item);
    LogHelper.WriteLog("中断数据同步");
    return;
    }
    LogHelper.WriteLog(item.IP, "开始执行");
    try
    {
    item.BeginTime = DateTime.Now;
    item.IsWork = "执行中";
    UpdateInfo?.Invoke(item);
    DoWork(item);
    }
    catch (Exception ex)
    {
    item.ExecuteResult += ex.Message;
    LogHelper.WriteErrorLog($"{item.IP} 执行出现异常:{item.ExecuteResult}");
    }
    item.EndTime = DateTime.Now;
    item.IsWork = "空闲";
    UpdateInfo?.Invoke(item);
    LogHelper.WriteLog(item.IP, "执行结束");
    }
    if(Constant.IsEnable) Thread.Sleep(Constant.GlobalInterval * 1000);
    } while (Constant.IsEnable);
    }

    以下是该工具最核心的数据库连接验证与数据克隆的精简伪代码实现:

    public bool UserSync(string conn, DBTaskDTO info, string DBName)
    {
    try
    {
    // 获取并转换主机数据
    var list = LocalUser.QueryUserInfo(DBName);
    var settings = new JsonSerializerSettings { Converters = { new ExpandoObjectConverter() } };
    var json = JsonConvert.SerializeObject(list, settings);
    var res = JsonConvert.DeserializeObject<List<ExpandoObject>>(json, settings).Cast<dynamic>().ToList();

    if (!res.Any())
    {
    LogHelper.WriteLog(info.IP, "主机无数据,同步跳过");
    return true;
    }

    using (var userDAL = new UserDAL(conn))
    {
    if (userDAL.IsExist(DBName))
    {
    info.ExecuteResult += $"{DBName}表的自增约束未删除";
    LogHelper.WriteLog(info.IP, $"同步失败:{DBName}表的自增约束未删除");
    return false;
    }

    // 提取 ID 并执行同步
    var ids = res.Select(p => Convert.ToInt32(((IDictionary<string, object>)p)["ID"])).ToList();
    userDAL.DeleteOthre(ids, DBName);
    userDAL.AddOrUpdate(res, DBName);
    userDAL.SaveChange();

    LogHelper.WriteLog(info.IP, $"同步{DBName}成功,数量:{res.Count}");
    return true;
    }
    }
    catch (Exception ex)
    {
    info.ExecuteResult = $"同步{DBName}出错";
    LogHelper.WriteLog(info.IP, $"同步{DBName}出错:{ex.Message}");
    return false;
    }
    }

    多线程调用逻辑实现:

    public DBTaskDTO DoWork(DBTaskDTO info)
    {
    try
    {
    bool flag = true;
    var con = Constant.ConnectionString.Replace("{IP}", info.IP);
    if (Constant.TableName1 != "" && Constant.TableNameEnable1)
    {
    flag = UserSync(con, info, Constant.TableName1);
    }
    if (Constant.TableName2 != "" && Constant.TableNameEnable2)
    {
    flag = UserSync(con, info, Constant.TableName2);
    }
    if (Constant.TableName3 != "" && Constant.TableNameEnable3)
    {
    flag = UserSync(con, info, Constant.TableName3);
    }
    if (Constant.TableName4 != "" && Constant.TableNameEnable4)
    {
    flag = UserSync(con, info, Constant.TableName4);
    }

    if (flag)
    {
    info.ExecuteResult = Success;
    }
    }
    catch (Exception ex)
    {
    info.ExecuteResult = Faile + info.ExecuteResult + ex.Message;
    LogHelper.WriteErrorLog($"{info.IP} 同步出现异常:{ex.Message}");
    }
    return info;
    }

    相关阅读:工控机偶发连不上数据库:Aborted connection、C# 代码片段(三):WinForms 常用写法。

    四、 项目总结与下一步迭代规划

    目前这款 DBSync 已经在笔者的内部网络环境中稳定运行。通过定制的 循环间隔 机制,它在保证秒级同步响应的同时,对服务器 CPU 和内存的占用几乎可以忽略不计。

    后续优化方向:

  • 增量同步(Binlog机制):目前采用的是全量或覆盖更新,后续考虑引入时间戳或解析 Binlog,实现只同步变更数据,进一步压低跨网段的带宽开销。
  • 日志可视化升级:在主界面的“查看日志”中引入实时高亮流(类似 Tailwind 终端输出),让排错更加赏心悦目。
  • 相关阅读:C# 代码片段(一):文件、字符串、时间与序列化。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » DBSync:基于 C# WinForm 的轻量级多节点数据库同步工具
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!