2011-12-22 18 views
6

mam coś, co wygląda tak:Przyspieszenie Perl DBI fetchrow_hashref

my $report = new ReportGenerator; #custom object 
my $dbh = $dbc->prepare('SELECT * FROM some_table WHERE some_condition'); #DBI handle 
$dbh->execute(); 
while(my $href = $dbh->fetchrow_hashref){ 
    $report->process_record($href); 
} 
$dbh->finish(); 
print $report->printReport(); 

Moim problemem jest to, że każda iteracja pętli jest bardzo powolna. Problemem jest MySQL. Zastanawiałem się, czy możliwe było umieszczenie jakiegoś opakowania w pętli while, aby pobrać więcej niż jeden rekord na raz, jednocześnie pobieranie wszystkich zapisów do pamięci również nie jest praktyczne. Nie martwię się o efektywność kodu (hashref vs arrayref, etc ..). Raczej jestem zainteresowany przy pobieraniu pozwala powiedzieć 10000 rekordów na raz.

Baza danych zawiera ~ 5 milionów rekordów. Nie mogę zmienić/zaktualizować serwera.

Dzięki

+0

Ten kod powinien działać wystarczająco szybko. Czy jesteś pewien, że wybór nie trwa długo? Możesz chcieć czas, jak długo trwa wykonanie. Oczywiście proces może być powolny. Możesz spróbować taktowania tylko pobrania bez procesu. –

Odpowiedz

8

Można użyć fetchall_arrayref funkcję, która przyjmuje argument „maxrows”:

while (my $data = $dbc->fetchall_arrayref(undef, 10000)) { 
    for my $row(@{$data}) { 
    $report->process_record($row); 
    } 
} 

Można też zajrzeć na własność RowCacheSize który próbuje kontrolować ile rekordów są zwracane w sprowadzić ze swojego kierowcy.

+1

fetchall_arrayref nie jest zalecany, gdy przetwarza się pojedyncze rekordy i odrzuca je. Dzieje się tak dlatego, że musi on dużo przydzielać pamięci, aby przechowywać wszystkie pola we wszystkich wierszach, a alokacja pamięci jest droga. Patrz strona 22 http://www.slideshare.net/Tim.Bunce/dbi-advanced-tutorial-2007 –

4

Który bit jest wolny? Czy to jest połączenie z execute, fetchrow_hashref lub process_record? Wydaje mi się mało prawdopodobne, że problemem jest fetchrow_hashref. O wiele bardziej prawdopodobne jest wykonanie zapytania lub czarnej skrzynki z process_record.

Ale to wszystko zgaduje. Naprawdę nie można tu naprawdę pomóc. Polecam uzyskać pewne prawdziwe dane na temat wydajności kodu przy użyciu Devel::NYTProf.

+0

Już to robiłem i stwierdziłem, że problem nie ma związku z tym pytaniem, które mówi, że obie metody są dość bliskie wzajemnie. 221 s vs 239 s. A więc wciąż jest niewielka poprawa. chociaż znalazłem interesujące wąskie gardło w wyszukiwaniu hash. Mam funkcję, która sprawdza, czy hash istnieje, jeśli robi to dostaje wartość, a jeśli nie, pobiera ją z mysql. ze średnią średnio 4 μs/połączenie. Problem polega na tym, że funkcja nazywa się 15 milionów razy. czyli prawie 1 minuta. ale to nie jest coś, co można łatwo naprawić. – Smartelf

3

Najszybszy sposób, aby pobrać wiersze jak mieszań wykorzystaniem DBI jest użycie bind_columns() takiego:

$sth->execute; 
    my %row; 
    $sth->bind_columns(\(@row{ @{$sth->{NAME_lc} } })); 
    while ($sth->fetch) { 
     print "$row{region}: $row{sales}\n"; 
    } 

To tylko odpowiednie, jeśli jesteś szczęśliwy dla każdego wiersza, aby używać tego samego skrótu.

Poza tym, zgadzam się z davorg, unikaj zgadywania: najpierw zmierz.

Aby uzyskać więcej informacji na temat korzystania z DBI, w tym wydajność, zobacz mój tutorial slides (od 2007, ale nadal istotne).

Powiązane problemy