LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

C# 打造企业级 SQL Server 数据库同步工具

admin
2026年11月9日 23:16 本文热度 424

前言

日常开发中,大家是否也遇到过这样的场景:测试环境的数据太久没更新,跑出来的结果和生产环境对不上;或者客户现场部署了一个新版本,但历史数据还在老系统里,迁移起来又慢又容易出错?更头疼的是,有些客户的数据库藏在内网深处,只能通过跳板机访问,外网工具根本用不了。这时候,一个轻量、可控、能跑在特殊网络环境下的数据库同步工具,就成了刚需。

今天分享的这个工具,正是为了解决这类问题而生——它完全基于 C# 开发,开源、无依赖、功能完整,并且在最新版本中新增了增量同步能力,特别适合那些"网络受限但又必须同步数据"的真实业务场景。

为什么需要自研同步工具?

市面上当然有成熟的数据库同步方案,比如 SSIS、Redgate 或云厂商提供的 DTS 服务。但它们要么收费昂贵,要么配置复杂,更重要的是,在客户内网或通过 VPN 跳板机访问的环境下,这些外部工具往往无法运行。

而手动导出 .bak 文件或生成 INSERT 脚本,效率低、易出错,还难以自动化。

于是,我决定自己写一个:轻量、图形化、支持结构与数据同步,还能智能判断哪些数据是新增或变更的——这就是这个工具的核心出发点。

核心功能一览

智能结构同步:自动比对源表与目标表的列定义、主键、默认值约束,差异部分可选择重建或增量调整。

灵活的数据策略:支持全量覆盖、增量追加(基于主键判断)、批量分页处理,避免内存溢出。

可视化操作界面:连接配置、表选择、进度条、日志输出一目了然。

配置持久化:保存常用连接和同步选项,下次直接加载,省去重复输入。

异常容错机制:单表失败不影响整体流程,支持手动中断,日志详细可追溯。

最关键的是,新增了增量同步功能——只传输变化的数据,大幅提升效率,尤其适合每日定时同步的场景。

运行效果

配置类设计

工具首先定义了清晰的配置模型,便于序列化和用户交互:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespaceAppSqlServerSynTool
{
    #region 配置和选项类
    [Serializable]
    publicclassSyncConfig
    {
        publicstring SourceConnectionString { get; set; }
        publicstring TargetConnectionString { get; set; }
        publicbool SyncStructure { get; set; }
        publicbool SyncData { get; set; }
        publicbool CreateTargetTables { get; set; }
        publicbool TruncateTargetTables { get; set; }
        publicbool IncrementalSync { get; set; } // 新增这个,以前版本没有
        publicint BatchSize { get; set; }
        public List<string> SelectedTables { get; set; }
    }

    publicclassSyncOptions
    {
        publicbool SyncStructure { get; set; }
        publicbool SyncData { get; set; }
        publicbool CreateTargetTables { get; set; }
        publicbool TruncateTargetTables { get; set; }
        publicint BatchSize { get; set; }
    }
    #endregion
}

通过 [Serializable] 特性,配置可直接保存为 XML 文件,方便复用。IncrementalSync 字段是本次升级的关键开关。

增量同步核心逻辑

增量同步的核心在于:如何判断一条记录是否已存在?

答案是:基于主键查询。如果目标表中不存在该主键,则插入;若存在且用户启用了"更新"选项,则执行更新。

private async Task<(int, int)> SyncWithPrimaryKeyAsync(string tableName, 
    DataTable sourceData, List<string> primaryKeys, 
    SqlConnection targetConnection, SqlTransaction transaction, int batchSize)
{
    int insertCount = 0;
    int updateCount = 0;
    
    string primaryKeyWhere = string.Join(" AND ", 
        primaryKeys.Select(pk => $"[{pk}] = @{pk}"));
    
    string selectExistsSql = $@"
        SELECT COUNT(*) FROM [{tableName}] 
        WHERE {primaryKeyWhere}"
;

    foreach (DataRow row in sourceData.Rows)
    {
        using (var selectCmd = new SqlCommand(selectExistsSql, targetConnection, transaction))
        {
            foreach (string pk in primaryKeys)
            {
                selectCmd.Parameters.AddWithValue($"@{pk}", row[pk] ?? DBNull.Value);
            }
            
            int existingCount = (int)await selectCmd.ExecuteScalarAsync();
            
            if (existingCount == 0)
            {
                await InsertNewRecord(tableName, row, targetConnection, transaction);
                insertCount++;
            }
            elseif (chkUpdateData.Checked)
            {
                await UpdateExistingRecord(tableName, row, primaryKeys, 
                    targetConnection, transaction);
                updateCount++;
            }
        }
    }
    return (insertCount, updateCount);
}

整个过程包裹在事务中,确保原子性,同时返回插入和更新的数量,便于统计。

表结构智能比对

结构同步不是简单地 DROP + CREATE,而是先做差异分析:

private async Task<bool> CompareTableStructureAsync(string tableName, 
    DataTable sourceColumnsSchema, List<string> sourcePrimaryKeys, 
    Dictionary<string, string> sourceDefaultConstraints, 
    SqlConnection targetConnection
)

{
    try
    {
        DataTable targetColumnsSchema = await GetTargetTableColumnsAsync(tableName, targetConnection);
        List<string> targetPrimaryKeys = await GetTargetPrimaryKeysAsync(tableName, targetConnection);
        
        if (!CompareColumns(sourceColumnsSchema, targetColumnsSchema))
        {
            LogMessage($"表 {tableName} 的列结构发生变化");
            returntrue; // 需要重建
        }
        
        if (!ComparePrimaryKeys(sourcePrimaryKeys, targetPrimaryKeys))
        {
            LogMessage($"表 {tableName} 的主键结构发生变化");
            returntrue; // 需要重建
        }
        
        returnfalse; // 结构无变化
    }
    catch (Exception ex)
    {
        LogMessage($"比较表 {tableName} 结构时出错: {ex.Message},将重建表");
        returntrue; // 出错时选择重建表以确保安全
    }
}

这种"保守但安全"的策略,避免因微小结构差异导致同步失败。

高性能批量写入

对于全量同步,使用 SqlBulkCopy 是性能最优解:

private async Task SynchronizeTableDataFullAsync(string tableName)
{
    using (SqlConnection targetConnection = new SqlConnection(_targetConnectionString))
    {
        await targetConnection.OpenAsync();
        
        using (SqlTransaction transaction = targetConnection.BeginTransaction())
        {
            try
            {
                using (SqlBulkCopy bulkCopy = new SqlBulkCopy(
                    targetConnection, SqlBulkCopyOptions.Default, transaction))
                {
                    bulkCopy.DestinationTableName = tableName;
                    bulkCopy.BatchSize = (int)numericBatchSize.Value;
                    bulkCopy.BulkCopyTimeout = 600;
                    bulkCopy.EnableStreaming = true; // 启用流式处理
                    
                    foreach (DataColumn column in sourceData.Columns)
                    {
                        bulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                    }
                    
                    await bulkCopy.WriteToServerAsync(sourceData);
                    LogMessage($"成功向目标表 {tableName} 插入 {sourceData.Rows.Count} 行数据");
                }
                transaction.Commit();
            }
            catch (Exception ex)
            {
                transaction.Rollback();
                throw;
            }
        }
    }
}

配合 EnableStreaming 和合理的 BatchSize,即使百万级数据也能平稳处理。

用户界面与体验优化

跨线程更新进度条是 WinForm 开发的经典问题,这里做了完整防护:

private void UpdateProgress(int current, int total)
{
    if (this.InvokeRequired)
    {
        if (this.IsHandleCreated && !this.IsDisposed)
        {
            try
            {
                this.Invoke(new Action(() => UpdateProgressInternal(current, total)));
            }
            catch (ObjectDisposedException) { return; }
            catch (InvalidOperationException) { return; }
        }
    }
    else
    {
        UpdateProgressInternal(current, total);
    }
}

private void UpdateProgressInternal(int current, int total)
{
    if (!this.IsDisposed)
    {
        progressBarMain.Value = current;
        labelProgress.Text = $"{current}/{total}";
        toolStripProgressBar.Value = (int)((double)current / total * 100);
    }
}

确保在长时间同步过程中界面不卡死、不崩溃。

实战配置示例

增量同步(日常更新)

var config = new SyncConfig
{
    SourceConnectionString = "Server=prod;Database=CRM;...",
    TargetConnectionString = "Server=test;Database=CRM_Test;...",
    SyncStructure = true,
    SyncData = true,
    IncrementalSync = true,  // 关键!
    BatchSize = 5000
};

全量迁移(首次部署):

var migrationConfig = new SyncConfig
{
    SyncStructure = true,
    SyncData = true,
    CreateTargetTables = true,
    TruncateTargetTables = true,
    IncrementalSync = false,
    BatchSize = 10000
};

总结

这个数据库同步工具虽小,却凝聚了多个工程实践要点:配置管理、异常处理、事务控制、高性能 I/O、UI 线程安全等。它不是为了替代商业产品,而是为那些"工具用不了、手动太麻烦"的边缘场景提供一个可靠、透明、可调试的解决方案。

最重要的是,代码完全掌握在自己手中——你可以根据客户需求,轻松加入字段映射、数据脱敏、定时任务等扩展功能。在受限网络环境下,这种自主可控的能力,往往比功能丰富更重要。

目前该工具已在多个客户项目中稳定运行,显著减少了数据同步的人工成本和出错率。如果你也面临类似挑战,不妨试试看。

关键词

SQL Server、#数据库同步、#增量同步、C#、#SqlBulkCopy、#WinForm、#主键比对、#配置持久化、#事务处理、#开源工具

作者:技术老小子

出处:mp.weixin.qq.com/s/GmXP8hLmeWpnhOJPUbOvZg
声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!



END




阅读原文:原文链接


该文章在 2026/11/10 11:00:27 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-2  粤公网安备44030602007207号