2024年PHP开发者大会 日本站

oci_set_prefetch

(PHP 5, PHP 7, PHP 8, PECL OCI8 >= 1.1.0)

oci_set_prefetch设置查询预取的行数

描述

oci_set_prefetch(资源 $statement, 整数 $rows): 布尔值

设置Oracle客户端库在成功调用oci_execute()后以及对数据库的每个后续内部提取请求要缓冲的行数。对于返回大量行的查询,通过将预取计数增加到默认值oci8.default_prefetch以上,可以显著提高性能。

预取是Oracle有效地从数据库中在每个网络请求中返回多于一行数据的方式。这可以提高网络和CPU利用率。行的缓冲在OCI8内部进行,无论预取计数如何,OCI8提取函数的行为保持不变。例如,oci_fetch_row() 将始终返回一行。预取缓冲区是每个语句的,不会被重新执行的语句或其他连接使用。

在调用oci_execute()之前调用oci_set_prefetch()

一个调整目标是将预取值设置为网络和数据库可以处理的合理大小。对于返回非常大量行的查询,如果从数据库中分块检索行(即,将预取值设置为小于行数),则整体系统效率可能会更好。这允许数据库在PHP脚本处理当前行集时处理其他用户的语句。

查询预取是在Oracle 8*i*中引入的。REF CURSOR预取是在Oracle 11*g*R2中引入的,当PHP与Oracle 11*g*R2(或更高版本)客户端库链接时发生。嵌套游标预取是在Oracle 11*g*R2中引入的,它需要Oracle客户端库和数据库版本都为11*g*R2或更高版本。

当查询包含LONG或LOB列时,不支持预取。预取值将被忽略,并且在所有不支持预取的情况下都将使用单行提取。

使用Oracle数据库12*c*时,PHP设置的预取值可以被Oracle的客户端oraaccess.xml配置文件覆盖。有关更多详细信息,请参阅Oracle文档。

参数

statement

oci_parse()创建并由oci_execute()执行的有效的OCI8语句标识符,或REF CURSOR语句标识符。

rows

要预取的行数,>= 0

返回值

成功时返回true,失败时返回false

示例

示例 #1 更改查询的默认预取值

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/XE');

$stid = oci_parse($conn, 'SELECT * FROM myverybigtable');
oci_set_prefetch($stid, 300); // 在调用 oci_execute() 之前设置
oci_execute($stid);

echo
"<table border='1'>\n";
while (
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
echo
"<tr>\n";
foreach (
$row as $item) {
echo
" <td>".($item !== null ? htmlentities($item, ENT_QUOTES) : "&nbsp;")."</td>\n";
}
echo
"</tr>\n";
}
echo
"</table>\n";

oci_free_statement($stid);
oci_close($conn);

?>

示例 #2 更改 REF CURSOR 提取的默认预取值

<?php
/*
创建PL/SQL存储过程如下:

CREATE OR REPLACE PROCEDURE myproc(p1 OUT SYS_REFCURSOR) AS
BEGIN
OPEN p1 FOR SELECT * FROM all_objects WHERE ROWNUM < 5000;
END;
*/

$conn = oci_connect('hr', 'welcome', 'localhost/XE');

$stid = oci_parse($conn, 'BEGIN myproc(:rc); END;');
$refcur = oci_new_cursor($conn);
oci_bind_by_name($stid, ':rc', $refcur, -1, OCI_B_CURSOR);
oci_execute($stid);

// 在执行游标之前更改预取数。
// 当PHP链接到Oracle 11gR2或更高版本的客户端库时,REF CURSOR预取有效
oci_set_prefetch($refcur, 200);
oci_execute($refcur);

echo
"<table border='1'>\n";
while (
$row = oci_fetch_array($refcur, OCI_ASSOC+OCI_RETURN_NULLS)) {
echo
"<tr>\n";
foreach (
$row as $item) {
echo
" <td>".($item !== null ? htmlentities($item, ENT_QUOTES) : "&nbsp;")."</td>\n";
}
echo
"</tr>\n";
}
echo
"</table>\n";

oci_free_statement($refcur);
oci_free_statement($stid);
oci_close($conn);

?>

如果PHP OCI8从REF CURSOR中提取数据,然后将REF CURSOR传递回第二个PL/SQL过程进行进一步处理,则将REF CURSOR预取计数设置为0以避免结果集中的行“丢失”。预取值是在每个OCI8内部数据库请求中提取的额外行数,因此将其设置为0意味着每次只提取一行。

示例#3 在将REF CURSOR传递回Oracle时设置预取值

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/orcl');

// 获取REF CURSOR
$stid = oci_parse($conn, 'BEGIN myproc(:rc_out); END;');
$refcur = oci_new_cursor($conn);
oci_bind_by_name($stid, ':rc_out', $refcur, -1, OCI_B_CURSOR);
oci_execute($stid);

// 显示两行,但不要预取任何额外行,否则
// 这些额外行将不会传递回myproc_use_rc()。
oci_set_prefetch($refcur, 0);
oci_execute($refcur);
$row = oci_fetch_array($refcur);
var_dump($row);
$row = oci_fetch_array($refcur);
var_dump($row);

// 将REF CURSOR传递给myproc_use_rc()以使用结果集进行更多数据处理
// with the result set
$stid = oci_parse($conn, 'begin myproc_use_rc(:rc_in); end;');
oci_bind_by_name($stid, ':rc_in', $refcur, -1, OCI_B_CURSOR);
oci_execute($stid);

?>

另请参阅

添加注释

用户贡献的注释 1 条注释

1
bmichael at goldparrot dot com
21 年前
如果您在任何项目中使用Oracle的OCI库(PHP也使用),则可以使用此限制。

我已经对该参数的影响进行了网络级别测试。它确实提高了效率。大幅度提高。

Oracle使用SQL*Net作为连接和数据库之间数据传输的机制。这就是为什么您必须正确设置Oracle的原因。

此参数告诉SQL*NET缓冲更多结果。当SQL*NET(在服务器端)收到数据请求时,它会将X行(1,2,3,1000等)捆绑在一起进行传输。它将相应的SQL*NET头发送回客户端,等待ACK,然后开始以MTU大小的块发送数据(以太网大约是1500字节,ATM的WAN大约是1000字节)。块大小也可以在SQL*NET中进行调整,但改进较少。

然后,TCP/IP将数据通过网络传输,将其分解成多个TCP/IP数据包。

交换完成后,SQL*NET客户端向
SQL*NET监听器(Oracle服务器)发送ACK,事务完成。

每次往返,SQL*NET都会在服务器内存(UGA - 用户全局区域)中查找查询结果。然后它获取发送所需的行列。如果是一行,与1000行相比。过程相同。

我可以告诉您很多关于数据库本身如何反应的信息。如果您能大幅减少往返次数……哇。

有关Oracle OCI的更多信息,请访问http://otn.oracle.com
To Top