会话游标缓存(session cursor cache)包含 SQL 和 PL/SQL(包括递归 SQL)的关闭会话游标。此缓存对于使用 Oracle Forms 的应用程序非常有用,因为从一个表单切换到另一个表单会关闭与第一个表单关联的所有会话游标。如果应用程序对同一组 SQL 语句重复发出解析调用,则重新打开会话游标可能会降低性能。通过重用游标,数据库减少了解析时间,从而加快了整体执行时间。
一、关于会话游标缓存
会话游标表示共享子游标的实例,该子游标存储在共享池中,用于特定会话。每个会话游标都存储对其已实例化的子游标的引用。
1.1 缓存机制
Oracle 数据库检查库缓存以确定是否对给定语句发出了三次以上的解析请求。如果游标已关闭 3 次,则 Oracle 数据库假定应缓存与该语句关联的会话游标,并将游标移动到会话游标缓存中。
通过一个实验来验证会话游标缓存
窗口1
cpp
select sid from v$mystat where rownum<2;
SID
----------
35
select object_name from t1 where object_id=9;
OBJECT_NAME
--------------------------------------------------------------------------------
I_FILE#_BLOCK#
SQL>
窗口2
cpp
set linesize 1000
col cursor_type for a30
select sid,sql_id,last_sql_active_time,cursor_type from v$open_cursor where sid=35 and sql_text like 'select object_name from t1 where object_id=9';
SID SQL_ID LAST_SQL_ CURSOR_TYPE
---------- ------------- --------- ------------------------------
35 gktg9sdjkxh6p 28-NOV-24 OPEN
当窗口1中的SQL执行3次,第3次执行时,状态发生了改变,从open变为SESSION CURSOR CACHED
cpp
SID SQL_ID LAST_SQL_ CURSOR_TYPE
---------- ------------- --------- ------------------------------
35 gktg9sdjkxh6p 28-NOV-24 SESSION CURSOR CACHED
同一会话解析 SQL 语句的后续请求将在数组中搜索指向共享游标的指针。如果找到该指针,则数据库取消引用该指针以确定共享游标是否存在。为了重用缓存中的游标,缓存管理器会检查游标的缓存状态是否与当前会话和系统环境匹配。
注意:重用缓存游标仍会被认为是硬解析,即使它不是硬解析。
1.2 会话游标老化算法
LRU 算法会删除会话游标缓存中的条目,以便在需要时为新条目腾出空间。缓存还使用基于时间的内部算法来老化已空闲一定时间的游标。
二、启用会话游标缓存
2.1 会话游标相关参数
以下初始化参数与会话游标缓存有关:
- SESSION_CACHED_CURSORS
该参数设置每个会话缓存的关闭游标的最大数量。默认值为 50。使用此参数可以为在同一会话中重复执行的语句重用缓存中的游标。
- OPEN_CURSORS
该参数指定一个会话可以同时打开的游标的最大数量。例如,如果其值设置为 1000,则每个会话一次最多可以打开 1000 个游标。
这些参数是独立的。例如,您可以将 SESSION_CACHED_CURSORS 参数的值设置为高于 OPEN_CURSORS 参数的值,因为会话游标在打开状态下不会被缓存。
2.2 配置会话游标缓存
要启用会话游标缓存:
- 确定缓存中保留的会话游标的最大数量。
- 执行以下操作之一:
-
要启用静态缓存,请将 SESSION_CACHED_CURSORS 参数的值设置为上一步中确定的数字。
-
要启用动态缓存,请执行以下语句:
cpp
ALTER SESSION SET SESSION_CACHED_CURSORS = value;
2.3 配置示例
1)设置打开游标参数
cpp
ALTER SESSION SET SESSION_CACHED_CURSORS = 2000;
2)打开一定数量的游标
cpp
declare
msql varchar2(2000);
mcur number;
mstat number;
begin
for i in 1..1000 loop
mcur := dbms_sql.open_cursor;
msql := 'select object_id from t1 where object_id='||to_char(i);
dbms_sql.parse(mcur,msql,dbms_sql.native);
mstat :=dbms_sql.execute(mcur);
end loop;
end;
/
注意:测试环境使用,生产环境上不要执行此代码
3)查询打开游标的数量
cpp
select count(1) from v$open_cursor;
COUNT(1)
----------
1000
SQL>
三、调整会话游标缓存的大小
使用 V$SESSTAT 视图确定会话游标缓存的大小是否足以容纳数据库实例。
要调整会话游标缓存的大小:
- 查询 V$SESSTAT 视图以确定当前在特定会话中缓存了多少游标。
- 查询 V$SESSTAT 视图以查找在会话游标缓存中找到游标的解析调用的百分比。
- 如果满足以下条件,请考虑增加 SESSION_CACHED_CURSORS 参数的值:
- 会话游标缓存计数接近最大值
- 相对于总解析,会话游标缓存命中的百分比较低
- 应用程序重复执行相同查询的解析调用
示例 3-1 查询 V$SESSTAT 视图
以下查询可查找特定会话中当前缓存的游标数量:
cpp
set linesize 1000
col MAX_CACHED for a20
col USERNAME for a20
SELECT a.value curr_cached, p.value max_cached,
s.username, s.sid, s.serial#
FROM v$sesstat a, v$statname b, v$session s, v$parameter2 p
WHERE a.statistic# = b.statistic# and s.sid=a.sid and a.sid=&sid
AND p.name='session_cached_cursors'
AND b.name = 'session cursor cache count';
此查询的输出可能如下所示:
cpp
CURR_CACHED MAX_CACHED USERNAME SID SERIAL#
----------- ---------- -------- ----- ----------
49 50 APP 35 263
此输出显示当前为会话 35 缓存的游标数量接近最大值。
以下查询查找在会话游标缓存中找到游标的解析调用的百分比:
cpp
SELECT cach.value cache_hits, prs.value all_parses,
round((cach.value/prs.value)*100,2) as "% found in cache"
FROM v$sesstat cach, v$sesstat prs, v$statname nm1, v$statname nm2
WHERE cach.statistic# = nm1.statistic#
AND nm1.name = 'session cursor cache hits'
AND prs.statistic#=nm2.statistic#
AND nm2.name= 'parse count (total)'
AND cach.sid= &sid and prs.sid= cach.sid;
此查询的输出可能如下所示:
CACHE_HITS ALL_PARSES % found in cache
---------- ---------- ----------------
34 700 4.57
此输出显示会话 35 的会话游标缓存中的命中数与解析总数相比较低。
在此示例中,将 SESSION_CACHED_CURSORS 参数的值设置为 100 可能有助于提高性能。