4

我编写了一个 perl 程序,将 csv 中的记录解析为 db。

该程序运行良好,但花了很长时间。所以我决定分叉主要的解析过程。

经过一番与 fork 的争论后,它现在运行良好并且运行速度提高了大约 4 倍。主要的解析方法是数据库密集型的。出于利益考虑,对于每条解析的记录,都有以下数据库调用:

1 - 检查唯一生成的 base62 对于 baseid 映射表是唯一的 2 - 有存档检查以查看记录是否已更改 3 - 记录插入到数据库中

问题是当解析器在分叉模式下运行时,我开始出现“Mysql has gone away”错误,所以经过多次摆弄后,我想出了以下 mysql 配置:

#
# * Fine Tuning
#
key_buffer              = 10000M
max_allowed_packet      = 10000M
thread_stack            = 192K
thread_cache_size       = 8
myisam-recover         = BACKUP
max_connections        = 10000
table_cache            = 64
thread_concurrency     = 32
wait_timeout           = 15
tmp_table_size  = 1024M

query_cache_limit       = 2M
#query_cache_size        = 100M
query_cache_size = 0
query_cache_type = 0

这似乎在解析器运行时解决了问题但是,当下一个模块在主解析器之后运行时,我现在得到一个“Mysql 服务器已经消失”。

奇怪的是,导致问题的模块涉及对当前只有 3 条记录的表进行非常简单的 SELECT 查询。直接作为测试运行(而不是在解析器之后)它工作正常。

我尝试在解析器模块运行后添加 4 分钟的暂停 - 但我得到了同样的错误。

我有一个主要的 DBConnection.pm 模型: package DBConnection;

use DBI;
use PXConfig;

sub new {
    my $class    = shift;
    ## MYSQL Connection
    my $config = new PXConfig();
    my $host     = $config->val('database', 'host');
    my $database = $config->val('database', 'db');
    my $user     = $config->val('database', 'user');
    my $pw       = $config->val('database', 'password');
    my $dsn      = "DBI:mysql:database=$database;host=$host;"; 
    my $connect2 = DBI->connect( $dsn, $user, $pw,  );
    $connect2->{mysql_auto_reconnect} = 1;
    $connect2->{RaiseError} = 1;
    $connect2->{PrintError} = 1;
    $connect2->{ShowErrorStatement} = 1;
    $connect2->{InactiveDestroy} = 1;

    my $self     = {
        connect => $connect2,
    };
    bless $self, $class;
    return $self;
}

然后所有模块,包括分叉的解析器模块,使用以下命令打开与数据库的连接:

package Example;

use DBConnection;

sub new {
    my $class    = shift;
    my $db       = new DBConnection;
    my $connect2 = $db->connect();
    my $self     = {
        connect2 => $connect2,
    };
    bless $self, $class;
    return $self;
}

问题是如果我有调用 Module2.pm 的 Module1.pm 调用 Module3.pm 并且它们每个都实例化与数据库的连接,如上所示(即在构造函数中),那么它们是使用不同的数据库连接还是相同的联系?

我想知道的是脚本是否需要 6 个小时才能完成,如果对数据库连接的顶层调用正在使较低级别的数据库连接超时,即使较低级别的模块正在建立其“自己的”连接。

试图找到问题非常令人沮丧,因为我只能在运行很长的解析过程后重现错误。

很抱歉这个问题很长,提前感谢任何能给我任何想法的人。


更新 1:

这是实际的分叉部分:

my $fh = Tie::Handle::CSV->new( "$file", header => 1 );
while ( my $part = <$fh> ) {
    if ( $children == $max_threads ) {
        $pid = wait();
        $children--;
    }
    if ( defined( $pid = fork ) ) {
        if ($pid) {
            $children++;
        } else {
            $cfptu = new ThreadedUnit();
            $cfptu->parseThreadedUnit($part, $group_id, $feed_id);
        }
    }
}

然后是 ThreadedUnit:

package ThreadedUnit;

use CollisionChecker;
use ArchiveController;
use Filters;
use Try::Tiny;
use MysqlLogger;

sub new {
    my $class    = shift;
    my $db       = new DBConnection;
    my $connect2 = $db->connect();
    my $self     = {
        connect2 => $connect2,
    };
    bless $self, $class;
    return $self;
}

sub parseThreadedUnit {
    my ( $self, $part, $group_id, $feed_id ) = @_;
    my $connect2 = $self->{connect2};

    ## Parsing stuff

    ## DB Update in try -> catch
    exit();
}

因此,据我了解,分叉后正在调用数据库连接。

但是,正如我上面提到的,上面概述的分叉代码可以正常工作。下一个不起作用的模块是从控制器模块运行的,该控制器模块一次只通过每个工作模块运行(解析器就是其中之一) - 控制器模块不会在其构造或任何地方创建数据库连接别的。


更新 2

我忘了提到,如果我只解析少量文件而不是完整队列,解析器之后的“问题”模块中不会出现任何错误。

因此,就好像密集的分叉解析和访问数据库使其在正常的非分叉进程结束一段时间后无法使用。

当解析器运行在 Mysql 状态下完成时,我唯一注意到的是 Threads_connected 位于 500 左右,并且在一段时间内没有减少。

4

2 回答 2

2

这取决于您的程序的结构,这从问题中不清楚。

如果你在你之前创建了数据库连接fork,Perl 将为每个进程制作一个数据库连接对象的副本。如果两个进程尝试使用相同的数据库连接同时访问数据库,这可能会导致问题。

另一方面,如果您在forking 之后创建 DB 连接,则每个模块都会有自己的连接。这应该可以,但是如果模块 x 创建一个连接,然后等待很长时间让模块 y 中的进程完成,然后尝试使用该连接,您可能会遇到超时问题。

总之,这就是你想要的:

  • 在您所在的位置没有任何打开的连接fork。子进程应该创建自己的连接。
  • 仅在您想使用它之前打开一个连接。如果您的程序中有一个点需要等待,请在等待完成后打开连接。
于 2013-04-12T08:27:15.030 回答
1

阅读 dan1111 的答案,但我怀疑您正在连接然后分叉。当子进程完成时,DBI 连接句柄超出范围并关闭。正如 dan1111 所说,出于他所说的所有原因,您可以更好地与孩子建立联系。阅读有关DBI 中的InactiveDestroyAutoInactiveDestroy的信息,这将帮助您了解正在发生的事情。

于 2013-04-12T08:39:36.003 回答