分享好友 编程语言首页 频道列表

用perl做数据库迁移,从MSSQL到MYSQL(三)--V1.1版~多线程+handlerSocket

perl  2023-02-09 08:170

从前边的程序的运行情况来看,程序是可以运行的,但速度太扯了,在读写1000W条之前速度还是可以的(大概2000条/秒左右),但过了1000W之后(变成400条/秒左右),当然这个与SQL SERVER读取,网络还有服务器等性能都是有关系的,但,这速度,不晓得有测试过的朋友受不受不了,我反正是受不了的,于是想了下,单线程慢,咱得改吧。改成多线程,多进程嘛。

另外再啰嗦一句,经小弟实测,改之后,效率真是快很多。。。。

不再啰嗦,直接上代码吧。

  1 #!/usr/bin/perl
  2 use DBI;
  3 use Switch;
  4 use strict;
  5 use Net::HandlerSocket;
  6 use threads;
  7 use Time::HiRes 'time';
  8 
  9 my $source_name = "##ODBC配置##";
 10 my $source_user_name = "##隐去的MSSQL数据库用户名##";
 11 my $source_user_psd = '##隐去的MSSQL数据库密码##';
 12 
 13 my $aim_ip='##隐去的目标MySQL数据库IP##';
 14 my $aim_db_name = "##隐去的目标MySQL数据库名##";
 15 ##关于HandlerSocket的安装,配置神马的,小编不想啰嗦,网上很多现成的东西。只是很纠结,以前HandlerSocket有一个很好的官网,不晓得怎么回事,被干了~~
 16 my $hs_port = 9999;
 17 
 18 my $dbh=DBI->connect("dbi:ODBC:$source_name",$source_user_name,$source_user_psd);
 19 #获取所有的用户表,不导有地理字段的表
 20 #地理字段的表,数据量不大,使用前边那个单线程的东西就OK了
 21 my $sth=$dbh->prepare("select name,object_id from sys.all_objects ao where type='U' and not exists(
 22 select 1 from  sys.all_columns col where col.object_id=ao.object_id and system_type_id=240)");
 23 $sth->execute();
 24 
 25 ##线程数。。。这个很纠结,小弟的服务器,在导的时候,5个线程以上,服务器会挂起~~~~
 26 ##于是想想,把线程改成进程,都不行~~哎!看来我这基础知识还差啊。
 27 my $threads_cnt=(not defined $ARGV[0])?5:$ARGV[0];   
 28 ##每次导的记录数
 29 my $per_records=(not defined $ARGV[1])?3000:$ARGV[1];
 30 
 31 my @data;
 32 while (@data=$sth->fetchrow_array())
 33 {
 34         ##测试时用
 35 #       @data=$sth->fetchrow_array();
 36         my ($select_columns,$insert_columns,$column_count,$sort_column,$column_types);
 37         #获取某个表的列,并构建 查询,插入,列总数,列类型
 38         ##输入参数如下:
 39         ###data[0]:表名,data[1]:对像ID
 40         ##返回参数描述如下:
 41         ###$select_columns:构建SELECT的时候,列字符串
 42         ###$insert_columns:构建insert的时候,列字符串。之所以要把这两分开,因为有些类型在select的时候,会用到列属性方法,例如geometry.STAsText()
 43         ###$column_count:列数,其实可以从@$column_types得到,但@$columns_types是后边加的,此参数也就没有去掉
 44         ###$sort_column:用来排序的字段,因为总结了一下,一般第一个字段都是标识字段,主键,因此,这里只取的第一个字段
 45         ###$columns_types:列的类型列表,一个数组。因为sql server里边的某些类型的值,在进mysql的时候,需要做处理,例如geometry
 46         #######另外再啰嗦一句,很少见有能同时返回多个值的东东(当然,可能是我把C#忘得差不多了)
 47         ($select_columns,$insert_columns,$column_count,$sort_column,$column_types)=get_columns($data[0],$data[1]);
 48         #查询结果。如果是导入失败,会返回False,否则为空
 49         ##传入参数?说明请参照上边的输出参数
 50         my $relt = export_data_in ($select_columns,$insert_columns,$column_count,$sort_column,$data[0],$column_types);
 51         
 52 }
 53 
 54 
 55 
 56 $dbh->disconnect;
 57 
 58 
 59 #插入数据
 60 sub export_data_in
 61 {
 62         #构建SQL
 63         my($select_columns,$insert_columns,$columns_count,$sort_column,$table_name,$column_types) = @_;
 64         my $rows_count=0;
 65         my $dbh2=DBI->connect("dbi:ODBC:$source_name",$source_user_name,$source_user_psd);
 66         my $sth_sc=$dbh2->prepare("select count(1) from $table_name");
 67         $sth_sc->execute();
 68         my @data_count=$sth_sc->fetchrow_array();
 69         my $begin_cnt = 0;
 70         ##这里不-1,会报21
 71         my $end_cnt = $per_records - 1;
 72         while($begin_cnt <= @data_count[0])
 73         {
 74                 my @threads;
 75                 ##循环开启导数据线程
 76                 for(my $count=1;$count<=$threads_cnt;$count++)
 77                 {
 78                         ##基本,下边的SQL语句成了本程序最大的性能瓶颈了。小弟的测试中,前1000W条数据还好,但,在1000W条之后,此SQL语句的查询性能急剧下降,当然,小弟是在远程测试上边导的,(当然,我的表没分区的,有MSSQL优化经验的知道,表是可以分区的)
 79                         ##与没有对MSSQL数据库进行性能优化也有很大关系。。
 80                         my $sql_select="select *
 81                         FROM
 82                         (
 83                                 SELECT $select_columns,ROW_NUMBER() OVER (ORDER BY $sort_column) AS RowNum
 84                                 FROM $table_name
 85                         ) as t
 86                         where t.RowNum  BETWEEN $begin_cnt and $end_cnt";
 87 
 88                         print "exporting data $table_name;total:@data_count[0];now:$begin_cnt \n";
 89                         ##开线程。参数请参照上边的描述
 90                         my $res0=threads->new(\&export_data,   $table_name,$sql_select,$insert_columns,$columns_count,$column_types);
 91                         push(@threads,$res0);
 92                         $begin_cnt = $begin_cnt + $per_records;
 93                         $end_cnt = $end_cnt + $per_records;
 94                 }
 95                 ##回收
 96                 foreach(@threads)
 97                 {
 98                         $_->join;
 99                 }
100         }
101 #       $dbh2->disconnect;
102 }
103 
104 sub export_data
105 {
106         my $startTime=time;
107         my ($table_name,$sql_select,$insert_columns,$columns_count,$column_types)=@_;
108         my $dbh_mssql=DBI->connect("dbi:ODBC:$source_name",$source_user_name,$source_user_psd);
109 
110         my $sth_select=$dbh_mssql->prepare($sql_select);
111         $sth_select->execute();
112         $sth_select->{LongTruncOk}=1;
113         ##生成标识ID
114         my $gid=rand(3200);
115         my $data_str="";
116 
117         my $select_data;
118         ##还是改成fetchrow_arrayref(),小弟测试了下,这个的速度,真不是之前fetchrow_array能比的
119         ##另外,我这种拼handlerSocket多条插入语句的方式,应该还是有点问题。我记得有一种更优化的方式是:insert,但由于找不到资料,只好作罢。
120         while($select_data=$sth_select->fetchrow_arrayref())
121         {
122                 if($data_str ne "")
123                 {
124                         $data_str="$data_str,";
125                 }
126 
127                 $data_str=$data_str."[$gid,'+',['".join("','",@{$select_data})."']]";
128 
129         }
130         printf("读出时间%.1f seconds.\n",time-$startTime);
131         $startTime=time;
132         ##测试的时候,查看数据的语句。
133 #       print "\n",$data_str,"\n";
134         if($data_str ne "")
135         {
136             $data_str="[$data_str]";
137             my $args = { host => $aim_ip, port => $hs_port };
138             my $hs = new Net::HandlerSocket($args);
139             my $res = $hs->open_index($gid, $aim_db_name, $table_name, 'PRIMARY', "$insert_columns");
140             die $hs->get_error() if $res != 0;
141             ##这里不加EVAL不行的,不信?您试
142             $res = $hs->execute_multi(eval($data_str));
143             die $hs->get_error() if $hs->get_error() != 0;
144             $hs->close();
145         }
146         undef $data_str;
147         printf("写入时间%.1f seconds.\n",time-$startTime);
148         
149         ##这里啰嗦一下,也给大家展示一下我的结果  ^-^
150             # exporting data t_p_areagroup_plate_userdiy_l;total:42758121;now:12825000
151             # exporting data t_p_areagroup_plate_userdiy_l;total:42758121;now:12830000
152             # exporting data t_p_areagroup_plate_userdiy_l;total:42758121;now:12835000
153             # exporting data t_p_areagroup_plate_userdiy_l;total:42758121;now:12840000
154             # exporting data t_p_areagroup_plate_userdiy_l;total:42758121;now:12845000
155             # 读出时间18.9 seconds.
156             # 写入时间1.3 seconds.
157             # 读出时间23.3 seconds.
158             # 写入时间1.4 seconds.
159             # 读出时间23.7 seconds.
160             # 写入时间1.1 seconds.
161             # 读出时间25.6 seconds.
162             # 写入时间0.6 seconds.
163             # 读出时间25.6 seconds.
164             # 写入时间0.9 seconds.
165         ##怎么样,写的速度够快吧,这就是TMD  HandlerSocket,而且,还不用去考滤锁。
166 }
167 
168 sub get_columns
169 {
170         print "loading columns of $_[0] \n";
171         my $sql="select col.name,tp.name from sys.all_columns col
172                                         inner join sys.types tp on col.system_type_id=tp.system_type_id  and col.user_type_id=tp.user_type_id
173                                         where object_id=$_[1]";
174         my $dbh2=DBI->connect("dbi:ODBC:$source_name",$source_user_name,$source_user_psd);
175         my $cols=$dbh2 -> prepare($sql);
176         $cols->execute();
177         my $cols_insert = "";
178         my $cols_select = "";
179         my $cols_count = 0;
180         my $sort_column="";
181         my @cols_types;
182         my @col;
183         while(@col= $cols->fetchrow_array())
184         {
185                 my ($col_name,$type_name)=@col;
186                 @cols_types[$cols_count]=$type_name;
187                 if($cols_count>0)
188                 {
189                         $cols_insert="$cols_insert,";
190                         $cols_select="$cols_select ,";
191                 }
192                 else
193                 {
194                         $sort_column="[$col_name]";
195                 }
196                 if($type_name eq "hierarchyid")
197                 {
198                         $cols_select = "$cols_select [$col_name].ToString() as $col_name";
199                         $cols_insert = "$cols_insert$col_name";
200                 }
201                 else
202                 {
203                         $cols_select="$cols_select [$col_name]";
204                         $cols_insert = "$cols_insert$col_name";
205                 }
206                 $cols_count++;
207         }
208         $dbh2->disconnect;
209         ($cols_select,$cols_insert,$cols_count,$sort_column,\@cols_types);
210 }
211 
212 

调用方法(将运行结果放到out.log):

1 nohup perl export_data_muti_thread_v0.5.pl 10 5000 > out.log &

 

 

另外再啰嗦一句。。。cnblogs的回复真不多,哪怕是拍砖也好呀。别这么死气沉沉的。

查看更多关于【perl】的文章

展开全文
相关推荐
反对 0
举报 0
评论 0
图文资讯
热门推荐
优选好物
更多热点专题
更多推荐文章
Linux下安装Perl和Perl的DBI模块
今天在虚拟机测试shell脚本的时候,有些命令使用不了。比如说 mysqlhotcopy ,它提示Perl的版本太低。我用的 RedHat9 的Perl才5.8.0版本。。。(2002年以前的)严重过时。所以重新安装了新版本的 Perl,过程记录如下: 1、在官方网站下载新版本的源码包:http:

0评论2023-03-16464

Perl 与Form
说明事项: 這個範例用來說明如何經由網頁上的HTML form 表單元件來呼叫伺服器端的perl 程式。这个范例用来说明如何经由网页上的HTML form 表单元件来呼叫伺服器端的perl 程式。首先在網頁上設計表單元件,這個範例是設計一個按鈕,其原始碼如下:首先在网页

0评论2023-02-10545

Perl学习 perl培训
http://www.sun126.com/perl5/perl5-1.htm翻译: flamephoenix 第一章 概述一、Perl是什么?二、Perl在哪里?三、运行四、注释一、Perl是什么?  Perl是Practical Extraction and Report Language的缩写,它是由Larry Wall设计的,并由他不断更新和维护,用

0评论2023-02-10506

- calm_水手">Perl中的箭头符-> - calm_水手
Perl中的箭头符-2012-05-21 17:14 calm_水手 阅读(623) 评论(0) 编辑 收藏 举报  有两种用法,都和解引用有关。第一种用法,就是解引用。根据 - 后面跟的符号的不同,解不同类型的引用,-[] 表示解数组引用,-{} 表示解散列引用,-() 表示解子程序引

0评论2023-02-09731

perl脚本语言学习 perl脚本调用perl脚本
来公司的第二个星期便看了一下perl语言,发现掌握一门脚本语言还是非常有用的。到现在为止已经入职两个月,用perl脚本做了这些活:1. 修改了公司的一个爬取网页源代码的脚本2. 改进了一个出特征库的脚本,根据svn status的状态,来优化,将只需要添加的DB的数

0评论2023-02-09317

Perl模块的安装方法 perl 安装模块
1. 下载离线安装包 *.tar.gz的形式解包后,#perl Makefile.PL#make#make install2. 在联网的情况下,通过CPAN安装# perl -MCPAN -e shellcpan install PAR::Packer 

0评论2023-02-09909

Perl像C一样强大,像awk、sed等脚本描述语言一样方便。
Perl是由Larry Wall设计的,并由他不断更新和维护的编程语言。Perl具有高级语言(如C)的强大能力和灵活性。事实上,你将看到,它的许多特性是从C语言中借用来的。Perl与 脚本语言一样,Perl不需要编译器和链接器来运行代码,你要做的只是写出程序并告诉Perl

0评论2023-02-09370

27-Perl 进程管理
1.Perl 进程管理Perl 中你可以以不同的方法来创建进程。本教程将讨论一些进程的管理方法。你可以使用特殊变量 $$ 或 $PROCESS_ID 来获取进程 ID。%ENV 哈希存放了父进程,也就是shell中的环境变量,在Perl中可以修改这些变量。exit() 通常用于退出子进程,主

0评论2023-02-09436

在perl中简单的正则匹配 正则匹配或的使用
(一)、在perl中关于元字符的匹配元字符代表含义点号( .)匹配处换行符以外的任何单字符星号(*)匹配前面的内容零次或多次反斜线屏蔽元字符的特殊含义。\\代表\,\.匹配点号.*匹配所有的字符串加号(+)匹配前一个条目一次以上问号(?)表示前面一个条目可

0评论2023-02-09908

Perl WEB 开发之 Template
由于工作需要, 最近开始使用Perl来作为服务器脚本来处理Web 请求。系统采用的Template 来做Web page 的模板,用来简化繁琐但并不困难的HTML标签的编写。Question 1: Template Toolkit 是啥?Template Toolkit是一组Perl Module的集合, 它实现了一种快速的

0评论2023-02-09418

Perl到底是什么意思? perpol意思
学习perl也有一段时间了,如果连perl是什么意思都不知道,那就太汗颜了,听好啦!perl == Practical Exstraction and Report Language,中文叫做实用抽取和报表语言。

0评论2023-02-09437

perl-cgi-form2
代码:         #!/usr/local/bin/perl        use CGI ':standard';        print header;        print start_html("Example CGI.pm Form");        print "h1 Example CGI.pm Form/h1\n";      

0评论2023-02-09501

Perl实战(一) perl进阶
在Perl中,我们可以通过uc,lc,\U,\L来修改变量的值。其中uc,\U可以将变量中的字母全部转换为大写。              lc,\L可以将变量中的字母全部转换为小写。              $big = "\U$var";       $big = uc($var);  

0评论2023-02-09685

Perl多线程(2):数据共享和线程安全 多线程epoll
线程数据共享在介绍Perl解释器线程的时候一直强调,Perl解释器线程在被创建出来的时候,将从父线程中拷贝数据到子线程中,使得数据是线程私有的,并且数据是线程隔离的。如果真的想要在线程间共享数据,需要显式使用threads::shared模块来扩展threads模块的功

0评论2023-02-09683

Linux下安装与使用本地的perl模块 centos安装perl
在使用Linux或是unix时,perl是一个非常有用的脚本的语言。关于perl的模块安装,网上也有很多介绍,一方面可以通过不同套件自带的软件安装工具安装,一方面可以通过cpan安装,再者就是可以直接编译源代码。 这样,对于拥有root权限的用户来说,没有任何问题

0评论2023-02-09497

Perl_Tkx_Canvas绘图功能函数介绍
1.画画布:     $canvas = $mw-new_tk__canvas;2.画线:         $canvas-create_line(10,10,200,50,-fill=”red”,-width=3);配置item参数:       $canvas-itemconfigure($id, -fill = "blue", -width = 2);3.画椭圆         “ova

0评论2023-02-09658

perl: warning: Falling back to the standard locale ("C").
/********************************************************************************** *perl: warning: Falling back to the standard locale ("C"). * 说明: * 使用debootstrap的时候,遇到这个问题,记录解决方法。 **2017-2-18 深圳 南山平山村 曾剑锋

0评论2023-02-09574

Perl操作Mysql数据库 perl操作excel
一. 安装DBI模块步骤1:从TOOLS栏目中下载DBI.zip,下载完后用winzip解开到一个temp目录,共有三个文件:ReadmeDBI.ppdDBI.tar.gz步骤2: 在DOS窗口下,temp目录中运行下面的DOS命令:ppm install DBI.ppd 如果提示无效命令,可在perl/bin目录下运行 二. 安装DBD

0评论2023-02-09348

更多推荐