1.行预取
在应用程序从数据库读取数据的时候,它可以一条一条地读取,也可以更好一点,一次读取多条记录。一次读取多条记录被称为行预取.
1.1运行机制
每次应用程序请求驱动从数据库返回1条记录的时候,会预取多条记录并将它们存储在客户端的内存中。这样,多个后续的请求就不需要执行数据库调用来读取数据。可以直接从客户端内存中得到它们。结果,到数据库的往返次数随预取记录数量的增加呈比例地降低。因此,检索包含大量记录的结果集的开销会显著降低。
必须明白的是,在没有行预取的情况下,检索性能之所以差并不是数据库引擎造成的。而是应用程序导致了低性能,返回来又被低性能拖累。
尽管行预取功能对客户端来讲更加重要,数据库也能从中得到好处。事实上,行预取功能显著地降低了逻辑读的次数。
下面演示在PL/SQL、JDBC以及ODP.NET环境下有效地使用行预取功能。
1。PL/SQL
如果在编译的时候动态初始化参数plsql_optimize_level被设置成2或者2以上,就会针对基于游标的FOR循环使用行预取功能。
ALTER SESSION SET plsql_optimize_level = 2;
BEGIN
FOR c IN (SELECT * FROM t)
LOOP
-- process data
NULL;
END LOOP;
END;
必须强调的是,行预取功能只能自动地应用到基于游标的FOR循环中。要对其他类型的游标使用行预取功能,必须使用BULK COLLECT 子句。下面是一个使用它来操作隐式游标的例子。
DECLARE
TYPE t_t IS TABLE OF t%ROWTYPE;
l_t t_t;
BEGIN
SELECT * BULK COLLECT INTO l_t
FROM t;
FOR i IN l_t.FIRST..l_t.LAST
LOOP
-- process data
NULL;
END LOOP;
END;
在前面的PL/SQL匿名块中,结果集中的所有记录都是在一次读取中返回的。如果记录数量很大,会需要使用大量的内存来保存它。因此,在实际操作中,要么知道要返回的记录数是有限的,要么需要使用LIMIT子句为每次读取设置一个范围。示例如下:
DECLARE
CURSOR c IS SELECT * FROM t;
TYPE t_t IS TABLE OF t%ROWTYPE;
l_t t_t;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_t LIMIT 100;
EXIT WHEN l_t.COUNT = 0;
FOR i IN l_t.FIRST..l_t.LAST
LOOP
-- process data
NULL;
END LOOP;
END LOOP;
CLOSE c;
END;
包dbms_sql、本地动态SQL和RETURNING子句都支持行预取功能。
2.JDBC
Oracle JDBC驱动默认启用了行预取功能。可以用两种方式来改变默认的预取记录的数量。第一种是在使用类OracleDataSource或OracleDriver创建数据库的连接时指定属性defaultRowPrefetch。示例如下:
connectionProperties = new Properties();
connectionProperties.put("user",user);
connectionProperties.put("password",password);
connectionProperties.put("defaultRowPrefetch","100");
dataSource.setConnectionProperties(connectionProperties );
第二种方法是在Staement类中使用方法setFetchSize来覆盖连接级别的默认值。示例如下:
sql = "SELECT id, pad FROM t";
statement = connection.prepareStatement(sql);
statement.setFetchSize(100);
resultset = statement.executeQuery();
while (resultset.next())
{
id = resultset.getLong("id");
pad = resultset.getString("pad");
// process data
}
resultset.close();
statement.close();
3.ODP.NET
ODP.NET定义的默认读取大小(65536)是字节数而不是记录数。可以通过类OracleCommand和OrcleDataReader提供的属性FetchSize来改变它的大小。示例如下:
String sql;
OracleCommand command;
OracleDataReader reader;
Decimal id;
String pad;
sql = "SELECT id, pad FROM t";
command = new OracleCommand(sql, connection);
command.AddToStatementCache = false;
reader = command.ExecuteReader();
reader.FetchSize = command.RowSize * fetchSize;
while (reader.Read())
{
id = reader.GetDecimal(0);
pad = reader.GetString(1);
// process data
// Console.WriteLine(id + " , " + pad);
}
reader.Close();