Oracle face "tilt column data using bind variables" solution scenarios

1. Background Information

    We know, Oracle in a traditional OLTP (online transaction processing) class system, it is strongly recommended to use bind variables, which can effectively reduce the hard analysis to increase concurrent processing capability of the system. Even in some older systems, lack of awareness at the beginning of the development phase is not used to bind variables, the late and can not increase the amount of concurrency when the transformation program, operation and maintenance DBA will be forced to set cursor_sharing = force to force the system to use bind variables (this is a program of last resort, not a best practice).

    While using bind variables has brought great benefits to the OLTP system, but also bring some difficult issues, the most typical is because the SQL text contains bind variables, the optimizer can not know the value of the bind variable represented by concrete, only use the default selectable rate, which may lead to inability to accurately determine the value of the optional rate caused by choosing the wrong execution plan. In Oracle 9i era we have a solution to this problem, namely spy bind variables (bind peeking) characteristics. In the case turns on the property, there are encountered when bind variables SQL, in its first hard analysis, the optimizer will snoop in order to accurately determine the true value of the optional rate (selectivity), select the correct final implementation plan. But the characteristics at the same time to introduce another thorny issue, because after the first hard analysis on all soft / soft resolution, and therefore would not pry into real bind variable's value again, and where the field if the value itself value ratio on the uneven distribution, it is likely to cause performance problems (especially if the first value represents the spy rare cases, the problem will be more serious), it has been, although Oracle this feature is enabled by default, but many of the customer's production environment best practices to this feature will be turned off.

    Until the era of Oracle 11g, was launched acs (adaptive_cursor_sharing) characteristics, with the bind peeking considered the true sense solution to this problem. But also not perfect, because acs characteristic itself will indeed be extra hard to resolve, and will lead to an increase in child cursor, so soft parsing scan chain becomes longer, while the shared pool space requirements also increased, and more early bug, even if Oracle default this feature is turned on, a lot of customer's production environment is to be closed.

    In this context, a consulting firm on SQL optimization expert Zhao Yong, is recommended when using bind variables encountered in the data slanted columns should be timely communication and development, whether in such heavily skewed distribution of data columns do not bind variables, if the value of the column a lot, do not bind variables may lead to a lot of hard parse words, but also in the application before issuing SQL, first determine which incoming value, whether it is atypical value, if that is the case, using non-SQL bind variables; if the typical value, the statement bind variables is used.

    If the application can not change the situation it? I can think of is currently sacrificing efficiency or atypical value (the value of the first atypical prevent being spied performance lead to more serious consequences, according to the implementation plan can be bound typical value); or is simply to try and open at the same bind peeking acs characteristics, the actual test validation can solve the problem without causing other performance problems (if these features are already closed production system on or to carefully test the decision).

2. The structure of test cases

    The configuration of a simple Oracle test solution described in this scenario provides (bind peeking + acs):

--建表T_SKEW,构造出严重的数据倾斜:
create table jingyu.t_skew as select * from dba_objects;
create index jingyu.idx_t_skew on jingyu.t_skew(object_id);
update jingyu.t_skew set object_id=3 where object_id>3;
commit;

--查看数据列OBJECT_ID的倾斜程度:
select object_id, count(*) from jingyu.t_skew group by object_id;

 OBJECT_ID   COUNT(*)
---------- ----------
         2          1
         3      86412

--收集统计信息:
exec dbms_stats.gather_table_stats('JINGYU','T_SKEW');

--查看列OBJECT_ID的直方图信息:
select owner, table_name, column_name, histogram from dba_tab_col_statistics where table_name = 'T_SKEW' and column_name = 'OBJECT_ID';
OWNER                                                        Name            Name                      HISTOGRAM
------------------------------------------------------------ --------------- ------------------------- ------------------------------
JINGYU                                                       T_SKEW          OBJECT_ID                 FREQUENCY

    Use MOS: SCRIPT - the script to query information Select to show Optimizer Statistics for CBO (document ID 31412.1) provides:

SQL> @sosi
SQL> set echo off

Please enter Name of Table Owner (Null = SYS): jingyu
Please enter Table Name to show Statistics for: t_skew

***********
Table Level
***********


Table                       Number                 Empty Average    Chain Average Global User               Sample Date
Name                       of Rows   Blocks       Blocks   Space    Count Row Len Stats  Stats                Size MM-DD-YYYY
--------------- ------------------ -------- ------------ ------- -------- ------- ------ ------ ------------------ ----------
T_SKEW                      86,413    1,262            0       0        0      96 YES    NO                 86,413 08-26-2019

Column                    Column                       Distinct          Number     Number Global User               Sample Date
Name                      Details                        Values Density Buckets      Nulls Stats  Stats                Size MM-DD-YYYY
------------------------- ------------------------ ------------ ------- ------- ---------- ------ ------ ------------------ ----------
OWNER                     VARCHAR2(30)                       27       0       1          0 YES    NO                 86,413 08-26-2019
OBJECT_NAME               VARCHAR2(128)                  51,864       0       1          0 YES    NO                 86,413 08-26-2019
SUBOBJECT_NAME            VARCHAR2(30)                       87       0       1     86,152 YES    NO                    261 08-26-2019
OBJECT_ID                 NUMBER(22)                          2       0       2          0 YES    NO                  5,389 08-26-2019
DATA_OBJECT_ID            NUMBER(22)                      8,670       0       1     77,703 YES    NO                  8,710 08-26-2019
OBJECT_TYPE               VARCHAR2(19)                       44       0       1          0 YES    NO                 86,413 08-26-2019
CREATED                   DATE                              904       0       1          0 YES    NO                 86,413 08-26-2019
LAST_DDL_TIME             DATE                              995       0       1          0 YES    NO                 86,413 08-26-2019
TIMESTAMP                 VARCHAR2(19)                    1,036       0       1          0 YES    NO                 86,413 08-26-2019
STATUS                    VARCHAR2(7)                         2       1       1          0 YES    NO                 86,413 08-26-2019
TEMPORARY                 VARCHAR2(1)                         2       1       1          0 YES    NO                 86,413 08-26-2019
GENERATED                 VARCHAR2(1)                         2       1       1          0 YES    NO                 86,413 08-26-2019
SECONDARY                 VARCHAR2(1)                         2       1       1          0 YES    NO                 86,413 08-26-2019
NAMESPACE                 NUMBER(22)                         20       0       1          0 YES    NO                 86,413 08-26-2019
EDITION_NAME              VARCHAR2(30)                        0       0       0     86,413 YES    NO                        08-26-2019

                              B                                            Average     Average
Index                      Tree Leaf       Distinct             Number Leaf Blocks Data Blocks      Cluster Global User               Sample Date
Name            Unique    Level Blks           Keys            of Rows     Per Key     Per Key       Factor Stats  Stats                Size MM-DD-YYYY
--------------- --------- ----- ---- -------------- ------------------ ----------- ----------- ------------ ------ ------ ------------------ ----------
IDX_T_SKEW      NONUNIQUE     1  298              2             86,413         149         617        1,234 YES    NO                 86,413 08-26-2019

Index           Column                     Col Column
Name            Name                       Pos Details
--------------- ------------------------- ---- ------------------------
IDX_T_SKEW      OBJECT_ID                    1 NUMBER(22)

***************
Partition Level
***************

***************
SubPartition Level
***************
SQL> 

3. scenario testing

First make sure bind_peeking and 3.1 acs are open state

--查询隐藏参数:
set linesize 333
col name for a35
col description for a66
col value for a30
SELECT   i.ksppinm name,  
   i.ksppdesc description,  
   CV.ksppstvl VALUE
FROM   sys.x$ksppi i, sys.x$ksppcv CV  
   WHERE   i.inst_id = USERENV ('Instance')  
   AND CV.inst_id = USERENV ('Instance')  
   AND i.indx = CV.indx  
   AND i.ksppinm LIKE '%&param%' 
ORDER BY   REPLACE (i.ksppinm, '_', '');  

--相关隐藏参数的默认值(表示bind_peeking和acs都是开启的):
NAME                                DESCRIPTION                                                        VALUE
----------------------------------- ------------------------------------------------------------------ ------------------------------
_optim_peek_user_binds              enable peeking of user binds                                       TRUE
_optimizer_adaptive_cursor_sharing  optimizer adaptive cursor sharing                                  TRUE
_optimizer_extended_cursor_sharing  optimizer extended cursor sharing                                  UDO
_optimizer_extended_cursor_sharing_ optimizer extended cursor sharing for relational operators         SIMPLE
rel

3.2 scenario test cases and test results

--1)场景测试用例
alter session set current_schema=jingyu;
alter session set statistics_level=all;
set lines 200 pages 200

var v1 number;
exec :v1 := 2;
select count(*) from t_skew where object_id = :v1;
select * from table(dbms_xplan.display_cursor(null,null,'allstats'));

exec :v1 := 3;
select count(*) from t_skew where object_id = :v1;
select * from table(dbms_xplan.display_cursor(null,null,'allstats'));

select count(*) from t_skew where object_id = :v1;
select * from table(dbms_xplan.display_cursor(null,null,'allstats'));

--2)场景测试结果
SQL> alter system flush shared_pool;
SQL> alter session set current_schema=jingyu;
SQL> alter session set statistics_level=all;
SQL> set lines 200 pages 200
SQL> 
--绑定变量值为2,第一次执行,采用INDEX RANGE SCAN的执行计划,Plan hash value: 3167530345:
SQL> var v1 number;
SQL> exec :v1 := 2;
SQL> select count(*) from t_skew where object_id = :v1;

  COUNT(*)
----------
         1
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  7mz2mhz0nq92n, child number 0
-------------------------------------
select count(*) from t_skew where object_id = :v1

Plan hash value: 3167530345

------------------------------------------------------------------------------------------
| Id  | Operation         | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |            |      1 |        |      1 |00:00:00.01 |       2 |
|   1 |  SORT AGGREGATE   |            |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  2 |   INDEX RANGE SCAN| IDX_T_SKEW |      1 |     16 |      1 |00:00:00.01 |       2 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=:V1)

--绑定变量值为3,第一次执行,沿用INDEX RANGE SCAN的执行计划,Plan hash value: 3167530345:
SQL> exec :v1 := 3;
SQL> select count(*) from t_skew where object_id = :v1;

  COUNT(*)
----------
     86412
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  7mz2mhz0nq92n, child number 0
-------------------------------------
select count(*) from t_skew where object_id = :v1

Plan hash value: 3167530345

------------------------------------------------------------------------------------------
| Id  | Operation         | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |            |      2 |        |      2 |00:00:00.10 |     301 |
|   1 |  SORT AGGREGATE   |            |      2 |      1 |      2 |00:00:00.10 |     301 |
|*  2 |   INDEX RANGE SCAN| IDX_T_SKEW |      2 |     16 |  86413 |00:00:00.06 |     301 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=:V1)

--绑定变量值为3,第二次执行,变为INDEX FAST FULL SCAN的执行计划,Plan hash value: 2333720604:
SQL> select count(*) from t_skew where object_id = :v1;

  COUNT(*)
----------
     86412
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  7mz2mhz0nq92n, child number 1
-------------------------------------
select count(*) from t_skew where object_id = :v1

Plan hash value: 2333720604

----------------------------------------------------------------------------------------------
| Id  | Operation             | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |            |      1 |        |      1 |00:00:00.07 |     502 |
|   1 |  SORT AGGREGATE       |            |      1 |      1 |      1 |00:00:00.07 |     502 |
|*  2 |   INDEX FAST FULL SCAN| IDX_T_SKEW |      1 |  86389 |  86412 |00:00:00.04 |     502 |
----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("OBJECT_ID"=:V1)

SQL> 

    It can be seen when the second execution of a bind variable value SQL 3, the adaptive adjustment of the implementation plan.

3.3 scenario testing in-depth analysis

You can use the V$ views for adaptive cursor sharing to see selectivity ranges, cursor information (such as whether a cursor is bind-aware or bind-sensitive), and execution statistics:
V$SQL shows whether a cursor is bind-sensitive or bind-aware
V$SQL_CS_HISTOGRAM shows the distribution of the execution count across a three-bucket execution history histogram
V$SQL_CS_SELECTIVITY shows the selectivity ranges stored for every predicate containing a bind variable if the selectivity was used to check cursor sharing
V$SQL_CS_STATISTICS summarizes the information that the optimizer uses to determine whether to mark a cursor bind-aware.

    See SQL (SQL_ID = '7mz2mhz0nq92n') through v $ sql child_number, executions, buffer_gets, bind-sensitive, bind-aware, is_shareable information:

SQL> SELECT CHILD_NUMBER, EXECUTIONS, BUFFER_GETS, IS_BIND_SENSITIVE AS "BS",
  2         IS_BIND_AWARE AS "BA", IS_SHAREABLE AS "SH", PLAN_HASH_VALUE
  3  FROM   V$SQL
  4  WHERE  SQL_ID = '7mz2mhz0nq92n';

CHILD_NUMBER EXECUTIONS BUFFER_GETS BS BA SH PLAN_HASH_VALUE
------------ ---------- ----------- -- -- -- ---------------
           0          2         348 Y  N  N       3167530345
           1          1         502 Y  Y  Y       2333720604

--再次分别执行绑定变量值为3和2的SQL:
SQL> select count(*) from t_skew where object_id = :v1;

  COUNT(*)
----------
     86412
SQL> exec :v1 := 2;
SQL> select count(*) from t_skew where object_id = :v1;

  COUNT(*)
----------
         1

--再次查询v$sql
CHILD_NUMBER EXECUTIONS BUFFER_GETS BS BA SH PLAN_HASH_VALUE
------------ ---------- ----------- -- -- -- ---------------
           0          2         348 Y  N  N       3167530345
           1          2        1004 Y  Y  Y       2333720604
           2          1           2 Y  Y  Y       3167530345

    You can see the current hang under the SQL 3 child_number the parent cursor (0 and 1 and 2, SH 1 and 2 wherein the value of Y, meant to be shared; SH 0 value N, meaning not shared).

    By v $ sql_cs_ * acs of relevant information:

--V$SQL_CS_HISTOGRAM
SQL> select * from V$SQL_CS_HISTOGRAM where sql_id = '7mz2mhz0nq92n';

ADDRESS          HASH_VALUE SQL_ID                     CHILD_NUMBER  BUCKET_ID      COUNT
---------------- ---------- -------------------------- ------------ ---------- ----------
0000000087F34700 3242927188 7mz2mhz0nq92n                         2          0          1
0000000087F34700 3242927188 7mz2mhz0nq92n                         2          1          0
0000000087F34700 3242927188 7mz2mhz0nq92n                         2          2          0
0000000087F34700 3242927188 7mz2mhz0nq92n                         1          0          0
0000000087F34700 3242927188 7mz2mhz0nq92n                         1          1          2
0000000087F34700 3242927188 7mz2mhz0nq92n                         1          2          0
0000000087F34700 3242927188 7mz2mhz0nq92n                         0          0          1
0000000087F34700 3242927188 7mz2mhz0nq92n                         0          1          1
0000000087F34700 3242927188 7mz2mhz0nq92n                         0          2          0

--V$SQL_CS_SELECTIVITY
SQL> col PREDICATE for a30
SQL> select * from V$SQL_CS_SELECTIVITY where sql_id = '7mz2mhz0nq92n';

ADDRESS          HASH_VALUE SQL_ID                     CHILD_NUMBER PREDICATE                        RANGE_ID LOW                  HIGH
---------------- ---------- -------------------------- ------------ ------------------------------ ---------- -------------------- --------------------
0000000087F34700 3242927188 7mz2mhz0nq92n                         2 =V1                                     0 0.000167             0.000204
0000000087F34700 3242927188 7mz2mhz0nq92n                         1 =V1                                     0 0.899749             1.099694
SQL> 

--V$SQL_CS_STATISTICS 
SQL> select * from V$SQL_CS_STATISTICS where sql_id = '7mz2mhz0nq92n';

ADDRESS          HASH_VALUE SQL_ID                     CHILD_NUMBER BIND_SET_HASH_VALUE PE EXECUTIONS ROWS_PROCESSED BUFFER_GETS   CPU_TIME
---------------- ---------- -------------------------- ------------ ------------------- -- ---------- -------------- ----------- ----------
0000000087F34700 3242927188 7mz2mhz0nq92n                         2          2064090006 Y           1              4           2          0
0000000087F34700 3242927188 7mz2mhz0nq92n                         1          2706503459 Y           1         172826         502          0
0000000087F34700 3242927188 7mz2mhz0nq92n                         0          2064090006 Y           1              4          49          0
SQL>

4. Summary

    Summarize the relevant points of knowledge test:
4.1 bar clean up a SQL execution plan

--查询SQL的ADDRESS和HASH_VALUE
SQL> select sql_id, ADDRESS, HASH_VALUE from v$sqlarea where sql_id = '7mz2mhz0nq92n';

SQL_ID                     ADDRESS          HASH_VALUE
-------------------------- ---------------- ----------
7mz2mhz0nq92n              0000000087F34700 3242927188

--清理SQL的执行计划
SQL> exec sys.DBMS_SHARED_POOL.PURGE('0000000087F34700,3242927188','C');

Close 4.2 bind peeking characteristics and acs

--均为动态参数
--bind peeking(绑定变量窥探)
alter system set "_optim_peek_user_binds"=false;

--acs(adaptive cursor sharing)
alter system set "_optimizer_extended_cursor_sharing_rel"=NONE;
alter system set "_optimizer_extended_cursor_sharing"=NONE;
alter system set "_optimizer_adaptive_cursor_sharing"=false;

    Special Note: If the bind peeking is off, in fact acs will not work, such as I have here only _optim_peek_user_binds parameter set to false, again according to the same experimental procedure was repeated 3.2, query results are as follows, it will not be used acs properties even if I do not show off to disable the corresponding parameter acs:

SQL> SELECT CHILD_NUMBER, EXECUTIONS, BUFFER_GETS, IS_BIND_SENSITIVE AS "BS",
  2         IS_BIND_AWARE AS "BA", IS_SHAREABLE AS "SH", PLAN_HASH_VALUE
  3  FROM   V$SQL
  4  WHERE  SQL_ID = '7mz2mhz0nq92n';

CHILD_NUMBER EXECUTIONS BUFFER_GETS BS BA SH PLAN_HASH_VALUE
------------ ---------- ----------- -- -- -- ---------------
           0          3        1506 N  N  Y       2333720604

--可以看到这3次执行执行计划都是一样的,因为受到OPT_PARAM('_optim_peek_user_binds' 'false')影响,采用了INDEX FAST FULL SCAN的执行计划,Plan hash value: 2333720604:

SQL> select * from table(dbms_xplan.display_cursor('7mz2mhz0nq92n',0,'advanced'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  7mz2mhz0nq92n, child number 0
-------------------------------------
select count(*) from t_skew where object_id = :v1

Plan hash value: 2333720604

------------------------------------------------------------------------------------
| Id  | Operation             | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |            |       |       |    82 (100)|          |
|   1 |  SORT AGGREGATE       |            |     1 |     3 |            |          |
|*  2 |   INDEX FAST FULL SCAN| IDX_T_SKEW | 43207 |   126K|    82   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1
   2 - SEL$1 / T_SKEW@SEL$1

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.4')
      DB_VERSION('11.2.0.4')
      OPT_PARAM('_optim_peek_user_binds' 'false')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      INDEX_FFS(@"SEL$1" "T_SKEW"@"SEL$1" ("T_SKEW"."OBJECT_ID"))
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("OBJECT_ID"=:V1)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - (#keys=0) COUNT(*)[22]

    So when confirming whether to open acs characteristics, but also set the query bind peek of the situation.

Guess you like

Origin www.cnblogs.com/jyzhao/p/11415820.html