暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle 19C 关于文字和绑定变量

原创 Asher.HU 2021-02-04
534

 

绑定变量对于Oracle数据库应用程序中的游标共享至关重要。

本节包含以下主题:

 

20.1.3.1文字和游标

在构造SQL语句时,某些Oracle应用程序使用文字而不是绑定变量。

例如,该语句SELECT SUM(salary) FROM hr.employees WHERE employee_id < 101 使用文字值101作为员工ID。默认情况下,当类似的语句不使用绑定变量时,Oracle数据库将无法利用游标共享因此,Oracle数据库将除value 102或任何其他随机值之外相同的语句视为全新的语句,需要进行硬解析。

真实世界性能小组确定使用文字的应用程序经常是导致性能,可伸缩性和安全性问题的原因。在现实世界中,在不考虑游标共享的情况下快速编写应用程序的情况并不少见。一个经典的示例是“屏幕抓取”应用程序,该应用程序将内容从Web表单中复制出来,然后连接字符串以动态构造SQL语句。


使用文字值导致的主要问题包括:

  • 连接最终用户输入的文字的应用程序容易受到SQL注入攻击。仅重写应用程序以使用绑定变量可以消除这种威胁。
  • 如果每个语句都经过硬解析,则不会共享游标,因此数据库必须消耗更多内存才能创建游标。
  • 硬解析时,Oracle数据库必须锁存共享池和库缓存。随着硬解析的数量增加,等待锁存共享池的进程数量也随之增加。这种情况会降低并发性并增加争用。

视频:


示例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


也可以看看:

 不要使用CURSOR_SHARING = FORCE作为永久修复程序 以了解SQL注入


20.1.3.2绑定变量和游标

您可以开发Oracle应用程序以使用绑定变量而不是文字。

绑定变量是在查询中的占位符。例如,该语句SELECT SUM(salary) FROM hr.employees WHERE employee_id < :emp_id将绑定变量:emp_id用于员工ID。

真实世界性能小组发现,使用绑定变量的应用程序性能更好,伸缩性更好,更安全。


使用绑定变量所带来的主要好处包括:

  • 使用绑定变量的应用程序不像使用文字的应用程序一样容易受到相同的SQL注入攻击。
  • 当相同的语句使用绑定变量时,当将不同的值绑定到同一条语句时,Oracle数据库可以利用游标共享,并共享计划和其他信息。
  • Oracle数据库避免了闩锁硬解析所需的共享池和库缓存的开销。

视频:



示例20-7绑定变量和共享游标

下面的示例使用VARIABLE在SQL * Plus命令创建emp_id绑定变量,然后执行使用三种不同的绑定值的查询(101120,和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_COUNT1指示数据库重用了相同的子游标,而不是创建三个单独的子游标。使用绑定变量使这种重用成为可能。

 


20.1.3.3绑定变量 偷看

绑定变量窥视(也称为bind peeking)中,当数据库执行语句的硬解析时,优化器查看绑定变量中的值。

优化器不会在每次解析之前查看绑定变量值。而是,优化器仅在硬解析期间首次调用优化器时才进行窥视

当查询使用文字时,优化器可以使用文字值来找到最佳计划。但是,当查询使用绑定变量时,优化器必须选择最佳计划,而SQL文本中不存在文字。此任务可能非常困难。通过在初始硬解析期间查看绑定值,优化器可以确定WHERE子句条件的基数,就好像使用了文字一样,从而改进了计划。

因为优化器仅在硬解析期间查看绑定值,所以该计划可能并非对所有可能的绑定值都是最佳的以下示例说明了这一原理。


示例20-8文字导致不同的执行计划

假定您执行以下语句,其执行使用不同文字三种不同的语句(101120,和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中执行的查询,以使用绑定变量代替文字。您将相同的值(101120165)绑定到bind变量: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这是通过自适应游标共享解决的问题。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论