公司数据同步程序的设计思路(二)
果然 上一篇末尾我的“预言”实现了。
我现在不仅需要向云上拉取数据,还需要将云上的数据推送到内网数据库。
现在面临的相关的问题存在以下几点:
1.我希望每次修改配置无需去部署机更改配置。目前的配置框架,每增加一个服务圈都需要去部署机器上改配置文件。因为部署机器不是本部门的,所以限制挺大。每次登的时候要申请,还要开VPN,跳堡垒机。今早登上去就被顶下来了。然后听说是对接部门在跟客户演示,让我们先不要连。
2.向内网机同步数据的机制初步决定用每天全表更新,每天同步时清空表再添加,使用 ado sqlsever 的批量添加,速度很快。但有一个重要的问题是,内网数据库的结构需要同云上保持一致。云上由于业务需求可能需要随时改表,那么肯定不希望每次都再回来改一下这边的数据库结构,最好可以实现同步。
那么思路大概如下:
云上提供一个接口,该接口返回需要(可以)同步的表 Key 值。由部署机器上的同步程序调用,调用后获得一组可同步的表 Key。然后循环表 Key 请求云上的接口,每次获取一个表的真实表名和数据。
部署程序获取到一个表之后,
判断内网数据库是否存在该表,不存在的话则根据 table 生成建表语句并执行,若表存在,
则对比表的列与 datatable的列 是否一样,不同的话则生成修改语句并执行。
在确定结构相同后,则进行批量的导入。然后进行下个表。
总的来说,不存在什么难点,但我没实现过根据 datatable 去对比数据库的结构生成修改语句,趁着这个机会写一下,也算积累自己的代码了。
放些关键代码:
1 using (var db = DBFactory.GetDB()) 2 { 3 var sb = new System.Text.StringBuilder(); 4 5 Print.Info($"判断表是否存在: {table.TableName}"); 6 var sql = $"SELECT COUNT(*) From sysobjects WHERE name = '{Program.TableName + key}' AND xtype = 'u'"; 7 var tableCount = db.ExecuteScalar<int>(sql); 8 if (tableCount < 1) 9 { 10 Print.Info($"{table.TableName} 表不存在,生成建表语句"); 11 12 // 新建表 13 sb.Append($"create table {Program.TableName + key}("); 14 for (int i = 0; i < table.Columns.Count; i++) 15 { 16 var colItem = table.Columns[i]; 17 sb.Append($"{colItem.ColumnName} {GetType(colItem)}"); 18 if (i + 1 != table.Columns.Count) 19 { sb.Append(","); } 20 } 21 sb.Append($")"); 22 sql = sb.ToString(); 23 24 Print.Info($" {table.TableName} 建表语句 : {sql}"); 25 Print.LogInfo("执行结果:" + db.Execute(sql).ToString()); 26 } 27 else 28 { 29 sb.Clear(); 30 Print.Info($"表存在: {table.TableName}"); 31 Print.Info($"判断表列是否对应: {table.TableName}"); 32 foreach (DataColumn item in table.Columns) 33 { 34 //判断列是否存在 35 sql = $"SELECT COUNT(*) FROM syscolumns WHERE id = object_id('{Program.TableName + key}') AND name = '{ item.ColumnName }'"; 36 var colCount = db.ExecuteScalar<int>(sql); 37 if (colCount < 1) 38 { 39 sb.Append($"alter table {Program.TableName + key} add {item.ColumnName} {GetType(item)};"); 40 } 41 } 42 if (sb.Length > 0) 43 { 44 sql = sb.ToString(); 45 Print.Info($"执行建列语句: {sql.ToString()}"); 46 Print.LogInfo($"执行建列语句结果:" + db.Execute(sql).ToString()); 47 } 48 } 49 //删除数据 50 Print.Info($"执行删除语句: {sql.ToString()}"); 51 db.Execute($"DELETE FROM {Program.TableName + key}"); 52 //插入数据 53 SqlBulkCopyByDatatable(db.ConnectionString, $"{Program.TableName + key}", table); 54 }
这是同步一个表的代码,包括了生成建列和建表语句。
SQL Sever 批量同步的代码:
1 public static bool SqlBulkCopyByDatatable(string connectionString, string TableName, DataTable dt) 2 { 3 bool isSuccess = false; 4 using (SqlConnection conn = new SqlConnection(connectionString)) 5 { 6 using (SqlBulkCopy sqlbulkcopy = new SqlBulkCopy(connectionString)) 7 { 8 try 9 { 10 sqlbulkcopy.DestinationTableName = TableName; 11 sqlbulkcopy.BulkCopyTimeout = 0; 12 for (int i = 0; i < dt.Columns.Count; i++) 13 { 14 Print.Info((i + 1) + "/" + dt.Columns.Count + " " + dt.Columns[i].ColumnName); 15 sqlbulkcopy.ColumnMappings.Add(dt.Columns[i].ColumnName, dt.Columns[i].ColumnName); 16 } 17 sqlbulkcopy.WriteToServer(dt); 18 isSuccess = true; 19 } 20 catch (System.Exception ex) 21 { 22 Print.Err(ex.Message); 23 throw ex; 24 } 25 } 26 } 27 return isSuccess; 28 }
代码里还是有很多可以优化的地方的,但是我最近工期很紧。年底各种项目都要求最后的需求完结,还有年初新的项目。