我想知道使用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。对于大量查询,这是 Not Acceptable 。

最佳答案

您的列是否具有大数据LOB(CLOB或BLOB)?如果是这样,则根本不需要使用LongReadLen。 DBD::Oracle提供了LOB流接口(interface)。

您要做的是将bind the param键入为ORA_CLOBORA_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;
}

09-27 07:10