Perl Module Instantiation + DBI + Forks Mysql сървърът е изчезнал

Написах програма на perl, която анализира записи от csv в db.

Програмата работи добре, но отне много време. Затова реших да разклоня основния процес на анализиране.

След малко спорове с вилицата сега работи добре и работи около 4 пъти по-бързо. Основният метод за анализиране е доста интензивен в базата данни. За интереси, за всеки запис, който се анализира, има следните db извиквания:

1 - има проверка дали уникално генерираният base62 е уникален спрямо таблица с карти на baseid 2 - Има проверка на архива, за да се види дали записът е променен 3 - Записът е вмъкнат в db

Проблемът е, че започнах да получавам грешки „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 с това: пакет 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;
}

След това всички модули, включително разклонените модули за анализатор, отварят връзка към DB, използвайки:

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 и всеки от тях инстанцира връзка с DB, както е показано по-горе (т.е. в конструктора), тогава те използват ли различни връзки към базата данни или едно и също Връзка?

Това, което се чудех, е дали скриптът отнема да речем 6 часа, за да завърши, дали повикването от най-високо ниво към db връзката изтича времето за изчакване на db връзката от по-ниско ниво, въпреки че модулът от по-ниско ниво прави своя „собствена“ връзка.

Много е разочароващо да се опитвам да намеря проблема, тъй като мога да възпроизведа грешката само след като стартирам много дълъг процес на анализ.

Извинете за дългия въпрос, благодаря предварително на всеки, който може да ми даде някакви идеи.


АКТУАЛИЗАЦИЯ 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();
}

Така че, както разбирам, DB връзката се извиква след разклонението.

Но, както споменах по-горе, раздвоеният код, очертан точно по-горе, работи добре. Това е следващият модул, който не работи, който се изпълнява от модул на контролер, който просто преминава през всеки работен модул един по един (парсерът е един от тях) - модулът на контролера не създава DB връзка в своята конструкция или навсякъде друго.


Актуализация 2

Забравих да спомена, че не получавам никакви грешки в модула „проблем“ след анализатора, ако анализирам само малък брой файлове, а не цялата опашка.

Така че е почти като че ли интензивното разклонено анализиране и достъп до DB я прави недостъпна за нормални неразклонени процеси веднага след като приключи за известно неопределено време.

Единственото нещо, което забелязах, когато изпълнението на анализатора завърши в Mysql състояние, е, че Threads_connected стои около, да речем, 500 и не намалява известно време.


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


Отговори (2)


Зависи от това как е структурирана вашата програма, което не става ясно от въпроса.

Ако създадете DB връзката преди fork, Perl ще направи копие на обекта за DB връзка за всеки процес. Това вероятно би причинило проблеми, ако два процеса се опитат да осъществят достъп до базата данни едновременно с една и съща DB връзка.

От друга страна, ако създадете DB връзките след forking, всеки модул ще има своя собствена връзка. Това трябва да работи, но може да имате проблем с изчакване, ако модул x създаде връзка, след това изчака дълго време за процес в модул y да завърши, след което се опита да използва връзката.

Накратко, ето какво искате:

  • Нямате отворени връзки в момента, в който fork. Дъщерните процеси трябва да създават свои собствени връзки.
  • Отваряйте връзка само непосредствено преди да искате да я използвате. Ако във вашата програма има точка, когато трябва да изчакате, отворете връзката, след като изчакването приключи.
person Community    schedule 12.04.2013
comment
Благодаря - актуализирах въпроса по-горе в Актуализация 1. Така че мисля, че се свързвам в детето. И да, всички мои DB връзки се отварят точно преди да ги използвам, когато се случва изчакване. - 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 се извикваше правилно преди, тъй като се извикваше при създаване на db връзка в разклонението... Объркан.. - person someuser; 12.04.2013