我想知道使用Perl DBI从Oracle数据库中提取任意大数据字段的最节省内存的方法。我知道使用的方法是将数据库句柄上的“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 ...
});
然而,这仍然是低效的,因为外围数据并不代表每条记录。最大值超过MB,但平均记录小于KB。我希望能够在尽可能少浪费未使用的缓冲区的同时提取所有信息(即没有截断)。
我考虑过的一种方法是以块的形式提取数据,一次说50条记录,并将LongReadLen设置为该块的最大记录长度。另一个可以但不必依赖于块构思的工作是分叉子进程,检索数据,然后杀死子进程(利用它浪费内存)。最棒的是强制释放DBI缓冲区的能力,但我认为这不可行。
有没有人解决过类似的问题,取得了哪些成功?谢谢你的帮助!
修改
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 );
“之前”的驻留内存使用量为18MB,“之后”的使用量为30MB。这对于大量查询来说是不可接受的。
答案 0 :(得分:5)
您的列是否包含大数据LOB(CLOB或BLOB)?如果是这样,您根本不需要使用LongReadLen; DBD :: Oracle提供了一个LOB流接口。
您要做的是bind the 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;
}
答案 1 :(得分:0)
我这样想:
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了解详情。