Perl多线程MySQL数据获取

时间:2013-11-12 17:28:32

标签: multithreading perl

我在mysql(数百万行)中有几个非常大的表,我需要加载到我的perl脚本中。

然后,我们对数据进行一些自定义处理,并将其聚合为哈希值。不幸的是,自定义处理无法在MySQL中实现。

这是一个快速伪代码。

my @data;
for my $table_num(@table_numbers){
    my $sth = $dbh->prepare(...);
    $sth->execute();
    $sth->bind_columns(\my($a,$b,$c,...));
    while(($sth->fetch()){
        $data[$table_num]{black_box($a)}{secret_func($b)}+=$c;
    }
}

my $x = $#data + 1;
for my $num (@table_numbers){
    for my $a (keys %{$data[$num]}){
        for my $b (keys %{$data[$num]{$a}){
            $data[$x]{$a}{$b} += $data[$num]{$a}{$b};
        }
     }
}

现在,第一个循环每次迭代可能需要几分钟才能运行,因此我正在考虑如何并行运行它们。我之前看过使用Perl Threads,但它们似乎只是一次运行几个perl解释器,而我的脚本已经在使用大量内存,并且合并数据似乎有问题。此外,在此阶段,脚本不使用大量CPU。

我一直在考虑使用Coro线程,但似乎会有学习曲线,加上我当前代码的相当复杂的集成。如果我有可能通过这条路线看到任何收益,我想知道什么。是否有更好的多线程代码方式。我不能再使用我的代码已经使用的内存了。我能在这里做些什么吗?

不幸的是,在MySQL中进行聚合不是一种选择,而用其他语言重写代码会非常耗时。我知道使用数组而不是散列可能会使我的代码更快/使用更少的内存,但同样需要重写大型脚本。

编辑:以上是伪代码,实际逻辑要复杂得多。 bucketing基于几个db表,还有更多输入,只需$ a和$ b。预计算它们是不实际的,因为有数万亿+可能的组合。主要目标是如何使perl脚本运行得更快,而不是如何修复SQL部分。这需要更改数据在实际服务器中的存储和索引方式。这会影响很多其他代码。还有其他人正在进行这些优化。我目前的目标是尝试更快地编写代码而不更改任何sql。

2 个答案:

答案 0 :(得分:1)

你可以在mysql中完成,只需将black_box和secret_func表(如有必要,临时表)预先填充相关列的每个现有值的结果。

除此之外,还要测量对black_box和secret_func与execute / fetch的调用所花费的时间。如果前者有很多,你可以记住结果:

my %black_box;
my %secret_func;
for my $table_num...
...
        $data[$table_num]{ $black_box{$a} //= black_box($a) }{ $secret_func{$b} //= secret_func($b) } += $c;

答案 1 :(得分:1)

如果您有内存问题,使用分叉代替线程可能会有所帮助。它们使用的内存比标准perl线程少得多。多线程会有一些内存损失,而YMMV就性能而言会有所下降,但你可能想尝试类似的东西:

use forks;
use Thread::Queue;

my $inQueue = Thread::Queue->new;
my $outQueue = Thread::Queue->new;

$inQueue->enqueue(@table_numbers);

# create the worker threads
my $numThreads = 4;
for(1 .. $numThreads) {
    threads->create(\&doMagic);
}

# wait for the threads to finish
$_->join for threads->list;

# collect the data
my @data;
while(my $result = $outQueue->dequeue_nb) {
    # merge $result into @data
}

sub doMagic {
    while(my $table_num = $inQueue->dequeue_nb) {
        my @data;
        # your first loop goes here
        $outQueue->enqueue(\@data);
    }
    return;
}