最近team 讨论 Bind Graduation 比较激烈,详细可以见上一篇文章,基于Bind Graduation 做了以下测试:
主要目的:
测试基于OCI JDBC 等接口的 Bind Graduation行为.针对目前Bind Graduation的行为,以及11.2.0.3出现的purge问题,由于bind graduation导致的child cursor过多问题,暂时没有好的solution(_cursor_obsolete_threshold ?).
建议对问题语句涉及到的表做水平拆分。
测试版本11.2.0.3
[oracle@testdb ~]$ ora si SQL*Plus: Release 11.2.0.3.0 Production on Wed Nov 28 16:39:30 2012 Copyright (c) 1982, 2011, Oracle. All rights reserved. Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production With the Partitioning, OLAP, Data Mining and Real Application Testing options SQL> SQL> show user USER is "SYS" SQL> alter system flush shared_pool; System altered.
1. sqlplus OCI
SQL> VARIABLE n NUMBER
VARIABLE v VARCHAR2(32)
EXECUTE :n := 1; :v := 'Helicon';
INSERT INTO t (n, v) VALUES (:n, :v);SQL> SQL> SQL>
PL/SQL procedure successfully completed.
SQL> SQL>
1 row created.
SQL> commit;
Commit complete.
SQL> SELECT sql_id, child_number, executions
FROM v$sql
WHERE sql_text = 'INSERT INTO t (n, v) VALUES (:n, :v)'; 2 3
SQL_ID CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
6cvmu7dwnvxwj 0 1
SQL> VARIABLE v VARCHAR2(33)
EXECUTE :n := 4; :v := 'Terminus';
INSERT INTO t (n, v) VALUES (:n, :v);SQL> SQL>
PL/SQL procedure successfully completed.
SQL> SQL>
1 row created.
SQL> commit;
Commit complete.
SQL> VARIABLE v VARCHAR2(129)
EXECUTE :n := 4; :v := 'Terminus';
INSERT INTO t (n, v) VALUES (:n, :v);SQL> SQL>
PL/SQL procedure successfully completed.
SQL> SQL>
1 row created.
SQL> commit;
Commit complete.
SQL> /
SQL_ID CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
6cvmu7dwnvxwj 0 2
6cvmu7dwnvxwj 1 1 -----------------129 产生第一个child cursor
SQL> SELECT s.child_number, m.position, m.max_length,
decode(m.datatype,1,'VARCHAR2',2,'NUMBER',m.datatype) aS datatype
FROM v$sql s, v$sql_bind_metadata m
WHERE s.sql_id = '&sql_id'
AND s.child_address = m.address
ORDER BY 1, 2; 2 3 4 5 6
Enter value for sql_id: 6cvmu7dwnvxwj
old 4: WHERE s.sql_id = '&sql_id'
new 4: WHERE s.sql_id = '6cvmu7dwnvxwj'
CHILD_NUMBER POSITION MAX_LENGTH DATATYPE
------------ ---------- ---------- ----------------------------------------
0 1 22 NUMBER
0 2 128 VARCHAR2
1 1 22 NUMBER
1 2 2000 VARCHAR2
丢失了 32这个区间
2 OCI pl/sqldeveloper 操作
SQL> VARIABLE n NUMBER
SQL> VARIABLE v VARCHAR2(32)
SQL> EXECUTE :n := 1; :v := 'Helicon';
PL/SQL procedure successfully completed
n
---------
1
v
---------
Helicon
SQL> INSERT INTO t (n, v) VALUES (:n, :v);
1 row inserted
n
---------
1
v
---------
Helicon
SQL> commit;
Commit complete
SQL> SELECT sql_id,child_number, executions
FROM v$sql
WHERE sql_text = ' INSERT INTO t (n, v) VALUES (:n, :v) '; 2 3
SQL_ID CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
fp1vwg5jfpk4b 0 1
SQL> VARIABLE v VARCHAR2(33)
SQL> EXECUTE :n := 4; :v := 'Terminus';
PL/SQL procedure successfully completed
n
---------
4
v
---------
Terminus
SQL> INSERT INTO t (n, v) VALUES (:n, :v);
1 row inserted
n
---------
4
v
---------
Terminus
SQL> commit;
Commit complete
SQL> /
SQL_ID CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
fp1vwg5jfpk4b 0 2
SQL> VARIABLE v VARCHAR2(129)
SQL> EXECUTE :n := 4; :v := 'Terminus';
PL/SQL procedure successfully completed
n
---------
4
v
---------
Terminus
SQL> INSERT INTO t (n, v) VALUES (:n, :v);
1 row inserted
n
---------
4
v
---------
Terminus
SQL>
SQL> commit;
Commit complete
SQL> /
SQL_ID CHILD_NUMBER EXECUTIONS
------------- ------------ ----------
fp1vwg5jfpk4b 0 3 -------------------没有产生child cursor
SQL> SELECT s.child_number, m.position, m.max_length,
decode(m.datatype,1,'VARCHAR2',2,'NUMBER',m.datatype) aS datatype
FROM v$sql s, v$sql_bind_metadata m
WHERE s.sql_id = '&sql_id'
AND s.child_address = m.address
ORDER BY 1, 2; 2 3 4 5 6
Enter value for sql_id: fp1vwg5jfpk4b
old 4: WHERE s.sql_id = '&sql_id'
new 4: WHERE s.sql_id = 'fp1vwg5jfpk4b'
CHILD_NUMBER POSITION MAX_LENGTH DATATYPE
------------ ---------- ---------- ----------------------------------------
0 1 22 NUMBER
0 2 4000 VARCHAR2
默认产生了4000的max值区间。
这个测试不具备任何意义,经过确认pl/sql developer经过了封装,导致oracle 端默认为4000的max区间。
3. OCI JAVA -(模拟真实环境)
代码如下:
oracle_conn = DriverManager.getConnection("jdbc:oracle:oci:@xxx", "xxx", "xxx");
oracle_stmt = oracle_conn.prepareStatement("insert into t values(?, ?)");
oracle_stmt.setInt(1, 1);
oracle_stmt.setString(2, "Helicon");
oracle_stmt.execute();
oracle_stmt.setInt(1, 2);
oracle_stmt.setString(2, "Helicon33333333333333333333333333333");
oracle_stmt.execute();
oracle_stmt.setInt(1, 3);
oracle_stmt.setString(2, "Helicon3333333333333333333333333333323sssssssssssssssssssssssssssssssssssssssssssssssssssssssddddddddddddddddddddddddddddddddddssssssssssssssssssssssss");
oracle_stmt.execute();
SQL> SELECT s.child_number, m.position, m.max_length,
2 decode(m.datatype,1,'VARCHAR2',2,'NUMBER',m.datatype) aS datatype
3 FROM v$sql s, v$sql_bind_metadata m
4 WHERE s.sql_id = 'dw481sdb5fkkt'
5 AND s.child_address = m.address
6 ORDER BY 1, 2;
CHILD_NUMBER POSITION MAX_LENGTH DATATYPE
------------ ---------- ---------- ----------------------------------------
0 1 22 NUMBER
0 2 32 VARCHAR2
1 1 22 NUMBER
1 2 2000 VARCHAR2
SQL_TEXT SQL_ID EXECUTIONS LOADS FIRST_LOAD_TIME
insert into t values(:1, :2) dw481sdb5fkkt 1 1 2012-11-28/20:30:05
insert into t values(:1, :2) dw481sdb5fkkt 2 1 2012-11-28/20:30:05
产生了32,2000的区间, 但是缺少了128的区间。
4 JDBC JAVA (目前使用的场景)
代码如下:
package com;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import oracle.jdbc.driver.*;
public class test {
public static void main(String[] args) {
Connection oracle_conn = null;
PreparedStatement oracle_stmt = null;
ResultSet oracle_rs = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
oracle.jdbc.driver.OracleDriver a;
oracle_conn = DriverManager.getConnection("jdbc:oracle:thin:@xx.xx.xx.xx:xx:yy", "xx", "xx");
oracle_stmt = oracle_conn.prepareStatement("insert into t values(?, ?)");
oracle_stmt.setInt(1, 1);
oracle_stmt.setString(2, "Helicon");
oracle_stmt.execute();
oracle_stmt.setInt(1, 2);
oracle_stmt.setString(2, "Helicon33333333333333333333333333333");
oracle_stmt.execute();
oracle_stmt.setInt(1, 3);
oracle_stmt.setString(2, "Helicon3333333333333333333333333333323sssssssssssssssssssssssssssssssssssssssssssssssssssssssddddddddddddddddddddddddddddddddddssssssssssssssssssssssss");
oracle_stmt.execute();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(oracle_rs != null) {
oracle_rs.close();
oracle_rs = null;
}
if(oracle_stmt != null) {
oracle_stmt.close();
oracle_stmt = null;
}
if(oracle_conn != null) {
oracle_conn.close();
oracle_conn = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
SQL> SELECT s.child_number, m.position, m.max_length,
2 decode(m.datatype,1,'VARCHAR2',2,'NUMBER',m.datatype) aS datatype
3 FROM v$sql s, v$sql_bind_metadata m
4 WHERE s.sql_id = 'fw60v89km14c9'
5 AND s.child_address = m.address
6 ORDER BY 1, 2;
CHILD_NUMBER POSITION MAX_LENGTH DATATYPE
------------ ---------- ---------- ----------------------------------------
0 1 22 NUMBER
0 2 32 VARCHAR2
1 1 22 NUMBER
1 2 128 VARCHAR2
2 1 22 NUMBER
2 2 2000 VARCHAR2
SQL_TEXT SQL_ID EXECUTIONS LOADS FIRST_LOAD_TIME
insert into t values(:1, :2) fw60v89km14c9 1 1 2012-11-28/16:16:46
insert into t values(:1, :2) fw60v89km14c9 2 1 2012-11-28/16:16:46
insert into t values(:1, :2) fw60v89km14c9 3 1 2012-11-28/16:16:46
JDBC 行为正常 32 128 2000的区间符合默认行为。
总结:
1. pl/sql developer 测试不具备任何价值。(各位同学也不要基于这个去测试了)
2. OCI sqlplus 缺少32区间, JAVA缺少 128区间 这个问题比较疑惑。
3. JDBC 目前正常
Bind Graduation oracle的本意是为了更详细的区分cursor,多次peeking 达到最佳的执行计划。但是对于一些设计很烂的表,将会出现child cursor暴增的可能
Reference:Oracle Call Interface