Perl DBI альтернатива LongReadLen - PullRequest
6 голосов
/ 08 декабря 2011

Я хотел бы знать, как наиболее эффективно использовать память для извлечения произвольно больших полей данных из базы данных Oracle с помощью 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 ...
});

Однако это по-прежнему неэффективно, поскольку внешние данные не являются репрезентативными для каждой записи. Наибольшие значения превышают МБ, но средняя запись меньше, чем КБ. Я хочу иметь возможность извлекать всю информацию (т. Е. Без усечения), тратя при этом как можно меньше памяти на неиспользуемые буферы.

Метод, который я рассмотрел, состоит в том, чтобы извлекать данные порциями, скажем, по 50 записей за раз, и устанавливать LongReadLen относительно максимальной длины записей этого порции. Другой обходной путь, который может, но не обязан, основываться на идее чанка, заключался бы в том, чтобы разветвить дочерний процесс, извлечь данные, а затем убить дочерний процесс (забрав с собой потерянную память). Самой замечательной вещью была бы возможность принудительного освобождения буферов DBI, но я не думаю, что это возможно.

Кто-нибудь решал подобную проблему с каким-либо успехом? Спасибо за помощь!

EDIT

Perl v5.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 ]

5 голосов
/ 08 декабря 2011

Являются ли ваши столбцы большими объектами данных (CLOB или BLOB)?Если это так, вам не нужно использовать LongReadLen вообще;DBD :: Oracle предоставляет интерфейс потоковой передачи больших объектов.

Вам нужно связать параметр как тип ORA_CLOB или ORA_BLOB, что даст вам «локатор больших объектов»"возвращено из запроса вместо текста.Затем вы используете 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;
}
0 голосов
/ 08 декабря 2011

Я думаю об этом так:

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 in CPAN чтобы узнать больше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...