SQL调优指南笔记20:Improving Real-World Performance Through Cursor Sharing
Posted dingdingfish
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL调优指南笔记20:Improving Real-World Performance Through Cursor Sharing相关的知识,希望对你有一定的参考价值。
本文为SQL Tuning Guide第20章“Improving Real-World Performance Through Cursor Sharing”的笔记。
游标共享可以将数据库应用程序性能提高几个数量级。
重要基础概念
-
optimizer goal
The prioritization of resource usage by the optimizer. Using the OPTIMIZER_MODE initialization parameter, you can set the optimizer goal best throughput or best response time. -
bind variable
A placeholder in a SQL statement that must be replaced with a valid value or value address for the statement to execute successfully. By using bind variables, you can write a SQL statement that accepts inputs or parameters at run time.
20.1 Overview of Cursor Sharing
Oracle 数据库可以共享游标,游标是指向共享池中私有 SQL 区域的指针。
20.1.1 About Cursors
私有 SQL 区域保存有关已解析 SQL 语句的信息和其他特定于会话的信息以供处理。
当服务器进程执行 SQL 或 PL/SQL 代码时,该进程使用私有 SQL 区域来存储绑定变量值、查询执行状态信息和查询执行工作区域。 每次执行语句的私有 SQL 区域不共享,并且可能包含不同的值和数据。
游标是特定私有 SQL 区域的名称或句柄。 游标包含特定于会话的状态信息,例如绑定变量值和结果集。
如下图所示,您可以将游标视为客户端的指针和服务器端的状态。 因为游标与私有 SQL 区域密切相关,所以这些术语有时可以互换使用。
20.1.1.1 Private and Shared SQL Areas
私有 SQL 区域中的游标指向库缓存中的共享 SQL 区域。
与包含会话状态信息的私有 SQL 区域不同,共享 SQL 区域包含语句的分析树和执行计划。 例如,SELECT * FROM employees
的执行具有存储在一个共享 SQL 区域中的计划和分析树。又例如, SELECT * FROM employees
的执行在语法和语义上都不同,它另有一个存储在单独的共享 SQL 区域中的计划和分析树。
同一或不同会话中的多个私有 SQL 区域可以引用单个共享 SQL 区域,这种现象称为游标共享。 例如,在一个会话中执行 SELECT * FROM employees
和在不同会话中执行 SELECT * FROM employees
(访问同一个表)可以使用相同的分析树和计划。 由多个语句访问的共享 SQL 区域称为共享游标。
Oracle 数据库使用以下步骤自动确定发出的 SQL 语句或 PL/SQL 块在文本上是否与当前在库缓存中的另一条语句相同:
- 计算语句文本的哈希值。
- 数据库为共享池中的现有 SQL 语句查找匹配的哈希值。 以下选项是可能的:
– 不存在匹配的哈希值。
在这种情况下,共享池中当前不存在 SQL 语句,因此数据库执行硬解析。 这将结束共享池检查。
– 存在匹配的哈希值。
在这种情况下,数据库会进行下一步,即文本匹配。 - 数据库将匹配语句的文本与哈希语句的文本进行比较,以确定它们是否相同。 以下选项是可能的:
– 文本匹配失败。
在这种情况下,文本匹配过程停止,导致硬解析。
– 文本匹配成功。
在这种情况下,数据库进行下一步:确定 SQL 是否可以共享现有的父游标。
要发生文本匹配,SQL 语句或 PL/SQL 块的文本必须逐个字符相同,包括空格、大小写和注释。 例如,以下语句不能使用相同的共享 SQL 区域:
通常,仅文字(literal)不同(本例为121和247)的 SQL 语句不能使用相同的共享 SQL 区域。 例如,以下语句不会解析到相同的 SQL 区域:SELECT * FROM employees; SELECT * FROM Employees; SELECT * FROM employees;
此规则的唯一例外是当参数 CURSOR_SHARING 已设置为 FORCE 时,在这种情况下,类似的语句可以共享 SQL 区域。SELECT count(1) FROM employees WHERE manager_id = 121; SELECT count(1) FROM employees WHERE manager_id = 247;
20.1.1.2 Parent and Child Cursors
每个已解析的 SQL 语句都有一个父游标和一个或多个子游标。
父游标存储 SQL 语句的文本。 如果两个语句的文本相同,则这两个语句共享同一个父游标。 但是,如果文本不同,则数据库会创建一个单独的父游标。
在这个例子中,前两个语句在语法上是不同的(字母“c”在第一个语句中是小写,在第二个语句中是大写),但在语义上是相同的。 由于语法差异,这些语句具有不同的父游标。 第三个语句在语法上与第一个语句(小写“c”)相同,但在语义上不同,因为它引用了不同模式中的客户表。 由于语法相同,第三条语句可以与第一条语句共享一个父游标。
SQL> CONNECT oe@inst1
Enter password: *******
Connected.
SQL> SELECT COUNT(*) FROM customers;
COUNT(*)
----------
319
SQL> SELECT COUNT(*) FROM Customers;
COUNT(*)
----------
319
SQL> CONNECT sh@inst1
Enter password: *******
Connected.
SQL> SELECT COUNT(*) FROM customers;
COUNT(*)
----------
55500
下面的 V$SQL 查询指示了两个父节点。 SQL ID 为 8h916vv2yw400 的语句,即该语句的小写“c”版本,有一个父游标和两个子游标:child 0 和 child 1。SQL ID 为 5rn2uxjtpz0wd 的语句,即大写的“ c” 版本的语句,具有不同的父游标和只有一个子游标:child 0。
SQL> CONNECT SYSTEM@inst1
Enter password: *******
Connected.
COL SQL_TEXT FORMAT a30
COL CHILD# FORMAT 99999
COL EXEC FORMAT 9999
COL SCHEMA FORMAT a6
SELECT SQL_ID, PARSING_SCHEMA_NAME AS SCHEMA, SQL_TEXT,
CHILD_NUMBER AS CHILD#, EXECUTIONS AS EXEC FROM V$SQL
WHERE SQL_TEXT LIKE '%ustom%' AND SQL_TEXT NOT LIKE '%SQL_TEXT%' ORDER BY SQL_ID;
SQL_ID SCHEMA SQL_TEXT CHILD# EXEC
------------- ------ ------------------------------ ------ -----
5rn2uxjtpz0wd OE SELECT COUNT(*) FROM Customers 0 1
8h916vv2yw400 OE SELECT COUNT(*) FROM customers 0 1
8h916vv2yw400 SH SELECT COUNT(*) FROM customers 1 1
20.1.1.2.1 Parent Cursors and V$SQLAREA
V$SQLAREA 视图为每个父游标包含一行。
在下面的示例中,V$SQLAREA 查询显示了两个父游标,每个游标都用不同的 SQL_ID 标识。 VERSION_COUNT 表示子游标的数量。
此SQL原文有错
COL SQL_TEXT FORMAT a30
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM V$SQLAREA
WHERE SQL_TEXT LIKE '%ustomer%'
AND SQL_TEXT NOT LIKE '%SQL_TEXT%';
SQL_TEXT SQL_ID VERSION_COUNT HASH_VALUE
------------------------------ ------------- ------------- ----------
SELECT COUNT(*) FROM customers 8h916vv2yw400 2 3320713216
SELECT COUNT(*) FROM Customers 5rn2uxjtpz0wd 1 1935639437
在前面的输出中,SELECT * FROM customers
的 VERSION_COUNT 为 2 表示多个子游标,这是必要的,因为该语句是针对两个不同的对象执行的。 相反,语句 SELECT * FROM Customers(注意大写“E”)只执行一次,因此有一个父游标和一个子游标(VERSION_COUNT 为 1)。
20.1.1.2.2 Child Cursors and V$SQL
每个父游标都有一个或多个子游标。
子游标包含执行计划、绑定变量、有关查询中引用的对象的元数据、优化器环境和其他信息。 与父游标相比,子游标不存储 SQL 语句的文本。
如果语句能够重用父游标,则数据库检查该语句是否可以重用现有的子游标。 数据库执行多项检查,包括以下内容:
- 数据库将发出的语句中引用的对象与池中语句引用的对象进行比较,以确保它们都是相同的。
在 SQL 语句或 PL/SQL 块中对模式对象的引用必须解析为相同模式中的相同对象。 例如,如果两个用户发出以下 SQL 语句,并且每个用户都有自己的雇员表,则以下语句不完全相同,因为该语句为每个用户引用不同的雇员表:
SELECT * FROM employees;
注意:数据库可以共享私有临时表的游标,但只能在同一个会话中。 数据库将会话标识符关联为游标上下文的一部分。 在软解析过程中,只有当前会话 ID 与游标上下文中的会话 ID 匹配时,数据库才能共享子游标。
- 数据库确定优化器模式是否相同。
例如,必须使用相同的优化器目标来优化 SQL 语句。
示例 20-2 多个子游标
V$SQL 描述当前驻留在库缓存中的语句。 它为每个子游标包含一行,如下例所示:
COL SQL_TEXT FORMAT A30
COL USR FORMAT A6
SELECT SQL_TEXT, SQL_ID, USERNAME AS USR, CHILD_NUMBER AS CHILD#,
HASH_VALUE, PLAN_HASH_VALUE AS PLAN_HASHV
FROM V$SQL s, DBA_USERS d
WHERE SQL_TEXT LIKE '%ustomer%'
AND SQL_TEXT NOT LIKE '%SQL_TEXT%'
AND d.USER_ID = s.PARSING_USER_ID;
SQL_TEXT SQL_ID USR CHILD# HASH_VALUE PLAN_HASHV
------------------------------ ------------- ------ ---------- ---------- ----------
SELECT COUNT(*) FROM customers 8h916vv2yw400 OE 0 3320713216 1140397121
SELECT COUNT(*) FROM Customers 5rn2uxjtpz0wd OE 0 1935639437 1140397121
SELECT COUNT(*) FROM customers 8h916vv2yw400 SH 1 3320713216 237477902
20.1.1.2.3 Cursor Mismatches and V$SQL_SHARED_CURSOR
如果父游标有多个子游标,则 V$SQL_SHARED_CURSOR 视图提供有关游标未共享的原因的信息。 对于几种类型的不兼容,TRANSLATION_MISMATCH 列指示与值 Y 或 N 不匹配。
示例 20-3 翻译不匹配
在此示例中,TRANSLATION_MISMATCH 列显示两个语句 (SELECT * FROM employees) 引用了不同的对象,导致最后一个语句的 TRANSLATION_MISMATCH 值为 Y。 因为无法共享,所以每个语句都有一个单独的子游标,如 CHILD_NUMBER 的 0 和 1 所示。
SELECT S.SQL_TEXT, S.CHILD_NUMBER, s.CHILD_ADDRESS,
C.TRANSLATION_MISMATCH
FROM V$SQL S, V$SQL_SHARED_CURSOR C
WHERE SQL_TEXT LIKE '%ustomer%'
AND SQL_TEXT NOT LIKE '%SQL_TEXT%'
AND S.CHILD_ADDRESS = C.CHILD_ADDRESS;
SQL_TEXT CHILD_NUMBER CHILD_ADDRESS T
------------------------------ ------------ ---------------- -
SELECT COUNT(*) FROM customers 0 00000000A7208390 N
SELECT COUNT(*) FROM customers 1 00000000A7192130 Y
SELECT COUNT(*) FROM Customers 0 00000000A71F3EF8 N
20.1.2 About Cursors and Parsing
如果应用程序发出一条语句,并且 Oracle 数据库无法重用游标,则它必须构建应用程序代码的新可执行版本。 此操作称为硬解析。
软解析是不是硬解析的任何解析,并且在数据库可以重用现有代码时发生。 一些软解析比其他软解析占用更少的资源。 例如,如果该语句的父游标已经存在,那么Oracle数据库可以进行各种优化,然后将子游标存储在共享SQL区域中。 但是,如果父游标不存在,则 Oracle 数据库还必须将父游标存储在共享 SQL 区域中,这会产生额外的内存开销。
实际上,硬解析会在运行语句之前重新编译它。 在每次执行之前硬解析 SQL 语句类似于在每次执行之前重新编译 C 程序。 硬解析执行如下操作:
- 检查 SQL 语句的语法
- 检查 SQL 语句的语义
- 检查发出语句的用户的访问权限
- 创建执行计划
- 多次访问库缓存和数据字典缓存以检查数据字典
硬解析的一个特别耗费资源的方面是多次访问库缓存和数据字典缓存以检查数据字典。 当数据库访问这些区域时,它使用一个称为锁存器的序列化设备,以便在检查期间它们的定义不会改变。 锁存器争用会增加语句执行时间并降低并发性。
由于上述所有原因,硬解析的 CPU 和内存开销会造成严重的性能问题。 这些问题在从表单接受用户输入然后动态生成 SQL 语句的 Web 应用程序中尤为明显。 真实世界性能组强烈建议尽可能减少硬解析。
https://youtu.be/WcRcmfSIajc
20.1.3 About Literals and Bind Variables
绑定变量对于 Oracle 数据库应用程序中的游标共享至关重要。
20.1.3.1 Literals and Cursors
在构造 SQL 语句时,某些 Oracle 应用程序使用文字而不是绑定变量。
例如,语句 SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101 使用文字值 101 作为员工 ID。 默认情况下,当类似语句不使用绑定变量时,Oracle 数据库无法利用游标共享。 因此,Oracle 数据库将除了值 102 或任何其他随机值之外完全相同的语句视为一条全新的语句,需要进行硬解析。
Real-World Performance 小组已确定使用文字的应用程序是性能、可伸缩性和安全问题的常见原因。 在现实世界中,不考虑游标共享而快速编写应用程序的情况并不少见。 一个典型的例子是“屏幕抓取”应用程序,它从 Web 表单中复制内容,然后连接字符串以动态构造 SQL 语句。
使用文字值导致的主要问题包括:
- 连接最终用户输入的文字的应用程序容易受到 SQL 注入攻击。 只有重写应用程序以使用绑定变量才能消除这种威胁。
- 如果每个语句都被硬解析,则游标不共享,因此数据库必须消耗更多内存来创建游标。
- Oracle 数据库在硬解析时必须锁定共享池和库缓存。 随着硬解析数量的增加,等待锁定共享池的进程数量也会增加。 这种情况会降低并发性并增加争用。
https://youtu.be/EBdZ-RE2HFs
示例 20-6 文字和游标共享
考虑一个执行以下语句的应用程序,它们仅在文字上有所不同:
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165;
下面的 V$SQLAREA 查询表明这三个语句需要三个不同的父游标。 如 VERSION_COUNT 所示,每个父游标都需要其自己的子游标。
COL SQL_TEXT FORMAT a30
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM V$SQLAREA
WHERE SQL_TEXT LIKE '%mployee%'
AND SQL_TEXT NOT LIKE '%SQL_TEXT%';
SQL_TEXT SQL_ID VERSION_COUNT HASH_VALUE
------------------------------ ------------- ------------- ----------
SELECT SUM(salary) FROM hr.emp b1tvfcc5qnczb 1 191509483
loyees WHERE employee_id < 165
SELECT SUM(salary) FROM hr.emp cn5250y0nqpym 1 2169198547
loyees WHERE employee_id < 101
SELECT SUM(salary) FROM hr.emp au8nag2vnfw67 1 3074912455
loyees WHERE employee_id < 120
20.1.3.2 Bind Variables and Cursors
您可以开发 Oracle 应用程序以使用绑定变量而不是文字。
绑定变量是查询中的占位符。 例如,语句 SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id 使用绑定变量:emp_id 作为员工 ID。
Real-World Performance 小组发现使用绑定变量的应用程序性能更好、扩展性更好并且更安全。 使用绑定变量带来的主要好处包括:
- 使用绑定变量的应用程序不易受到与使用文字的应用程序相同的 SQL 注入攻击。
- 当相同的语句使用绑定变量时,Oracle 数据库可以利用游标共享,并在不同的值绑定到同一语句时共享计划和其他信息。
- Oracle 数据库避免了锁定硬解析所需的共享池和库缓存的开销。
示例 20-7 绑定变量和共享游标
以下示例使用 SQL*Plus 中的 VARIABLE 命令创建 emp_id 绑定变量,然后使用三个不同的绑定值(101、120 和 165)执行查询:
VARIABLE emp_id NUMBER
EXEC :emp_id := 101;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
EXEC :emp_id := 120;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
EXEC :emp_id := 165;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
下面的 V$SQLAREA 查询显示了一个唯一的 SQL 语句:
COL SQL_TEXT FORMAT a34
SELECT SQL_TEXT, SQL_ID, VERSION_COUNT, HASH_VALUE
FROM V$SQLAREA
WHERE SQL_TEXT LIKE '%mployee%'
AND SQL_TEXT NOT LIKE '%SQL_TEXT%';
SQL_TEXT SQL_ID VERSION_COUNT HASH_VALUE
---------------------------------- ------------- ------------- ----------
SELECT SUM(salary) FROM hr.employe 4318cbskba8yh 1 615850960
es WHERE employee_id < :emp_id
VERSION_COUNT 值为 1 表示数据库重复使用同一个子游标,而不是创建三个单独的子游标。 使用绑定变量使这种重用成为可能。
20.1.3.3 Bind Variable Peeking
在绑定变量查看(也称为绑定查看)中,优化器在数据库执行语句的硬解析时查看绑定变量中的值。
优化器不会在每次解析之前查看绑定变量值。 相反,优化器仅在首次调用优化器时进行查看,这是在硬解析期间。
当查询使用文字时,优化器可以使用文字值来找到最佳计划。 但是,当查询使用绑定变量时,优化器必须在 SQL 文本中不存在文字的情况下选择最佳计划。 这项任务可能非常困难。 通过在初始硬解析期间查看绑定值,优化器可以确定 WHERE 子句条件的基数,就好像使用了文字一样,从而改进计划。
因为优化器只在硬解析期间查看绑定值,所以计划可能不是对所有可能的绑定值都是最优的。 下面的例子说明了这个原则。
示例 20-8 文字导致不同的执行计划
假设您执行以下语句,这些语句使用不同的文字(101、120 和 165)执行三个不同的语句,然后显示每个语句的执行计划:
SET LINESIZE 167
SET PAGESIZE 0
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
数据库硬解析了所有三个不相同的语句。 DISPLAY_CURSOR 输出(为清楚起见进行了编辑)显示优化器为前两个语句选择了相同的索引范围扫描计划,但为使用文字 165 的语句选择了全表扫描计划:
SQL_ID cn5250y0nqpym, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101
Plan hash value: 2410354593
-------------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost(%CPU)|Time|
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | |2 (100)| |
| 1| SORT AGGREGATE | |1 | 8 | | |
| 2| TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES |1 | 8 |2 (0) | 00:00:01 |
|*3| INDEX RANGE SCAN | EMP_EMP_ID_PK |1 | |1 (0) | 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("EMPLOYEE_ID"<101)
SQL_ID au8nag2vnfw67, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 120
Plan hash value: 2410354593
-------------------------------------------------------------------------------------
|Id| Operation | Name |Rows|Bytes|Cost(%CPU)|Time|
-------------------------------------------------------------------------------------
| 0| SELECT STATEMENT | | | |2 (100)| |
| 1| SORT AGGREGATE | |1 | 8 | | |
| 2| TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES |20|160|2 (0) | 00:00:01 |
|*3| INDEX RANGE SCAN | EMP_EMP_ID_PK |20| |1 (0) | 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("EMPLOYEE_ID"<120)
SQL_ID b1tvfcc5qnczb, child number 0
-------------------------------------
SELECT SUM(salary) FROM hr.employees WHERE employee_id < 165
Plan hash value: 1756381138
-------------------------------------------------------------------------
| Id | Operation | Name |Rows| Bytes |Cost(%CPU)| Time |
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
| 1 | SORT AGGREGATE | | 1 | 8 | | |
|* 2 | TABLE ACCESS FULL| EMPLOYEES | 66 | 528 | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("EMPLOYEE_ID"<165)
前面的输出表明,对于返回更多行的查询,优化器认为全表扫描比索引扫描更有效。
示例 20-9 绑定变量导致游标重用
此示例重写了示例 20-8 中执行的查询,以使用绑定变量而不是文字。 您将相同的值(101、120 和 165)绑定到绑定变量 :emp_id,然后显示每个值的执行计划:
VAR emp_id NUMBER
EXEC :emp_id := 101;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
EXEC :emp_id := 120;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
EXEC :emp_id := 165;
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR());
DISPLAY_CURSOR 输出显示优化器为所有三个语句选择了完全相同的计划:
SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id
Plan hash value: 2410354593
-------------------------------------------------------------------------------------
| Id | Operation | Name |Rows|Bytes|Cost (%CPU)|Time|
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | |2 (100)| |
| 1 | SORT AGGREGATE | |1|8 | | |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES |1|8 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | EMP_EMP_ID_PK |1| | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("EMPLOYEE_ID"<:EMP_ID)
相比之下,当上述语句以字面量执行时,优化器在员工ID值为165时选择了成本较低的全表扫描。这就是自适应游标共享解决的问题。
20.1.4 About the Life Cycle of Shared Cursors
当优化器解析出一个新的非 DDL 的 SQL 语句时,数据库会分配一个新的共享 SQL 区。 所需的内存量取决于语句的复杂性。
数据库可以从共享池中删除共享 SQL 区域,即使该区域对应于一个长时间未使用的打开游标。 如果打开的游标稍后用于运行它的语句,那么数据库将重新分析该语句并分配一个新的共享 SQL 区域。 数据库不会删除其语句正在执行或其行尚未完全提取的游标。
由于依赖模式对象或优化器统计信息的更改,共享 SQL 区域可能会变得无效。 Oracle 数据库使用两种技术来管理游标生命周期:失效和滚动失效。
20.1.4.1 Cursor Marked Invalid
当共享 SQL 区域被标记为无效时,数据库可以将其从共享池中删除,连同一段时间未使用的有效游标。
在某些情况下,数据库必须执行与共享池中无效共享 SQL 区域关联的语句。 在这种情况下,数据库在执行之前对语句进行硬解析。
当满足以下条件时,数据库立即将依赖的共享 SQL 区域标记为无效:
- 当 NO_INVALIDATE 参数为 FALSE 时,DBMS_STATS 收集表、表簇或索引的统计信息。
- SQL 语句引用模式对象,稍后由使用立即游标失效(默认)的 DDL 语句修改。
您可以在 ALTER TABLE … IMMEDIATE VALIDATION 和 ALTER INDEX … IMMEDIATE VALIDATION 等语句上手动指定立即失效,或者在会话或系统级别将 CURSOR_INVALIDATION 初始化参数设置为 IMMEDIATE。
注意:使用 DEFERRED VALIDATION 子句的 DDL 语句会覆盖 CURSOR_INVALIDATION 初始化参数的 IMMEDIATE 设置。
当满足上述条件时,数据库会在下次执行时重新解析受影响的语句。
当数据库使游标无效时,V
S
Q
L
.
I
N
V
A
L
I
D
A
以上是关于SQL调优指南笔记20:Improving Real-World Performance Through Cursor Sharing的主要内容,如果未能解决你的问题,请参考以下文章 SQL调优指南笔记1:Introduction to SQL Tuning