A couple of new cool Blogs in the area of Oracle Performance!
Abel Macias and Stelios Charalambides are now blogging! Abel is one of those truly gurus in the area of Oracle performance (both server and SQL Tuning) and Stelios is well versed in Oracle performance and Oracle in general. Both work for Oracle helping out in the Exadata team. And both know SQLTXPLAIN in and out. Abel has been a key contributor in the creation of SQLTXPLAIN and Stelios has written a book on SQLTXPLAIN. Please take some time to browse their new blogs. You may want to bookmark them or subscribe for future references. Please join me to welcome both! I am so happy to see more Oracle professionals opening up to share their treasured knowledge through some blog of their own!
A couple of myths about Plan Stability and CBO Statistics
Every so often I get asked about this two statements:
- Bind Peeking requires Histograms in order to generate multiple Execution Plans.
- Freezing CBO Statistics freezes an Execution Plan.
Both are myths. The true is that you don’t have to have Histograms for a Plan using Bind Peeking to change. And even if you freeze your CBO Statistics your Plan may change.
Range Predicates and Bind Peeking
Consider Range Predicate “COLUMN > :b1“. Where column low/high values are “A” and “Z”. If value of bind :b1 is “A” at the time of a hard parse, the Selectivity of this Predicate is close to 1. If the CBO peeks the value of “Z” the Selectivity is close to zero. A variation of Selectivity like this may change an Execution Plan. Thus: “COLUMN does not require to have a CBO Histogram for Bind Peeking to produce multiple Execution Plans”.
Frozen CBO Statistics and Plan Stability
If your table contains columns based on sequences (like in a Primary Key), or if it contains DATE type columns, your Execution Plan may change even if the CBO Statistics do not change.
- Consider Predicate “N1 = :b1” where N1 is a NUMBER type column based on a sequence with 100 distinct values and low/high values of 1 and 100. Then hard parse this SQL with values of 100 and 200. The first value may result in a Selectivity of 0.01 while the second my be close to zero. That is a significant change that may cause a plan to change.
- Consider Predicate “D1 = :b2” where D1 is a DATE type column with 100 distinct values and low/high of 2013/01/01 and 2013/04/17. Then hard parse this SQL with values of 2013/02/14 and 2013/12/31. Same as the prior case, one value may render a selectivity close to 0.01 while the other close to zero.
The question in both cases above is: WHY would I pass a value out of range? As time progresses, users tend to pass more recent values and since CBO Statistics are frozen, these peeked values become out of low/high range and departing from their low/high end over time. Thus, the Predicate Selectivity diminishes gradually over time. At some point your otherwise stable Execution Plan may suddenly change. Thus: “Freezing CBO Statistics does not freeze Execution Plans”.
Conclusion
Since lack of Histograms or freezing CBO Statistics do not guarantee Plan Stability, do not rely on these two myths. If what you are looking for is Plan Stability use then SQL Plan Management available since 11g or SQL Profiles available from 10g.
IOUG COLLABORATE 13
I will be speaking at the Independent Oracle Users Group (IOUG) “COLLABORATE 13” conference next week (April 8-11, 2013). I will deliver two sessions:
- SQL Tuning made easier with SQLTXPLAIN (SQLT): Monday, April 8 between 3:45 and 4:45 pm. Mile High Ballroom 3A.
- How to improve SQL Performance with new SQL Health Check Tool: Wednesday, April 10 between 1:00 and 2:00 pm. Mile High Ballroom 3A.
This time I will demo SQLT installation and use of the SQLT XTRACT method. For the SQLHC I will demo an actual execution. So, I will spend less time in PPTs and more time showing how to use these tools and how to navigate their output. Feedback from prior events ask for more demo time…
Traveling to Denver in 3 hours (Saturday morning) and returning home (Orlando) on Thursday night. If you are attending this IOUG COLLABORATE 13 event I would love to meet you!
Also, if you have ANY questions about SQLTXPLAIN (SQLT) or SQL Health-Check (SQLHC) keep in mind I am just one email away!
Fixing ORA-19384 from DBMS_SQLTUNE.PACK_STGTAB_SQLSET on SQLT
If you see in the main html SQLTXPLAIN (SQLT) the following error you can execute the commands below connected as SYS:
sqlt$a: *** d:ORA-19384: cannot pack into staging table from previous version
sqlt$a: *** d:DBMS_SQLTUNE.PACK_STGTAB_SQLSET
DROP TABLE sqltxplain.sqli$_stgtab_sqlset; BEGIN SYS.DBMS_SQLTUNE.CREATE_STGTAB_SQLSET ( table_name => 'SQLI$_STGTAB_SQLSET', schema_name => 'SQLTXPLAIN'); END; / DROP TABLE sqltxplain.sqlt$_stgtab_sqlset; BEGIN SYS.DBMS_SQLTUNE.CREATE_STGTAB_SQLSET ( table_name => 'SQLT$_STGTAB_SQLSET', schema_name => 'SQLTXPLAIN'); END; /
SQLTXPLAIN and TRCANLZR use PL/SQL NATIVE compilation mode
Starting on SQLT/TRCA release 11.4.5.4 both tools switched PL/SQL compilation mode of their libraries from INTERPRETED to NATIVE. This change was introduced to reduce overhead in PL/SQL code used by these two tools.
Some customers report that installing SQLT or TRCA they get some PL/SQL errors similar to the ones below. That means those systems are not correctly setup to benefit of the NATIVE compilation mode for PL/SQL. If you get similar errors you have two options: configure your system so it can benefit of this PL/SQL optimization, or revert to use INTERPRETED mode for SQLT/TRCA.
Typical errors observed:
LINE/COL ERROR -------- ----------------------------------------------------------------- 0/0 PLS-00923: native compilation failed: C compilation or linking failed:spdzj.c:Could not invoke program /opt/Prod/SUNW/bin/cc:No such file or directory LINE/COL ERROR -------- ----------------------------------------------------------------- 0/0 PLS-00801: internal error [spdzj.c:child process terminated due to signal:]
If you want to revert to use PL/SQL compilation mode of INTERPRETED on SQLT follow these steps:
- Modify sqlt/install/sqcommon1.sql removing the two dashes “–” at the beginning of this line (27): –DEF plsql_code_type = ‘INTERPRETED’;
- Re-install SQLT by executing sqlt/install/sqcreate.sql
- If problem persists perform a fresh install executing sqlt/install/sqdrop.sql followed by sqlt/install/sqcreate.sql
Be aware that you may have to perform these steps above every time you upgrade your SQLT tool until you address the root cause of your PL/SQL configuration issue.
To perform the same on TRCA look for script trca/install/tacommon1.sql and modify its line 27 removing the two dashes “–” at the beginning of the line, so it becomes: DEF plsql_code_type = ‘INTERPRETED’;. Then proceed to install the tool with trca/install/tacreate.sql as usual.
How a Cursor becomes Bind Aware?
On 11g we have Adaptive Cursor Sharing (ACS) as a mechanism to allow the generation of multiple optimal execution plans for a query. Before ACS we would only have one optimal execution plan in memory for a query given an optimizer environment. There were some exceptions to the rule like having different plans among RAC nodes, or different plans among child cursors when they were not shared. But in essence, before 11g we had one plan in memory for each query.
On ACS we have “bind sensitive” and “bind aware” cursors. The former stage only means “been monitored”, while the latter means: selectivity of predicates during a soft parse determine which optimal plan to use among the ones in memory. So it requires two mechanisms: one to control how to change state from “sensitive” to “aware” and another to select a plan in memory as per the values of the binds.
Becoming Bind Aware
Column v$sql_cs_statistics.rows_processed seems to drive this mechanism. This column is updated during a hard parse and it is a fuzzy representation of the amount of data the query manipulates during its execution (correlated to actual rows in execution plan).
For small values of v$sql_cs_statistics.rows_processed we increment by one v$sql_cs_histogram.bucket_id(0). For medium values we increase by one v$sql_cs_histogram.bucket_id(1). And for large values we do v$sql_cs_histogram.bucket_id(2). Observations on a test case built on 11.2.0.3 seem to indicate that boundaries for these 3 buckets are: less than 1,000; between 1,000 and 1,000,000; and more than 1,000,000.
Cursor Sharing histogram buckets 0-2 are updated on every execution of a bind sensitive query. They represent data volume manipulation of every execution. If there are significant variances then the query would benefit of ACS and it becomes Bind Aware.
I have observed a couple of scenarios where the values stored on these 3 buckets cause the cursor to become bind aware:
- When two of the buckets have the same value, and this value is not zero
- When the smallest and the largest buckets (0 and 2) have a value other than zero
Keep in mind that observations above are not comprehensive and they may change in future releases. But they give you some clues WHY in some cases your query seems to be erratic in regards to ACS. WHY sometimes it stays as bind sensitive and sometimes it becomes bind aware. Basically ACS requires a ramp-up process. If your SQL executes mostly in one of the 3 volume ranges (as per buckets boundaries), it is possible that sometimes it becomes bind aware but not always. In such cases you may want to use the /*+ bind_aware */ CBO Hint. See this Optimizer Blog.
To help you inserting the /*+ bind_aware */ CBO Hint on an application that cannot be easily modified, you may want to use sqlt/utl/coe_gen_sql_patch.sql which is part of SQLTXPLAIN (MOS/Metalink 215187.1). You can also create a SQL Patch as documented in this other Optimizer Blog.
Script sqlt/utl/coe_gen_sql_patch.sql implements a SQL Patch adding by default 3 CBO Hints to your SQL: GATHER_PLAN_STATISTICS, MONITOR and BIND_AWARE. These 3 Hints would help to force “bind aware” status, and would also enable diagnostics on your query. When you execute this script sqlt/utl/coe_gen_sql_patch.sql you can change the CBO Hints you want in your SQL Patch. I recommend you only pass diagnostics Hints and not access path modifiers.
Interesting case where a full table scan is chosen instead of an index
While I was presenting at Hotsos Symposium 2013, I showed a real-life case where the CBO was choosing a full table scan instead of an index range scan. The root cause was a suspicious “NVL(column, value) = value” predicate seen in the execution plan. But the actual SQL contained only “column = value”. So the question became “WHERE the NVL is coming from”? The selection of index or full scan became a simple side effect of the “function(column) = value” predicate.
On 11g there is a new optimization to improve the performance of DDL operations adding columns to an exiting table with data, where the new column is declared as having a constant as default value while making it not nullable. In other words, all existing rows are logically expected to be updated with default value on this new column. If the table is large, this ALTER TABLE ADD COLUMN operation would take long. This optimization records instead just the metadata for this new column (default value and not null constraint) and returns in no time regardless the size of the table.
What was confusing in this real-life case was the fact that filter predicate looked like this “NVL(USER2.SOME_FLAG,’N’)=’Y'” while SOME_FLAG default value was “Y” instead of “N” according to DBA_TAB_COLS. How was that possible? At some point we suspected the column default value had changed from “N” to “Y” but it was a simple speculation. So HOW do we prove or disprove the unexpected “N” in the filter predicate was the product of changing the default value of the affected column?
I created a small test case in order to reproduce observations from the real-life case. I created a table with data on my 11g test database. I created afterwards a new column which would use this new optimization (having a default constant and making it not nullable during same DDL). Last, I modified the default value for same column with another DDL. Keep in mind that when column was created only the metadata was recorded and the actual rows where not updated (this is the optimization).
DROP TABLE t1;
CREATE TABLE t1 AS
WITH v1 AS (SELECT ROWNUM n FROM dual CONNECT BY LEVEL <= 10000)
SELECT ROWNUM id,
DBMS_RANDOM.STRING('U', 40) c1
FROM v1, v1
WHERE ROWNUM <= 10000;
-- rows are not updated. only metadata is recorded. (first DDL)
ALTER TABLE t1 ADD (c2 VARCHAR2(3) DEFAULT 'NO' NOT NULL);
-- data default is recorded. (second DDL)
ALTER TABLE t1 MODIFY (c2 DEFAULT 'YES');
SELECT COUNT(*) FROM t1 WHERE c2 = 'NO';
/*
COUNT(*)
----------
10000 <<<<<<<<<<<<<<<<< expecting 0!
*/
SET PAGES 100 LIN 300;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'PREDICATE'));
/*
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 23 (100)| |
| 1 | SORT AGGREGATE | | 1 | 3 | | |
|* 2 | TABLE ACCESS FULL| T1 | 11342 | 34026 | 23 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(NVL("C2",'NO')='NO') <<<< expecting (NVL("C2",'YES')='NO')!
*/
I was expecting different result and I thought this was a bug. I presented the issue to Mauro Pagano for a peer review and he explained that results were expected (and correct). If we hadn’t had the DDL optimization, at the time of the first ALTER TABLE we would had updated all rows with value “NO”. Then at second DDL we would had updated no rows with value “YES”, but future rows may get that value “YES” if c2 was not explicitly assigned a value. It made sense. And the optimization mimics the same behavior. So, rows that existed before the first DDL have no actual value for “c2” but stored metadata will assign “NO” when accessed. Rows created between DDL 1 and 2 would acquire default value of “NO” if needed (and updated with that “NO” value). And rows created after 2nd DDL would get value “YES” if needed (and updated with that “YES” value). So, only rows before DDL 1 would still have an internal NULL but external “NO”, thanks to initial stored metadata from DDL optimization.
Conclusion
The use of DDL optimization improved the performance of the ALTER TABLE ADD COLUMN command but it introduced a filter predicate which disabled the possible use of a normal index on same column, affecting the performance of queries having such filter predicates. Possible solutions include:
- Adding such columns without the optimization: Add column (DDL), update all rows to default value (DML), add default value to column (DDL), add not null constraint (DDL)
- Issue an ALTER SESSION SET “_add_col_optim_enabled” = FALSE; before the column is created
- Create either an expression with function seen in predicate, or a function-based index
Hotsos Symposium 2013
Today is the 3rd and last day of the Hotsos Symposium 2013. I will stay the extra day so I can attend Maria Colgan’s 1-day class. I am delighted to be here. This annual event is awesome. I will certainly apply to speak next year!
In my opinion, most sessions have been of good quality. Topics are interesting, and having 2 sessions at the same time allows me to pick one out of two instead of many. Of course there are times where I would like to attend the two!
Since this is my second year, I feel like coming back home. I know more people than last year, including other speakers. My kudos to Hotsos for keeping up with the quality of this event.
I would love to see more Oracle attendees, specially Support managers so they can better sense what the Oracle community needs… Anyways, I am happy to see some Oracle faces here… Since many of us work from home these days, participating in events like this helps to reestablish relationships, that would otherwise fade over time.
Yesterday, we paid a visit to Enkitec‘s offices. Veronica showed us the office and we ate some pizza there. The office is modern, stylish and cool. It invites to work! One thing I noticed is the natural synergy among employees. I find this intimate networking of smart guys so important to achieve better results. It encourages constant collaboration and kind of explains me the success of this firm. It brought to my memory my first years at Oracle, when we used to gathered in the aisles and talk about anything, including work! Anyways, it was nice to go and visit…
My to-do list after this Symposium grows:
- I noticed some misconceptions about statistics gathering. I have to chase down some Metalink notes which are causing this trouble…
- Without knowing it, some speakers have created some “enhancement requests” to SQLT, so I have to incorporate some of these soon…
- I need to review some SQL tuning complex cases that some participants want to discuss with me. I will review and provide some advice…
- I expect to receive some emails with enhancements to either the CBO or the database. I will log them to corresponding groups…
- Oracle Support would benefit of some refreshment in “diagnostics collection” for SQL tuning issues. That means another round of internal training. Maybe short remote sessions…
- I have a list of at least half a dozen topics that could be of interest for future conferences. I need to polish this list and draft some abstracts. Then I will review with some OUG’s key players to sense interest.
- I need to purchase (and read) like 3-4 books.
- I need to evaluate the possibility of writing a new tool. It would be a holistic review of CBO statistics (including schema, system, data dictionary and fixed objects). The tool would perform many health-checks and return a set of observations. A natural component could be the dynamic generation of a second script to actually implement those observations. Statistics is still a hot issue and I noticed very diverse opinions. Some are good and some are simply wrong.
- We need to investigate if “STATISTICS_LEVEL to ALL” can be improved in terms of performance in EXADATA.
- I need to learn STRACE and determine its real value for Support. Then review existing Metalink notes and update them accordingly.
- Big data and fussy data mining seem to be hot. Performance of data mining may become a concern… I have to research.
- Method-R and Delphix have excellent products. I need to learn more about them since I expect their adoption by the Oracle community will make them more visible to all of us. I may write a Metalink note on the former so Support can make better sense of it.
- I have to document in this Blog more about the many ramifications of SQLT, specially in the area of Plan Stability and Plan Migration. It seems to be an eternal concern. SQLT provides a lot of help in this area but I think we need better explanations on how to exploit these capabilities.
Ok. Time for a coffee and my daily email race… So far, email is winning by far…
SQLTXPLAIN (SQLT) Show and Tell at Hotsos Symposium
I will have the honor to speak at the Hotsos Symposium 2013 next week. My session name is “Understanding SQLTXPLAIN (SQLT) Main Report by Navigating through Some Samples“. It is scheduled for Tuesday, March 5 at 8:30 am in room A.
I plan to cover my PPT in no more than 15 minutes, so I can dedicate 45 minutes to ad-hoc demo and simultaneous Q&A. SQLT is quite big, so in 45 minutes I cannot cover the full content of the main SQLT report, but I will cover for sure the parts we use more often.
If you have never seen SQLT this is a good opportunity to see what is all the buzz about it. If you know SQLT then you can learn even more, and ask ANY question you may have about it. I would love to see you there! And please, please: ask as many questions as you may have about this tool and how to use it to diagnose SQL statements performing poorly.
About Hotsos:
One more thing: if you are newbie in SQL Tuning or overall Oracle performance, you may want to know more about Hotsos and their annual Symposium. In my opinion, this conference is the best of the best in the area of Oracle performance. I attended for the first time last year (2012) when I was accepted as a speaker. I was in awe all the time! So much knowledge in such a short time! I learned in 4 days more than in any class I have taken in my 17 years at Oracle. I also learned that most speakers have some really valuable nuggets to share with our Oracle community, and most important: speakers are so approachable! So, if you are attending for the first time, I hope you enjoy this symposium as much as I do. If you cannot attend this year, I hope you can attend next year! It is worth every cent and every minute of your time…
How to flush one Cursor out of the Shared Pool
During my 2-days SQL Tuning session at the NoCOUG last week, someone asked me how to flush just one Cursor out of the Shared Pool. We were playing with Adaptive Cursor Sharing (ACS) and between tests we were simply flushing the entire Shared Pool. On 11g there is an API that allows us to flush just one Cursor. I wrote the small script flush_cursor.sql below, and I also placed it inside SQLTXPLAIN (SQLT) under directory sqlt/utl. Feel free to use it and share as needed:
REM Flushes one cursor out of the shared pool. Works on 11g+ REM To create DBMS_SHARED_POOL, run the DBMSPOOL.SQL script. REM The PRVTPOOL.PLB script is automatically executed after DBMSPOOL.SQL runs. REM These scripts are not run by as part of standard database creation. SPO flush_cursor_&&sql_id..txt; PRO *** before flush *** SELECT inst_id, loaded_versions, invalidations, address, hash_value FROM gv$sqlarea WHERE sql_id = '&&sql_id.' ORDER BY 1; SELECT inst_id, child_number, plan_hash_value, executions, is_shareable FROM gv$sql WHERE sql_id = '&&sql_id.' ORDER BY 1, 2; BEGIN FOR i IN (SELECT address, hash_value FROM gv$sqlarea WHERE sql_id = '&&sql_id.') LOOP SYS.DBMS_SHARED_POOL.PURGE(i.address||','||i.hash_value, 'C'); END LOOP; END; / PRO *** after flush *** SELECT inst_id, loaded_versions, invalidations, address, hash_value FROM gv$sqlarea WHERE sql_id = '&&sql_id.' ORDER BY 1; SELECT inst_id, child_number, plan_hash_value, executions, is_shareable FROM gv$sql WHERE sql_id = '&&sql_id.' ORDER BY 1, 2; UNDEF sql_id; SPO OFF;
