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ń.
czy myślisz, że robisz to w "równoległy" sposób?! – amrfaissal
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
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. –