Here is a problem query (some columns are hidden to preserve readability):
SQL Plan Monitoring Details (Plan Hash Value=40624586) ================================================================================================================================================ | Id | Operation | Name | Execs | Rows | Read | Read | Activity | Activity Detail | | | | | | (Actual) | Reqs | Bytes | (%) | (# samples) | ================================================================================================================================================ | 0 | SELECT STATEMENT | | 1 | 0 | | | | | | 1 | NESTED LOOPS | | 1 | 0 | | | | | | 2 | NESTED LOOPS | | 1 | 62594 | | | | | | 3 | TABLE ACCESS BY INDEX ROWID BATCHED | MAIN_TABLE | 1 | 62594 | 22777 | 178MB | 25.89 | Cpu (1) | | | | | | | | | | db file parallel read (28) | | 4 | INDEX RANGE SCAN | MAIN_TABLE_I | 1 | 62594 | 25 | 200KB | | | | 5 | INDEX UNIQUE SCAN | CHILD_TABLE_PK | 109K | 62594 | 63798 | 498MB | 71.43 | Cpu (2) | | | | | | | | | | db file parallel read (78) | | 6 | TABLE ACCESS BY INDEX ROWID | CHILD_TABLE | 105K | 0 | 1402 | 11MB | 1.79 | db file parallel read (2) | ================================================================================================================================================You can see that we've obtained 62K rows at step 3, but notice a number of "Execs" at step 5: 109K. It's almost twice as higher than 62K.
Number of rows ("Rows (Actual)") is correct, though.
I checked relevant columns in V$SQL_PLAN_MONITOR view (STARTS/OUTPUT_ROWS) and verified that there's no contradiction between V$SQL_PLAN_MONITOR view and DBMS_SQLTUNE.REPORT_SQL_MONITOR output.
I investigated this issue further and constructed a simple test case which can be used to reproduce the issue.
SQL> create table fact 2 as 3 select date '2015-08-01' + trunc(level/4)/86400 fact_date, 4 lpad('x', 240, 'x') padding, 5 mod(level, 100000) dim_id 6 from dual 7 connect by level<=4*86400; Table created. SQL> SQL> exec dbms_stats.gather_table_stats( '', 'fact') PL/SQL procedure successfully completed. SQL> SQL> create table dim 2 as 3 select trunc(dbms_random.value(1,100000)) id, 4 lpad('x', 340, 'x') padding 5 from dual 6 connect by level<=2*86400; Table created. SQL> SQL> create index dim_i on dim(id); Index created. SQL> SQL> exec dbms_stats.gather_table_stats( '', 'dim') PL/SQL procedure successfully completed.I created 2 tables: FACT and DIM, populated them with data and gathered statistics.
Now, I will flush the buffer cache and execute a test query:
SQL> alter system flush buffer_cache; System altered. SQL> SQL> select /*+ monitor leading(f) use_nl(d) full(f)*/ 2 count(f.padding), 3 count(d.padding) 4 from fact f, 5 dim d 6 where f.fact_date between to_date('01.08.2015 12:00', 'dd.mm.yyyy hh24:mi') and to_date('01.08.2015 12:10', 'dd.mm.yyyy hh24:mi') 7 and d.id = f.dim_id; COUNT(F.PADDING) COUNT(D.PADDING) ---------------- ---------------- 4214 4214 1 row selected. SQL>Let's see the DBMS_SQLTUNE report for the last query:
Ok, we've just reproduced the issue: row source 4 of the plan generated 2404 rows, but the number of "Execs" (2539) at line 5 is equals to 2539, which is slightly greater.
It was not by accident that I flushed the buffer cache before I execute a query.
If I'll execute a query once again, then dbms_sqltune.report_sql_monitor report has number of "Execs" in line 5 equals to number of rows in line 4:
SQL Plan Monitoring Details (Plan Hash Value=85884857) ====================================================================================================================================================== | Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Read | Read | Activity | Activity Detail | | | | | (Estim) | | Active(s) | Active | | (Actual) | Reqs | Bytes | (%) | (# samples) | ====================================================================================================================================================== | 0 | SELECT STATEMENT | | | | 1 | +0 | 1 | 1 | | | | | | 1 | SORT AGGREGATE | | 1 | | 1 | +0 | 1 | 1 | | | | | | 2 | NESTED LOOPS | | | | 1 | +0 | 1 | 4214 | | | | | | 3 | NESTED LOOPS | | 5025 | 11890 | 1 | +0 | 1 | 4214 | | | | | | 4 | TABLE ACCESS FULL | FACT | 2408 | 3535 | 1 | +0 | 1 | 2404 | 108 | 100MB | | | | 5 | INDEX RANGE SCAN | DIM_I | 2 | 1 | 1 | +0 | 2404 | 4214 | | | | | | 6 | TABLE ACCESS BY INDEX ROWID | DIM | 2 | 4 | 1 | +0 | 4214 | 4214 | | | | | ======================================================================================================================================================I believe, that differences in execs/rows were caused by NLJ batching/Table prefetching.
How can we prove it?
First, I'll show how changes "physical reads cache prefetch" before/after query execution.
SQL> select n.name, s.value 2 from v$statname n, v$mystat s 3 where n.name like '%prefetch%' 4 and s.statistic#=n.statistic# 5 and s.value>0 6 order by n.name; NAME VALUE ------------------------------ ---------- physical reads cache prefetch 127987 1 row selected. SQL> SQL> select /*+ monitor leading(f) use_nl(d) full(f)*/ 2 count(f.padding), 3 count(d.padding) 4 from fact f, 5 dim d 6 where f.fact_date between to_date('01.08.2015 12:00', 'dd.mm.yyyy hh24:mi') and to_date('01.08.2015 12:10', 'dd.mm.yyyy hh24:mi') 7 and d.id = f.dim_id; COUNT(F.PADDING) COUNT(D.PADDING) ---------------- ---------------- 4214 4214 1 row selected. SQL> SQL> select n.name, s.value 2 from v$statname n, v$mystat s 3 where n.name like '%prefetch%' 4 and s.statistic#=n.statistic# 5 and s.value>0 6 order by n.name; NAME VALUE ------------------------------ ---------- physical reads cache prefetch 131321 1 row selected.Statistic increased by the 131321-127987=3334 blocks. Second, when I disable nlj_batching, there are no differences in execs/rows:
Or, if we want to obtain "more-classic" NLJ plan shape (I added no_nlj_prefetch(d) hint):
I have searched for a similar issues through MOS and found a Bug 13634445 : V$SQL_PLAN_MONITOR AND NL BATCHING IN 11G, which has status 92 "Closed, Not a Bug".
Another interesting observation is that by adding a "gather_plan_statistics" hint, I'll disable prefetching for that case and fix V$SQL_PLAN_MONITOR "discrepancy"
Комментариев нет:
Отправить комментарий
Примечание. Отправлять комментарии могут только участники этого блога.