Альтернатива Perl DBI для LongReadLen
Я хотел бы знать наиболее эффективный для памяти способ извлечения произвольно больших полей данных из Oracle db с Perl DBI. Метод, который я знаю, чтобы использовать, чтобы установить атрибут' LongReadLen ' в дескрипторе базы данных на что-то достаточно большое. Тем не менее, мое приложение должно вытащить несколько тысяч записей, поэтому делать это арбитражно крайне неэффективно.
на doc предлагает выполнить запрос заранее, чтобы найти наибольшее потенциальное значение и установить что.
$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 ...
});
однако это все еще неэффективно, так как внешние данные не являются репрезентативными для каждой записи. Самые большие значения превышают МБ, но средняя запись меньше КБ. Я хочу иметь возможность вытащить все informatoin (т. е. без усечения), тратя как можно меньше памяти на неиспользуемые буферы.
метод, который я рассмотрел, - это вытащить данные в куски, скажем, 50 записей за раз и установить LongReadLen против максимальной длины записи этого куска. Другая работа, которая могла бы, но не должна, основываться на идее куска, состояла бы в том, чтобы развить дочерний процесс, получить данные, а затем убить ребенка (взяв с собой потерянную память). Самое замечательное - это возможность освободить буферы DBI, но я не думаю, что это возможно.
кто-нибудь обращался к подобной проблеме с каким-либо успехом? Спасибо за помощь!
редактировать
на Perl У5.8.8, DBI v1.52
чтобы уточнить: неэффективность памяти происходит от использования 'LongReadLen' вместе с {ora_pers_lob => 1} в подготовке. Используя этот код:
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 );
использование резидентной памяти " до "составляет 18 Мб, а использование "после" - 30 МБ. Это неприемлемо для большого числа запросов.
2 ответов
являются ли ваши столбцы с большими полями данных (CLOBs или BLOBs)? Если это так, вам не нужно использовать LongReadLen вообще; DBD::Oracle предоставляет интерфейс потоковой передачи LOB.
что вы хотите сделать, это связать param тип ORA_CLOB
или ORA_BLOB
, который получит вам" локатор LOB", возвращенный из запроса, вместо tex. Тогда вы используете ora_lob_read вместе с локатором LOB для получения данных. Вот пример кода, который работал для меня:
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;
}
Я думаю об этом так:
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;
Регистрация Parallel:: ForkManager на CPAN чтобы узнать больше.