2011-12-08 22 views
6

Chciałbym poznać najbardziej efektywny pod względem pamięci sposób pobierania arbitralnie dużych pól danych z bazy danych Oracle z Perl DBI. Metoda, której używam, to ustawienie atrybutu "LongReadLen" na uchwycie bazy danych na coś wystarczająco dużego. Jednak moja aplikacja musi pobrać kilka tysięcy rekordów, więc robienie tego arbiternie jest wyjątkowo nieefektywne w pamięci.Perl DBI alternatywa dla LongReadLena

Sugeruje wykonanie zapytania z góry, aby znaleźć największą potencjalną wartość i ustawienie tej wartości.

$dbh->{LongReadLen} = $dbh->selectrow_array(qq{ 
    SELECT MAX(OCTET_LENGTH(long_column_name)) 
    FROM table WHERE ... 
}); 
$sth = $dbh->prepare(qq{ 
    SELECT long_column_name, ... FROM table WHERE ... 
}); 

Jest to jednak nadal nieskuteczne, ponieważ dane odległy nie jest reprezentatywny dla każdego rekordu. Największe wartości przekraczają MB, ale średni rekord jest mniejszy niż KB. Chcę być w stanie wyciągnąć wszystkie informacje (tzn. Bez obcięcia), jednocześnie marnując jak najmniej pamięci na nieużywane bufory.

Metodą, którą rozważałem jest wyciąganie danych w porcjach, powiedzmy 50 rekordów na raz i ustawienie LongReadLen na maksymalną długość rekordów tego kawałka. Kolejną pracą, która mogłaby, ale nie musi, opierać się na idei kawałka, byłoby rozwidlenie procesu potomnego, odzyskanie danych, a następnie zabicie dziecka (zabranie ze sobą zmarnowanej pamięci). Najcudowniejszą rzeczą byłaby możliwość wymuszenia buforów DBI, ale nie sądzę, że to możliwe.

Czy ktoś rozwiązał podobny problem z jakimkolwiek sukcesem? Dzięki za pomoc!

EDIT

Perl v5.8.8 DBI v1.52

celu wyjaśnienia: niewydolność pamięci pochodzące z użyciem 'LongReadLen' wraz z {ora_pers_lob => 1} w przygotowaniu. Przy użyciu tego kodu:

my $sql = "select myclob from my table where id = 68683"; 
my $dbh = DBI->connect("dbi:Oracle:$db", $user, $pass) or croak $DBI::errstr; 

print "before"; 
readline(*STDIN); 

$dbh->{'LongReadLen'} = 2 * 1024 * 1024; 
my $sth = $dbh->prepare($sql, {'ora_pers_lob' => 1}) or croak $dbh->errstr; 
$sth->execute() or croak('Cant execute_query '. $dbh->errstr . ' sql: ' . $sql); 
my $row = $sth->fetchrow_hashref; 

print "after"; 
readline(*STDIN); 

Wykorzystanie pamięci rezydentnej "przed" wynosi 18 MB, a użycie "po" wynosi 30 MB. Jest to niedopuszczalne w przypadku dużej liczby zapytań.

+0

czy myślisz, że robisz to w "równoległy" sposób?! – amrfaissal

+0

Co masz na myśli przez "wyjątkowo nieefektywną pamięć"? Uważam, że każdy podatek od pamięci LongReadLen to _pera instrukcja handle_, nie dla pobranego wiersza. (Tj. Pobieranie tysięcy rekordów za pomocą LOB nie jest + tysiąckrotną karą w pamięci). Rzeczywiście, pomiary "DBD :: Oracle' 1.36 z' Devel :: Size' sugerują, że LOB "bufor pobierania" jest tylko tak potrzebny dla największego LOB pobrano do tej pory. Tak więc, jeśli trzymanie pojedynczego dużego bufora jest uciążliwe, być może po prostu WYBIERZ LENGTHB (long_col) i pozwól DBI rozwijać go w razie potrzeby. – pilcrow

+0

Zaktualizowałem wpis, podając więcej szczegółów.Nie wspominałem o używaniu ora_pers_lob; masz rację, że LongReadLen samo w sobie nie jest głodne pamięci. Używanie LongReadLen bez {ora_pers_lob => 1} w przygotowaniu skutkowało ORA-24812, który właśnie rozwiązałem. Dlatego wierzę, że moje pytanie zostało odebrane - mogę teraz używać LongReadLena z resztą świata. –

Odpowiedz

5

Czy Twoje kolumny mają duże LOBy danych (CLOB lub BLOBy)? Jeśli tak, nie musisz w ogóle używać LongReadLen; DBD :: Oracle udostępnia interfejs przesyłania strumieniowego LOB.

To, co chcesz zrobić, to bind the param jako typ ORA_CLOB lub ORA_BLOB, dzięki czemu otrzymasz "LOB locator" zwrócony z zapytania, zamiast tex. Następnie używasz ora_lob_read wraz z lokalizatorem LOB do pobierania danych. Oto przykład kodu, który zadziałał:

sub read_lob { 
    my ($dbh, $clob) = @_; 

    my $BLOCK_SIZE = 16384; 

    my $out; 
    my $offset = 1; 

    while (my $data = $dbh->ora_lob_read($clob, $offset, $BLOCK_SIZE)) { 
    $out .= $data; 
    $offset += $BLOCK_SIZE; 
    } 
    return $out; 
} 
+1

+1 dla specjalnego interfejsu LOB. Ale czy "nie zwróci $", co może być bardzo dużym skalarem? – pilcrow

+0

Tak, ale przynajmniej jest używany tylko w razie potrzeby. I oczywiście, jeśli możliwe jest przetwarzanie danych w postaci strumieniowej, możesz to zrobić zamiast '$ out. = $ Data'. – hobbs

+0

Dobrze. Jeśli użycie pamięci jest problemem, dobrym pomysłem byłoby przesyłanie strumieniowe na dysk lub inne przetwarzanie. (Możesz również zwrócić '\ $ out', aby uniknąć kopiowania.) – pilcrow

0

myślę o tym w ten sposób:

use Parallel::ForkManager 
use strict; 

# Max 50 processes for parallel data retrieving 
my $pm = new Parallel::ForkManager(50); 

# while loop goes here 
while (my @row = $sth->fetchrow_array) { 

# do the fork 
$pm->start and next; 

# 
# Data retreiving goes here 
# 

# do the exit in the child process 
$pm->finish; 
} 
$pm->wait_all_children; 

check Parallel::ForkManager w CPAN wiedzieć więcej.

+0

Dzięki za cynk. Jeśli nie mogę rozwiązać mojego problemu z ORA-24812, prawdopodobnie wybiorę tę trasę. –