从一个简单的示例入手:
创建测试表
create table t1 as select * from dba_objects;
使用一段java 代码做测试。
package dbtest; import java.sql.*; public class Dbtest { public static final String drive = "oracle.jdbc.driver.OracleDriver"; public static final String url = "jdbc:oracle:thin:@//192.168.196.200:1521/orcl2pdb"; public static final String DBUSER="wxc"; public static final String password="wxc"; public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub Connection conn = null;//表示数据库连接 Statement stmt= null;//表示数据库的更新 ResultSet result = null;//查询数据库 Class.forName(drive);//使用class类来加载程序 conn =DriverManager.getConnection(url,DBUSER,password); //连接数据库 //Statement接口要通过connection接口来进行实例化操作 conn.setAutoCommit(false); stmt = conn.createStatement(); //执行SQL语句来查询数据库 stmt.execute("truncate table t9"); long t1 = System.currentTimeMillis(); for (int i = 0; i<=30000; i++ ){ stmt.execute("insert into t9 values ("+i+")"); } conn.commit(); long t2 = System.currentTimeMillis(); System.out.println("不使用邦定变量测试"+ (t2 - t1)); stmt.execute("truncate table t9"); long t3 = System.currentTimeMillis(); PreparedStatement pstmt =conn.prepareStatement("insert into t9 values (?)"); for (int i = 0 ; i<=30000; i++ ){ pstmt.setInt(1, i);
pstmt.executeUpdate(); } conn.commit(); long t4 = System.currentTimeMillis(); System.out.println("邦定变量测试"+ (t4 - t3)); conn.close();//关闭数据库 } }
不使用邦定变量测试36805
邦定变量测试17080
结果可以看出,使用绑定变量和不使用绑定 变量结果相差几倍;
如下截图中,使用绑定变量时,只生成一个游标,如第一行所示,不使用绑定变量时,每次执行都会生成一个游标,想想不使用绑定变量时每条语句都要经过一次硬解析,对共享池的压力是很大的。
使用绑定变量的不好之处,这里举例说明。
将表t9 中owner列值改为100个为public,其余都为sys,模拟一个owner 数据严重倾斜的例子。
在sqlplus 下执行下例语句
variable owner varchar2(100) execute :owner := 'SYS'; select * from t1 where owner = :owner; execute :owner := 'PUBLIC'; select * from t1 where owner = :owner;
检查共享池 ,看到只生成了一个游标
在共享池中的执行计划如下
select * from table(dbms_xplan.display_cursor('d0g5mw5skptkq',0,'allstats advanced last'));
SQL_ID d0g5mw5skptkq, child number 0 ------------------------------------- select * from t1 where owner = :owner Plan hash value: 3617692013 --------------------------------------------------------------------------- | Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 427 (100)| | |* 1 | TABLE ACCESS FULL| T1 | 90944 | 9M| 427 (1)| 00:00:01 | --------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T1@SEL$1 Peeked Binds (identified by position): -------------------------------------- 1 - :1 (VARCHAR2(30), CSID=178): 'SYS' Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OWNER"=:OWNER)
上述执行计划中,对于值sys,走全表扫描是比较合适的,但是对于public显然不合适,绑定变量窥探的时候使用的是sys,黄色部分所示。使用绑定变量生成的执行计划,如果不考虑实际的数据分布情况,会导致后续的执行盲目的沿用一个不合适的执行计划。绑定变量的这种缺陷往往会导致一些严重的性能问题。
还好11gR2 中开始有自适应游标共享,可以纠正这种情况。
使用提示启用自适应游标共享
variable owner varchar2(100) execute :owner := 'SYS'; select /*+ bind_aware */ * from t1 where owner = :owner; execute :owner := 'PUBLIC'; select /*+ bind_aware */ * from t1 where owner = :owner;
查看共享池有两个游标
查看执行计划
SQL_ID 8squu0a2shjfv, child number 0 ------------------------------------- select /*+ bind_aware */ * from t1 where owner = :owner Plan hash value: 3617692013 --------------------------------------------------------------------------- | Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 427 (100)| | |* 1 | TABLE ACCESS FULL| T1 | 90944 | 9M| 427 (1)| 00:00:01 | --------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T1@SEL$1 Peeked Binds (identified by position): -------------------------------------- 1 - :1 (VARCHAR2(30), CSID=178): 'SYS' Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OWNER"=:OWNER) SQL_ID 8squu0a2shjfv, child number 1 ------------------------------------- select /*+ bind_aware */ * from t1 where owner = :owner Plan hash value: 2024642480 -------------------------------------------------------------------------------------------------- | Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time | -------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 3 (100)| | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| T1 | 100 | 11300 | 3 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX_OWNER | 100 | | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / T1@SEL$1 2 - SEL$1 / T1@SEL$1 Peeked Binds (identified by position): -------------------------------------- 1 - :1 (VARCHAR2(30), CSID=178): 'PUBLIC' Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OWNER"=:OWNER)
可以看到自适应游标共享可以解决由于绑定变量造成执行计划不准确这种情况。