背景介绍:
国内一流的财经门户站点做栏目调整,需要调整相应的数据调整。
目前站点数据库采用的是 id主键自增长,根据具体业务,将数据存在多张表。
相同业务的,数据存在一张表。每一条新闻资讯都有相应的新闻类型id 和新闻id。通过 http:// www.xx.com/{type}-{newsid}.html 方式,去读取相关的新闻资讯,其中type 表示新闻类别,newsid表示新闻id。每个资讯频道有多个子栏目,类似于和讯网,
新闻频道包含了 滚动、国内、国际等资讯,在新闻表中,使用type 类别进行区分。
同一频道下或者多个频道下 的数据存放在一张表。
不同频道或者耦合性比较低的存放在不同的表中, 类似于 新闻是张新闻表,股票是张股票表。
运营团队提出,在新的形势下,需要对整站的栏目进行调整,同时要对搜索引擎收录的资讯做301重定向,在数据清理过程中,需要提供重定向依据。
任务拆解和具体实现:
整体需求大致分类:
1、同一张数据表之间的调整,仅仅调整资讯type ,需要生成 重定向依据
类似于以前的地址 http://news.xx.com/111,1101.html 重定向到 http:// news.xx.com/113,1101.html
2、不同数据表之间的数据合并,因为主键使用int 自增长,所以避免不了id重复,需要提供重定向依据,类似于
http://news.xx.com/111,1101.html 重定向到 http://stock.xx.com/222,2202.html
根据以上需求,决定使用 C# 控制台直接跑数据,然后将重定向的 数据存入指定的数据表中。
在数据库连接的选择上,有3中,
- linq to sql;
- SqlHpler 帮助类;
- Dapper
最终选择了 Dapper,原因是据说速度比Ado.net 快。2、接触下这个轻型的 Orm。
新建控制台应用程序
添加Dapper 引用。通过 nuget 安装Dapper, 依次打开 工具->Nuget包管理器->程序包管理器控制台,在 程序包管理器控制台 输入Install-Package Dapper 后,敲回车,静等安装成功提示。
完成安装后,创建Dapper帮助类 DapperHelper,在App.config中配置数据库连接字符串。代码如下:
1 public class DapperHelper 2 { 3 private static string mssqlConn = ConfigurationManager.ConnectionStrings["appConn"].ToString().Trim(); 4 5 /// <summary> 6 /// Dapper 帮助类 7 /// </summary> 8 /// <returns></returns> 9 public static DbConnection MssqlConnection() 10 { 11 SqlConnection conn = new SqlConnection(mssqlConn); 12 conn.Open(); // 打开连接 13 14 return conn; 15 } 16 }
测试数据库连接情况
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 // 测试 数据库连接情况 6 using (DbConnection conn = DapperHelper.MssqlConnection()) 7 { 8 dynamic time = conn.QueryFirstOrDefault("select getdate();"); 9 } 10 } 11 }
此时已经可以联通数据库了。
因为是做数据的合并和迁移的,需要做相关的日志记录和打印当前情况,做了简单的日志处理帮助类,
为什么不用log4net 或者其他的呢?
因为本身就比较简单,所以就造了个轮子,核心代码如下:
1 /// <summary> 2 /// 简单日志帮助类 3 /// </summary> 4 public class LogHelper 5 { 6 7 private static object loker = new object(); 8 9 /// <summary> 10 /// 添加日志 11 /// </summary> 12 /// <param name="log"></param> 13 public static void Write(string log) 14 { 15 log = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\r\n" + log; 16 17 WriteTextLog(log); //记录日志到文本文件 18 Console.WriteLine(log); //打印日志到控制台 19 } 20 21 /// <summary> 22 /// 写日志到文本文件 23 /// </summary> 24 /// <param name="log"></param> 25 private static void WriteTextLog(string log) 26 { 27 lock (loker) // 为什么要用 lock?主要是因为 如果并发的话,会有异常的 28 { 29 string path = AppDomain.CurrentDomain.BaseDirectory + @"Log\"; 30 if (!Directory.Exists(path)) 31 Directory.CreateDirectory(path); 32 33 string fileFullPath = path + DateTime.Now.ToString("yyyy-MM-dd") + ".System.txt"; 34 35 StreamWriter sw; 36 if (!File.Exists(fileFullPath)) 37 { 38 sw = File.CreateText(fileFullPath); 39 } 40 else 41 { 42 sw = File.AppendText(fileFullPath); 43 } 44 sw.WriteLine(log); 45 sw.Close(); 46 } 47 48 } 49 }
调用 方式 LogHelper.Write("清洗程序已经启动");
会自动打印到控制台和 记录日志。
主要涉及到的功能点:
资讯表主键为ID自增张,有些不同表之间的ID有重复,怎么处理相关的ID?
我这边的思路,先将数据按照分页大小取出来,每次清洗5000条记录,取出本表最大值,然后再去设置表的 标识值
代码:
1 dynamic _maxId = conn.QueryFirst("select max(id) as maxId from CLOUD_NEWS_GLOBAL "); 2 int maxId = int.Parse(_maxId.maxId.ToString()); 3 maxId = maxId + 1000; 4 conn.Execute("DBCC CHECKIDENT(CLOUD_NEWS_GLOBAL,RESEED," + maxId + ")"); 5
因为资讯的主键值会做改变,所以插入代码主要是用的以下Sql。
1 conn.Execute( 2 @"SET IDENTITY_INSERT CLOUD_NEWS_GLOBAL ON 3 INSERT INTO CLOUD_NEWS_GLOBAL([ID],[INDUSTRYID],...) 4 values(@ID ,@INDUSTRYID ....) 5 SET IDENTITY_INSERT CLOUD_NEWS_GLOBAL OFF", 6 info); 7
主要是设置允许 主键显示插入值。
总结:
为什么没有采用异步的方式开几个线程一起搞?主要是我的观点是,保证数据平稳的迁移合并,比速度要重要。宁愿画点时间盯着控制
台程序输出日志