我在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。
答案 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;
}