LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1137|回复: 8

perl 语言编程实例-多进程篇

[复制链接]
发表于 2004-7-6 11:21:16 | 显示全部楼层 |阅读模式

  1. perl 语言编程实例-多进程篇

  2. perl 语言是一种非常强大的脚本语言,其应用遍及系统维护,CGI,数据库编程。
  3. 以下是我遇到的一个具体问题,应用perl获得圆满解决。

  4. 问题提出:
  5.     某数据库应用。需要检索一批数据(A表,数据量12万左右)。对该批数据
  6. 将进行逐一核对,期间将关联三个千万级的表(C,D,E表,分别有近亿条数据),
  7. 并将检索状态插入一张新表(F)。

  8. 传统解决方案:
  9.     编写存储过程。打开一个cursor,对A表遍历,逐一检索C,D,E表。
  10. 判断状态写入新表。编程过程十分简单,顺利完成。但执行时效率低下,耗时在
  11. 8小时左右,不能满足要求。

  12. 分析:
  13.     C,D,E表建有极其完备的索引。对单条数据检索极其快速。同时执行时主机CPU


  14. 内存等资源十分空闲。查询单条记录耗时:8×3600/12万=0.24秒,也是在合理的

  15. 范围。
  16. 同时主机数据库在业务高峰期时可以支持500-600用户同时登陆(telnet方式)。

  17. 以上
  18. 说明性能瓶颈不在主机,数据库上。

  19. 结论:以上所有都合情合理,采用单进程方式无法进一步提高性能。为提高速度,

  20. 只能
  21. 采用多进程。


  22. 快速构造原型:

  23. 原型一:
  24. #!/usr/bin/perl

  25. my $maxchild=20;
  26. foreach $item (1..500) {
  27.         while ( `ps -ef|grep $0|wc -l` > $maxchild) { select undef,undef,undef,0.1; };
  28.         if ($PID=fork()){
  29.                 print "Starting Sub_Process:$PID\n";
  30.         } else {
  31.                 print "I will handle data:$item\n";
  32.                 sleep 1;
  33.                 exit 1;
  34.         };
  35. }

  36. 执行以上,正常,子进程控制在20。

  37. 以上述脚本为基础,添加数据库部分:

  38. #!/usr/bin/perl

  39. use DBI;


  40. my $dbh=DBI->connect(...);
  41. my $sth=$dbh->prepare(qq/select * from A/);
  42. $sth->execute();
  43. $sth->bind_column(undef,.....);

  44. while ($sth->fetch()) {
  45.         while ( `ps -ef|grep $0|wc -l` > $maxchild) { select undef,undef,undef,0.1; };
  46.         if ($PID=fork()) {
  47.                 print "Starting Sub_Process:$PID\n";
  48.         } else {
  49.                 query(B,C,D);                #执行数据库操作
  50.                 insert(E);
  51.                 exit 1;
  52.         }
  53. }
  54. $sth->finish();
  55. $dbh->disconnect();

  56. 确保无语法错误,执行。处理一两条数据后脚本报错,中断。具体错误略。

  57. 分析:程序框架没错,但是在fork子进程时,$dbh同时被子进程继承,导致该数据

  58. 库连接反复使用。
  59. 由于数据库底层的某种原因,对该种操作是不允许的。结论:以上简单的多进程方

  60. 式不可行。数据库
  61. 连接部分必须同 fork 分离。

  62. ######################################

  63. 考虑很久,设计如下原型:将打开A表的cursor单独提出,结果传给另外一个进程


  64. 12万数据较大,作为参数传递似乎不妥,考虑利用管道通信。



  65. 原型二:
  66. ############################

  67. 分成  getdata,setdata两个程序。首先建立管道 : mknod data.pipe p

  68. cat getdata:

  69. #!/usr/bin/perl

  70. use DBI;
  71. open(DATAPIPE,">./data.pipe") or die "$!\n";

  72. my $dbh=DBI->connect(...);
  73. my $sth=$dbh->prepare(qq/select * from A/);
  74. $sth->execute();
  75. $sth->bind_column(undef,.....);

  76. while ($sth->fetch()) {
  77.         print DATAPIPE data.....;
  78. }
  79. close(DATAPIPE);

  80. ######################

  81. cat setdata:

  82. #!/usr/bin/perl
  83. use DBI;
  84. open(DATAPIPE,"<./data.pipe") or die "$!\n";

  85. my $pipecount=0;
  86. my $maxlines=2000;
  87. my @lines=();

  88. while($record=<DATAPIPE>) {
  89.         $pipecount++;
  90.         push @lines,$record;

  91.         unless ($pipecount % $maxlines) {
  92.                 if ($PID=fork()){
  93.                         print "Starting Sub_Process:$PID\n";
  94.                         @lines=();
  95.                 }else{
  96.                         my $dbh=DBI->connect(...);
  97.                         foreach (@lines) {
  98.                                 handle_data($_);
  99.                         }
  100.                         $dbh->disconnect();
  101.                         exit 1;
  102.                 }
  103.         }
  104. }

  105. my $dbh=DBI->connect(...);
  106. foreach (@lines) {
  107.         handle_data($_);
  108. }

  109. $dbh->disconnect();

  110. 以上脚本运行正常,执行时启动:12万/$maxlines= 60个子进程。
  111. 处理完所有数据耗时在 10分钟左右,效率提高几十倍。

复制代码
发表于 2004-7-6 12:13:10 | 显示全部楼层
发表于 2004-7-7 13:14:27 | 显示全部楼层
这么大规模的应用也是perl的范围么
 楼主| 发表于 2004-7-7 13:50:05 | 显示全部楼层
该脚本只是定期执行,使用并不频繁。

perl语言使用越久,越感到功能强大。
感觉没有它完成不了的任务。
发表于 2004-7-7 22:01:59 | 显示全部楼层
没看你关联其他表和插入新表呢?
 楼主| 发表于 2004-7-8 08:42:03 | 显示全部楼层
数据库操作并不是关键,就是一般的sql语句。
我这里只是想探讨一下多进程编程的模式框架。
发表于 2004-7-15 09:39:41 | 显示全部楼层
程序思路相当不错, perlchina 收入啦
 楼主| 发表于 2004-7-15 16:06:27 | 显示全部楼层
将原有程序修改一下,两个合成一个,也不用另外建管道了。
程序没测过,应该没问题




  1. #!/usr/bin/perl

  2. use DBI;

  3. pipe(PIPE_READ,PIPE_WRITE) or die "Can't make pipe!\n";

  4. unless ($pid = fork) {
  5.    defined $pid or die "can't fork:$!\n";
  6.    close(PIPE_READ);

  7.    my $dbh=DBI->connect(...);
  8.    my $sth=$dbh->prepare(qq/select * from A/);
  9.    $sth->execute(); $sth->bind_column(undef,.....);
  10.    while ($sth->fetch()) { print PIPE_WRITE data.....;}
  11.    $dbh->disconnect();
  12.    exit;
  13. }

  14. $SIG{CHLD} = sub { waitpid($pid,0) };

  15. close(PIPE_WRITE);

  16. my $pipecount=0;
  17. my $maxlines=2000;
  18. my @lines=();

  19. while($record=<PIPE_READ>) {
  20.    $pipecount++;
  21.    push @lines,$record;
  22.    unless ($pipecount % $maxlines) {
  23.         if ($PID=fork()){
  24.                print "Starting Sub_Process:$PID\n";
  25.                @lines=();
  26.         }else{
  27.                my $dbh=DBI->connect(...);
  28.                foreach (@lines) { handle_data($_);}
  29.                $dbh->disconnect();
  30.          }
  31.                exit 1;
  32.         }
  33.    }
  34. }

  35. my $dbh=DBI->connect(...);
  36. foreach (@lines) {handle_data($_);}
  37. $dbh->disconnect();


复制代码
 楼主| 发表于 2004-7-15 16:14:17 | 显示全部楼层
谢谢 tsingson 支持。
本来我要在 perlchina.org 发的,
不过以前发贴时感觉好像不正常。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表