Альтернатива 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 чтобы узнать больше.