-1

我在 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 线程,但它们似乎只是同时运行几个 perl 解释器,而且我的脚本已经使用了大量内存,合并数据似乎没有问题。此外,在这个阶段,脚本并没有使用很多 CPU。

我一直在考虑可能使用 Coro 线程,但似乎会有一个学习曲线,加上我当前代码的相当复杂的集成。我想知道走这条路是否有可能获得任何收益。有没有更好的方法来处理这样的多线程代码。我不能再使用我的代码已经使用的内存了。还有什么我可以在这里做的吗?

不幸的是,在 MySQL 中进行聚合不是一种选择,并且用不同的语言重写代码会太耗时。我知道使用数组而不是哈希可能会使我的代码更快/使用更少的内存,但这同样需要对大型脚本进行重大重写。

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

4

2 回答 2

1

您可以在 mysql 中简单地通过制作 black_box 和 secret_func 表(临时表,如果需要)预先填充相关列的每个现有值的结果。

除此之外,测量调用 black_box 和 secret_func 与执行/获取花费了多少时间。如果前者很多,您可以记住结果:

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;
于 2013-11-12T17:47:41.007 回答
1

If you have memory concerns, using forks instead of threads may help. They use much less memory than the standard perl threads. There is going to be somewhat of a memory penalty for multi-threading, and YMMV as far as performance goes, but you might want to try something like:

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;
}
于 2013-11-12T18:24:18.783 回答