Perl Module Instantiation + DBI + Forks Сервер Mysql ушел

Я написал программу на Perl, которая анализирует записи из csv в db.

Программа работала нормально, но долго. Поэтому я решил разветвить основной процесс разбора.

Немного повозившись с fork, теперь он работает хорошо и работает примерно в 4 раза быстрее. Основной метод синтаксического анализа довольно интенсивно использует базу данных. Ради интереса для каждой проанализированной записи есть следующие вызовы БД:

1 - есть проверка того, что уникально сгенерированный base62 уникален по сравнению с таблицей карты baseid 2 - есть проверка архива, чтобы увидеть, изменилась ли запись 3 - запись вставлена ​​в БД

Проблема в том, что я начал получать ошибки «Mysql ушел», когда синтаксический анализатор запускался в разветвленном режиме, поэтому после долгих возни я придумал следующую конфигурацию 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 ушел», когда следующий модуль запускается после основного синтаксического анализатора.

Странно то, что модуль, вызывающий проблемы, включает очень простой запрос SELECT к таблице, содержащей в настоящее время только 3 записи. Запускать напрямую как тест (не после парсера) работает нормально.

Я пытался добавить паузу в 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;
}

Вопрос в том, есть ли у меня Module1.pm, который вызывает Module2.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 и не уменьшается в течение некоторого времени.


person someuser    schedule 12.04.2013    source источник


Ответы (2)


Это зависит от того, как устроена ваша программа, что неясно из вопроса.

Если вы создадите соединение с БД до того, как fork, Perl создаст копию объекта соединения с БД для каждого процесса. Это, вероятно, вызовет проблемы, если два процесса попытаются получить доступ к базе данных одновременно с одним и тем же соединением с БД.

С другой стороны, если вы создаете соединения с БД после forking, у каждого модуля будет свое собственное соединение. Это должно работать, но у вас может возникнуть проблема с тайм-аутом, если модуль x создает соединение, затем долго ждет завершения процесса в модуле y, а затем пытается использовать соединение.

В общем, вот что вы хотите:

  • У вас нет открытых подключений в момент, когда вы fork. Дочерние процессы должны создавать свои собственные соединения.
  • Открывайте соединение только непосредственно перед тем, как вы хотите его использовать. Если в вашей программе есть точка, когда вам нужно ждать, откройте соединение после завершения ожидания.
person Community    schedule 12.04.2013
comment
Спасибо - я обновил вопрос выше в обновлении 1. Так что я думаю, что подключаюсь к ребенку. И да, все мои соединения с БД открываются прямо перед их использованием, когда происходит ожидание. - person someuser; 12.04.2013
comment
@l_t, я вижу, что вы создаете это соединение в дочернем элементе, но есть ли другие соединения, созданные до разветвления? Если это так, эти соединения могут быть проблемой. Даже если они не используются в форке, они все равно могут быть причиной проблемы. - person ; 12.04.2013
comment
Да, приведенный выше код, который получает данные из файла csv, находится в другом модуле, который выполняет итерацию по базе данных, чтобы получить имена файлов для анализа. Спасибо за предложение, я рассмотрю его. - person someuser; 16.04.2013

Прочитайте ответ dan1111, но я подозреваю, что вы подключаетесь, а затем разветвляетесь. Когда дочерний элемент завершает работу, дескриптор соединения DBI выходит за пределы области действия и закрывается. Как говорит dan1111, вы лучше подключаетесь к ребенку по всем причинам, которые он сказал. Прочтите об InactiveDestroy и AutoInactiveDestroy в DBI, что поможет вам понять, что происходит.

person bohica    schedule 12.04.2013
comment
Спасибо. Пожалуйста, смотрите обновление 1 выше. Я думаю, что я подключаюсь в ребенке. - person someuser; 12.04.2013
comment
Не знал об AutoInactiveDestroy - попробую. Опять же, я не совсем уверен, но я думаю, что InactiveDestroy вызывался правильно до того, как он вызывался при создании соединения с БД в форке... Запутался.. - person someuser; 12.04.2013