大数据高效复制的处理案例分析总结

一个老客户提出这样的需求,希望将SQLServer中的某个表的数据快速复制到SQLite数据库里面以便进行定期的备份处理,数据表的记录大概有50多万条记录,表有100个字段左右,除了希望能够快速做好外,效率是第一位的,他自己测试总是在一两个小时的时间以上。客户提出这样的需求,我我觉得肯定是没有很好的利用事务的特性,否则速度应该会快得多,但是具体能快到什么程度,心里也不太确定。于是按照这个要求,把这样大的表数据复制作为一个案例来进行研究,最终大数据的复制处理,不到20分钟就可以完成全部数据的复制更新处理。本文主要介绍这个需求如何结合实际开发的需要进行处理,达到快速高效的复制数据的目的,并提供相关的实现思路和代码供参考学习。

1、复制数据的需求及开发思路

由于客户是需要做定期的数据备份,因此这样的复制是进行的,因此大数据的复制效率肯定是很重要的,应该尽可能的短时间完成。数据表的记录大概有50多万条记录,表有100个字段左右的需要也是比常规的表数据会多一些,因此需要做好很好的测试,我们根据这样的需求背景,使用一个测试案例来对性能进行测试。

这样多字段的表,数据字段的一一对应,手工肯定是很累的,所以我们使用代码生成工具Database2Sharp来进行快速开发,这样底层的处理我们就可以不用太过关注,而且可以为不同的数据处理,生成不同的数据访问层即可。

在底层我们主要是采用了微软的Enterprise Library的数据库访问模块,因此它能够很好抽象各种数据库的事务,以适应各种不同数据库的事务处理。使用微软的Enterprise Library模块,可以很好支持SQLSever、Oracle、Mysql、Access、SQLite等数据库。

开发框架,常见的分层模式,可以分为UI层、BLL层、DAL层、IDAL层、Entity层、公用类库层等等

框架的基类我们封装了大量的通用性处理函数,包括数据访问层、业务逻辑层的基类,所有的基类函数基本上都带有一个DbTransaction trans = null 的定义,就是我们可以采用事务,也可以默认不采用事务,是一个可选性的事务参数。

如数据访问接口和基于SQLServer的数据访问类的实现图示如下所示。

在最高级的抽象基类AbstractBaseDAL的数据访问层里面,都有大量关于数据操作和相关事务的接口可以使用,因此我们在底层继承的子类,如果我们处理数据的增删改查等操作,基本上就不需要做任何扩展性代码了,这样很符合我们快速开发的目的。

在框架的整个数据访问层,我们都定义了很多公用的、带有事务参数的接口,如果我们在常规的数据处理里面,使用事务的话,那么也是很方便的事情。使用事务的批量处理,对于SQLite的操作来说,效率是非常明显的,具体可以在我之前的随笔里《使用事务操作SQLite数据批量插入,提高数据批量写入速度,源码讲解》可以了解到,他们之间的处理效率是很大差距的。

 

2、使用代码生成工具生成所需的代码

上面讲到,开发这样的数据复制处理程序,这样多字段的表,数据字段的一一对应,手工肯定是很累的,所以我们使用代码生成工具Database2Sharp来进行快速开发。

因此使用代码生成工具来快速生成所需要的代码,展开数据库后,从数据库节点上,右键选择【代码生成】【Enterprise Library代码生成】就可以生成标准的界面层一下的代码了,由于我们整个案例是非标准的数据复制处理,界面部分不需要利用代码生成工具进行Winform界面的生成的。

生成代码的一步步操作,最后确认一下就可以生成相关的底层代码了

最后我们生成这样的BLL、DAL、IDAL、Entity几个层的项目代码,整个项目的代码各种继承关系已经处理好了,也就具有了基类拥有的增删改查等基础操作了。

我们做两个不同数据库的复制处理操作,关键还是要生成两个不同数据库访问类的代码(也就是生成一个标准的SQLServer后,复制一份代码,修改下继承基类即可实现),如下代码是两个数据访问类的代码,不用增加任何接口即可满足当前项目的需要的了。

最终我们的项目结构如下所示。

 

3、进行数据复制处理的Winform界面代码逻辑

为了方便整个复制过程的进度展示(很重要),我们设计了进度条以及文字内容,展示处理过程的进度和耗时等信息,最终界面设计如下所示。

整个界面设计利用后台线程的方式对数据复制进行处理,方便及时在界面显示进度而不阻塞界面线程。

具体的界面代码如下所示。

    public partial class FrmMain : Form{private TimeSpan ExecuteTime;private int currentCount = 0;private BackgroundWorker work = new BackgroundWorker();//使用后台线程进行处理,不阻塞界面显示public FrmMain(){InitializeComponent();//定义后台线程的处理work.DoWork += work_DoWork;work.WorkerReportsProgress = true;work.ProgressChanged += work_ProgressChanged;work.RunWorkerCompleted += work_RunWorkerCompleted;}//线程完成后通知结束void work_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){this.toolStripProgressBar1.Value = 100;this.toolStripProgressBar1.Visible = false;MessageUtil.ShowTips("操作完成");ShowMessage(this.toolStripProgressBar1.Value);//完成
        }/// <summary>/// 在界面显示文本信息/// </summary>/// <param name="percent">完成百分比</param>private void ShowMessage(int percent){if (this.ExecuteTime != null){this.lblTips.Text = string.Format("[当前完成数量:{0},完成百分比:{1}, 执行耗时:{2}毫秒 | {3}分钟{4}秒]",this.currentCount, percent, this.ExecuteTime.TotalMilliseconds, this.ExecuteTime.Minutes, this.ExecuteTime.Seconds);}}/// <summary>/// 报告进度的时候,显示相关的数量、耗时等内容/// </summary>void work_ProgressChanged(object sender, ProgressChangedEventArgs e){this.toolStripProgressBar1.Value = e.ProgressPercentage;this.statusStrip1.Refresh();ShowMessage(e.ProgressPercentage);}/// <summary>/// 后台线程执行的逻辑代码/// </summary>void work_DoWork(object sender, DoWorkEventArgs e){CopyDataUtil util = new CopyDataUtil();//使用一个Action的Lamda表达式,执行通知界面处理util.Start((percent, ts, current) =>{work.ReportProgress(percent);this.ExecuteTime = ts;this.currentCount = current;});}private void btnCopy_Click(object sender, EventArgs e){if(!work.IsBusy){//如果每次要求使用空白数据库测试,那么先删除旧数据库,再复制备份过去即可string dbfile = Path.Combine(Environment.CurrentDirectory, "localdb.db");string bakfile = Path.Combine(Environment.CurrentDirectory, "db.db");if (this.chkCopyEmptyDb.Checked && File.Exists(dbfile)){File.Delete(dbfile);File.Copy(bakfile, dbfile, true);}//显示进度条,并异步执行线程this.toolStripProgressBar1.Visible = true;work.RunWorkerAsync();}}private void FrmMain_FormClosing(object sender, FormClosingEventArgs e){//取消注册的相关事件,防止退出的时候出现异常if(work != null && work.IsBusy){work.ProgressChanged -= work_ProgressChanged; //取消通知事件work.RunWorkerCompleted -= work_RunWorkerCompleted;//取消完成事件
                work.Dispose();}}}

在上面的窗体界面代码里面,最为关键的代码就是具体后台进程的处理逻辑,如下代码所示。

        /// <summary>/// 后台线程执行的逻辑代码/// </summary>void work_DoWork(object sender, DoWorkEventArgs e){CopyDataUtil util = new CopyDataUtil();//使用一个Action的Lamda表达式,执行通知界面处理util.Start((percent, ts, current) =>{work.ReportProgress(percent);this.ExecuteTime = ts;this.currentCount = current;});}

上面的处理逻辑为了方便,把数据的复制内容放到了一个辅助类里面,并在辅助类的Start方法里面传入了界面通知的Action处理函数,这样我们在CopyDataUtil 处理的时候就可以随时进行消息的通知了。

数据复制的Start方法定义如下所示。

        /// <summary>/// 开始执行赋值/// </summary>public void Start(Action<int, TimeSpan, int> doFunc){StartTime = DateTime.Now;//计时开始
InternalCopry(doFunc);//处理数据复制逻辑,并执行外部的函数
EndTime = DateTime.Now;//计时结束}

整个辅助类CopyDataUtil 类里面定义了两个不同数据库类型的对象,方便数据库的赋值处理操作,并且定义了开始时间,结束时间,这样可以统计总共的耗时信息,如下代码所示。

    /// <summary>/// 复制数据的处理类/// </summary>public class CopyDataUtil{//使用一个计时器,对操作记录进行计时private DateTime StartTime, EndTime;//SQLServer数据库表对象private ProductSqlServer sqlserver = null;//SQLite数据表对象private ProductSqlite sqlite = null;public CopyDataUtil(){//构建对象,并指定SQLServer的数据库配置项sqlserver = new ProductSqlServer();sqlserver.DbConfigName = "sqlserver";//构建对象,并指定SQLite的数据库配置项sqlite = new ProductSqlite();sqlite.DbConfigName = "sqlite";}

整个复制数据的逻辑,主要就是基于事务性的处理,按照分页规则,每次按照一定的数量,批量从SQLServer里面取出数据,然后插入SQLite数据库里面,使用事务可以是的SQLite的数据写入非常高效快速,具体代码如下所示。

        /// <summary>/// 大数据复制的处理逻辑/// </summary>/// <param name="doFunc">外部调用的函数</param>private void InternalCopry(Action<int, TimeSpan, int> doFunc){//设置主键,并指定分页数量大小,提高检索效率string primaryKey = "h_id";int pageSize = 1000;PagerInfo info = new PagerInfo(){PageSize = pageSize, CurrenetPageIndex =1};//根据数据的总数,取得总页数int totalPageCount = 1;int totalCount = sqlserver.GetRecordCount();if (totalCount % pageSize == 0){totalPageCount = totalCount / pageSize;}else{totalPageCount = totalCount / pageSize + 1;}totalPageCount = (totalPageCount < 1) ? 1 : totalPageCount;//利用事务进行SQLite数据写入,提高执行响应效率DbTransaction trans = sqlite.CreateTransaction();if (trans != null){//根据每页数量,依次从指定的页数取数据for (int i = 1; i <= totalPageCount; i++){info.CurrenetPageIndex = i;//设定当前的页面,并进行数据获取int j = 1;List<ProductInfo> list = sqlserver.FindWithPager("1=1", info, primaryKey, false);foreach (ProductInfo entity in list){//取得当前数量和进度百分比int current = (i - 1) * pageSize + j;int percent = GetPercent(totalCount, current);//计算程序耗时,执行外部函数进行界面通知TimeSpan ts = DateTime.Now - StartTime;doFunc(percent, ts, current);//执行通知处理//如果不存在主键记录,则写入,否则更新if (!sqlite.IsExistKey(primaryKey, entity.H_id, trans)){sqlite.Insert(entity, trans);}else{sqlite.Update(entity, entity.H_id, trans);}j++;}                    }trans.Commit();}}

至此,整个项目的代码就基本上介绍完毕了,测试整个复制过程,单表50多万的数据,100个字段左右,在开发机器上20分钟不到就复制完成,确实是很不错的成绩了,如果修改为服务器的环境专门做复制处理,肯定速度还会提高不少。

 

转载于:https://www.cnblogs.com/wuhuacong/p/5091251.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397015.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

@autowired注解注入为null_Intellij IDEA中Mybatis Mapper自动注入警告的6种解决方案

相信使用Mybaits的小伙伴们一定会经常编写类似如下的代码&#xff1a;可以看到 userMapper 下有个红色警告。虽然代码本身并没有问题&#xff0c;能正常运行&#xff0c;但有个警告总归有点恶心。本文分析原因&#xff0c;并列出解决该警告的几种方案。原因众所周知&#xff0c…

五 Python之socket网络编程

<1>socket概念 socket通常也称作"套接字"&#xff0c;用于描述IP地址和端口&#xff0c;是一个通信链的句柄&#xff0c;应用程序通常通过"套接字"向网络发出请求或者应答网络请求。ocket本质上就是在2台网络互通的电脑之间&#xff0c;架设一个通道…

html ie乱码_Java 0基础入门(初识Html)

在学习Java之前&#xff0c;我们需要了解一定的前端知识。毕竟页面才是用户真正看到的&#xff0c;而且也是体现Java后端逻辑结果的地方。学习HTML后&#xff0c;能够制作界面美观大方的静态网站(更复杂的功能需要JavaScript脚本一起来实现)。HTML制作的网页。所需要的Html开发…

live server插件怎么用_分享几个我日常使用的VS Code插件

在这篇文章中&#xff0c;我想介绍一下自己日常使用中最喜欢的Visual Studio Code扩展。大多数时候&#xff0c;我用VS Code编写的是JavaScript应用程序(标准JavaScript、React、Angular、NodeJS……各式各样)。Bracket Pair Colorizer 2这个扩展很不错&#xff0c;可以帮助我搞…

Android应用程序开发

第一章 Android应用初体验 1.1应用基础 activity是Android SDK中Activity类的一个具体实例&#xff0c;负责管理用户与信息屏的交互。 应用的功能是通过编写一个个Activity子类来实现的。 布局定义了一系列用户界面对象以及它们显示在屏幕上的位置。组成布局的定义保存在XML文件…

phonegap

phonegap 框架详解 转自&#xff1a;http://www.cnblogs.com/hubcarl/p/4216844.html首先, 来看一下phonegap 初始化流程以及Native 与 JS 交互流程图。 说明&#xff1a;socket server模式下, phonegap.js 源码实现的采用1 毫秒执行一次XHR请求, 当Native JS 队列里面有JS语…

j2ee核心模式_Operator和Sidecar正在成为软件交付新模式

现如今的开发人员希望可以开发出具备弹性和可扩展的分布式系统。该系统受益于软件复用和开源模型创新&#xff0c;针对安全性问题能够轻易完成补丁更新并进行低风险的升级。该系统不可能通过带有各种嵌入式语言库的应用程序框架来实现。最近&#xff0c;一篇关于“多运行时微服…

微信JS-SDK选择相册或拍照并上传PHP实现

理解&#xff1a;微信上传接口是拍照&#xff0c;或者选择本地照片&#xff0c;上传到微信的服务器&#xff0c;获取到一个id&#xff0c;通过token与这个id获取到图片&#xff0c;保存到服务器即可。 效果 通过微信js接口&#xff0c;调用底层程序。 需要引入js文件&#xff0…

android socket 长连接_TCP/IP,http,socket,长连接,短连接

点击上方蓝色字体&#xff0c;选择“标星公众号”优质文章&#xff0c;第一时间送达上一篇&#xff1a;这300G的Java资料是我师傅当年给我的&#xff0c;免费分享给大家下一篇&#xff1a;这200G的Java实战资料是我师傅当年教我的第二招作者 | ksfzhaohui来源 | my.oschina.net…

二、Python安装扩展库

第一步:推荐easy_install工具 下载地址:https://pypi.python.org/pypi/setuptools 下载"ez_setup.py"文件; 通过运行cmd命令找到ez_setup.py文件所在目录,通过命令[python ez_setup.py]执行安装easy_install 安装成功截图 第二步:安装扩展酷 例如安装"suds"…

ORACLE 10.2.01升级10.2.05 for windows 详细文档

最近要做一个数据库的升级工作&#xff0c;提前在自己的PC机上练习了一下&#xff0c;这种文档在网上很多&#xff0c;但是大多都是使用命令编辑脚本&#xff0c;其实数据库还有一个DBUA的升级工具可以使用&#xff0c;使升级工作方便了很多。 OS环境&#xff1a;windows XP 32…

stm32正交编码器 原理图_恶劣环境下应用的电感式增量编码器和绝对编码器

编码器可分为两种基本类型 - 增量编码器和绝对编码器。增量编码器的显着特征是它报告角度的变化。换句话说&#xff0c;当增量编码器通电时&#xff0c;它不会报告其角位置&#xff0c;直到它具有测量的参考点。绝对编码器明确地在比例或范围内报告其位置。换句话说&#xff0c…

python基础代码的含义_Python基础学习篇

原标题&#xff1a;Python基础学习篇 1、编码 默认情况下&#xff0c;Python 3 源码文件以 UTF-8 编码&#xff0c;所有字符串都是unicode 字符串。 当然你也可以为源码文件指定不同的编码&#xff1a;# -*- coding: cp-1252 -*- 2、标识符 第一个字符必须是字母表中字母或下划…

java面向对象super_【JavaSE】面向对象之super、final

一、super关键字它是一个指代变量&#xff0c;用于在子类中指代父类对象。1.作用指代父类对象区分子父类同名的成员变量&#xff0c;区分父类中成员变量和子类中同名的局部变量2.使用与this相同&#xff0c;调用父类成员变量和成员方法&#xff1a;super.xx super.xxx()调用父类…

mac下的svn服务器建立

MAC下的SVN服务器建立: from : http://blog.csdn.net/q199109106q/article/details/8655204 在Windows环境中&#xff0c;我们一般使用TortoiseSVN来搭建svn环境。在Mac环境下&#xff0c;由于Mac自带了svn的服务器端和客户端功能&#xff0c;所以我们可以在不装任何第三方软件…

php 字符串 替换 最后,php如何替换字符串中的最后一个字符

php替换字符串中的最后一个字符的方法是&#xff1a;可以通过preg_replace()函数来实现。该函数的语法为&#xff1a;【preg_replace(mixed $pattern, mixed $replacement, mixed $subject】。要替换字符串中的最后一个字符&#xff0c;可以通过preg_replace()函数来实现。(如果…

logback的使用和logback.xml详解

原文地址&#xff1a;https://www.cnblogs.com/warking/p/5710303.html#4046335 作者&#xff1a;行走在云端的愚公 一、logback的介绍   Logback是由log4j创始人设计的另一个开源日志组件,官方网站&#xff1a; http://logback.qos.ch。它当前分为下面下个模块&#xff1a; …

tcp协议的主要功能是什么_前端要知道的网络知识一:TCP/IP 协议到底在讲什么...

你之所以不知道那套书在讲什么&#xff0c;是因为你还没有认识到网络协议有什么用&#xff0c;怎么用&#xff0c;以什么形式在使用&#xff0c;网络协议的概念很简单&#xff0c;就几句话&#xff0c;你只知道网络协议的概念&#xff0c;只知道很多大神都推荐这套书&#xff0…

mysql创建定时器(event),查看定时器,打开定时器,设置定时器时间

为什么80%的码农都做不了架构师&#xff1f;>>> 由于项目需要创建定时器&#xff08;evevt&#xff09;&#xff0c;所以就百度了一下&#xff0c;发现基本都是来源于一个模板&#xff0c;有些功能还不全&#xff0c;现在自己总结一下。 注&#xff1a;mysql版本是…

音频视频

1.IOS视频播放代码&#xff08;添加MediaPlayer.framework和#import&#xff09; -(void)playMovie:(NSString *)fileName{//视频文件路径NSString *path [[NSBundle mainBundle] pathForResource:fileName ofType:"mp4"];//视频URLNSURL *url [NSURL fileURLWithP…