Archive for the ‘Parallel Execution Health-Check and Diagnostics Reports’ Category
Counting rows fast
A friend of mine asked me last night basically this: “How is that SQLTXPLAIN counts rows?”. In particular, he was referring to the use of the SAMPLE clause of the SELECT statement. Look at this SQLT’s log piece:
SQL_ID a9x1kc4ymyhkz -------------------- SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e4 FROM "XYPZ"."INSTRUMENT" SAMPLE (.01) t SQL_ID 025v6k1032t69 -------------------- SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e5 FROM "XYPZ"."POSITION_COMPOSITION" SAMPLE (.001) t SQL_ID 8rby3340xpd9k -------------------- SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e5 FROM "XYPZ"."POSITION_EVENT" SAMPLE (.001) t
WHY is it that SQLT has to count rows?
SQLT has to count rows so it can report side by side DBA_TABLES.NUM_ROWS and COUNT(*) from each Table. This is an easy way to see if your statistics are way off, and this mechanism exists on SQLT well before DBA_TAB_MODIFICATIONS came to existence. Actually, SQLT uses both methods to health-check how stale are your Table statistics.
The conundrum here is: “I use SQLT because I want to diagnose a performance issue on a QUERY on top of large Tables, but I do not want SQLT to take a long time just to produce a COUNT(*) of my Tables…”.
Fast versus Precise
In Performance tuning, there is always a trade-off. You want X but you sacrifice B. Counting rows is no different. Do you want it faster? Then you sacrifice precision. The SAMPLE clause of the SELECT statement allows you to do exactly that (syntax below):
SAMPLE [ BLOCK ] ( sample_percent ) [ SEED ( seed_value ) ]
So, if you specify a 10% sample size then you have to multiply the COUNT(*) by 10. If you sample 1% you multiply the COUNT(*) by 100. In large Tables if you sample, lets say 0.1%, your multiplier becomes 1,000, which is the same than 1e3 (10**3 or 10^3 depending where you went to school). Sample size can be as small as 0.000,001 and as large as 100 (but without including 100 itself). It represents probabilities more than an actual sample size.
The optional BLOCK clause simple says: use sample blocks instead of rows. And the optional SEED clause tries to provide some consistency in the result of the count when you use the same value for two executions of the exact same count. This SEED clause takes a value between 0 and 4,294,967,295.
How SQLT counts rows?
SQLT has over 40 tool parameters. One of them is count_star_threshold with a seeded value of 10,000.
SQLT includes a small algorithm (below) that determines the size of the SAMPLE according to the estimated size of the Table itself, by looking at its statistics as per DBA_TABLES.NUM_ROWS. No statistics? then skip the sample and do a normal full scan. If the Table is expected to be smaller then the count_star_threshold, then do a full scan. So is up to 10x this threshold. After that, use a sample size proportionally inverse to the Table size. The bigger the Table the smaller the Sample.
SQLT also forces a full Table scan and invokes Parallel Execution (PX) as a method to expedite the count. This count can be really fast on Exadata systems as you can imagine.
/* ------------------------- * * private perform_count_star * * called by: sqlt$i.common_calls and sqlt$i.remote_xtract * * ------------------------- */ PROCEDURE perform_count_star (p_statement_id IN NUMBER) IS l_sql VARCHAR2(32767); l_number NUMBER; l_count NUMBER; BEGIN write_log('=> perform_count_star'); IF sqlt$a.get_param_n('count_star_threshold') = 0 THEN write_log('skip "count_star" as per corresponding parameter'); ELSE FOR i IN (SELECT owner, table_name, num_rows, source FROM &&tool_administer_schema..sqlt$_dba_all_tables_v WHERE statement_id = p_statement_id ORDER BY owner, table_name) LOOP IF i.num_rows IS NULL THEN l_sql := 'SELECT /*+ FULL(t) PARALLEL */ COUNT(*) FROM "'||i.owner||'"."'||i.table_name||'" t WHERE ROWNUM <= :number'; l_number := sqlt$a.get_param_n('count_star_threshold'); ELSIF i.num_rows < sqlt$a.get_param_n('count_star_threshold') THEN l_sql := 'SELECT /*+ FULL(t) PARALLEL */ COUNT(*) FROM "'||i.owner||'"."'||i.table_name||'" t WHERE ROWNUM <= :number'; l_number := sqlt$a.get_param_n('count_star_threshold') * 10; ELSIF i.num_rows < (sqlt$a.get_param_n('count_star_threshold') * 1e1) THEN l_sql := 'SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e1 FROM "'||i.owner||'"."'||i.table_name||'" SAMPLE (:number) t'; l_number := 1e1; ELSIF i.num_rows < (sqlt$a.get_param_n('count_star_threshold') * 1e2) THEN l_sql := 'SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e2 FROM "'||i.owner||'"."'||i.table_name||'" SAMPLE (:number) t'; l_number := 1e0; ELSIF i.num_rows < (sqlt$a.get_param_n('count_star_threshold') * 1e3) THEN l_sql := 'SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e3 FROM "'||i.owner||'"."'||i.table_name||'" SAMPLE (:number) t'; l_number := 1/1e1; ELSIF i.num_rows < (sqlt$a.get_param_n('count_star_threshold') * 1e4) THEN l_sql := 'SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e4 FROM "'||i.owner||'"."'||i.table_name||'" SAMPLE (:number) t'; l_number := 1/1e2; ELSIF i.num_rows < (sqlt$a.get_param_n('count_star_threshold') * 1e5) THEN l_sql := 'SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e5 FROM "'||i.owner||'"."'||i.table_name||'" SAMPLE (:number) t'; l_number := 1/1e3; ELSIF i.num_rows < (sqlt$a.get_param_n('count_star_threshold') * 1e6) THEN l_sql := 'SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e6 FROM "'||i.owner||'"."'||i.table_name||'" SAMPLE (:number) t'; l_number := 1/1e4; ELSIF i.num_rows < (sqlt$a.get_param_n('count_star_threshold') * 1e7) THEN l_sql := 'SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e7 FROM "'||i.owner||'"."'||i.table_name||'" SAMPLE (:number) t'; l_number := 1/1e5; ELSE l_sql := 'SELECT /*+ FULL(t) PARALLEL */ COUNT(*) * 1e8 FROM "'||i.owner||'"."'||i.table_name||'" SAMPLE (:number) t'; l_number := 1/1e6; END IF; l_sql := REPLACE(l_sql, ':number', l_number); write_log('num_rows='||i.num_rows||' sql='||l_sql); l_count := NULL; BEGIN EXECUTE IMMEDIATE l_sql INTO l_count; write_log(l_count||' rows counted'); EXCEPTION WHEN OTHERS THEN write_log('** '||SQLERRM); write_log(l_sql||' failed with error above. Process continues.'); END; IF l_count IS NOT NULL THEN IF i.source = 'DBA_TABLES' THEN UPDATE &&tool_repository_schema..sqlt$_dba_tables SET count_star = l_count WHERE statement_id = p_statement_id AND owner = i.owner AND table_name = i.table_name; ELSIF i.source = 'DBA_OBJECT_TABLES' THEN UPDATE &&tool_repository_schema..sqlt$_dba_object_tables SET count_star = l_count WHERE statement_id = p_statement_id AND owner = i.owner AND table_name = i.table_name; END IF; END IF; END LOOP; COMMIT; END IF; write_log('<= perform_count_star'); END perform_count_star;
Conclusion
Counting rows is like counting beans, you can count one at a time, or you can take some shortcuts. If you are willing to sacrifice some precision for the sake of gaining performance, consider then using the SAMPLE clause of the SELECT statement.
SQL Tuning with SQLTXPLAIN 2-days Workshop
SQLTXPLAIN is a SQL Tuning tool widely used by the Oracle community. Available through My Oracle Support (MOS) under document 215187.1, this free tool is available for download and use to anyone with MOS access. It has helped thousands of times to expedite the resolution of SQL Tuning issues, and many Oracle DBAs and Developers benefit of its use on a daily basis.
Stelios Charalambides has done an excellent job writing a book on this topic. In his book Stelios covers many aspects about SQLTXPLAIN and some related topics. I highly recommend to get a copy of this book if you want to learn more about SQLTXPLAIN. It is available at Amazon and many other retailers.
The new 2-days SQLTXPLAIN Workshop offered by Enkitec (an Oracle Platinum business partner and my employer) is a completely new course that interleaves “how to use effectively SQLTXPLAIN” with important and related SQL Tuning Topics such as Plan Flexibility and Plan Stability. This hands-on workshop offers participants the unique opportunity to fully understand the contents of SQLTXPLAIN and its vast output through an interactive session. About half the time is dedicated to short guided labs, while the other half uses presentations and demos. This workshop is packed with lots of content. It was a real challenge packaging so much info in only two days, but I am very pleased with the result. It became a 2-days intensive knowledge transfer hands-on workshop on SQLTXPLAIN and SQL Tuning!
The first session of this workshop is scheduled for November 7-8 in Dallas, Texas. I expect this pilot session to fill out fast. Other sessions and onsite ones will be offered during 2014. I hope to meet many of you face to face on November 7!
What is new on SQLTXPLAIN 11.4.4.7 released on July 2?
I uploaded a new release of SQLTXPLAIN (SQLT) today. The 20 changes for this 11.4.4.7 release are listed below, but I’d rather explain what is important and what is not.
Long ago my friend Lester Gutierrez suggested me an interesting addition to SQLT. In those days he was managing an EBS performance development team, now he does the same for Fusion performance. Anyways, he mentioned it would be nice to include with SQLT XTRACT some relevant AWR reports. In particular, at the time when the SQL had performed poorly. The idea here is this: since SQLT already presents lots of details about the SQL and its environment, some broader diagnostics at the time the SQL had poor execution times would help to better understand the system in terms of load and concurrency. This new feature is one of the 20 changes below. SQLT now includes up to 6 AWR reports (6 is a default value for a new configuration parameter). These 6 reports correspond to the periods between two consecutive AWR snapshots where the SQL has had the largest delta elapsed time, across all nodes in case of RAC.
So, why limit this functionality to AWR? On this new release of SQLT, besides the new set of relevant AWR reports, SQLT also produces up to 6 ADDM reports, some ASH reports from memory and from AWR (one set per instance), up to 12 SQL Monitor Active reports, and one SQL Detail report. All driven by the SQL being analyzed. Prior releases included only one SQL Monitor Active report and under some special cases one SQL Detail report. So this 11.4.4.7 is more aggressive in terms of including reports that sometimes are simply unknown or under-utilized. I am setting the defaults for the parameters controlling the number of these reports to some small values, at least on 11.4.4.7. I will monitor their performance as I get new cases, and I may adjust these default values on subsequent releases. In terms of SQLT performance, the impact of these new features seems marginal.
Another relevant change is an addition of a new health-check recently requested by Abel Macias. It detects bug 14013094, which in short it causes Index Partition statistics to go wrong. This is when the order of partition names on an index do not match their table at index creation time. I have already seen a couple of cases and I feel more will come. This new health-check is also included into the new SQL Health-Check script (SQLHC). So if the SQL being analyzed may suffer from this bug, SQLT and SQLHC will create an “Observation”, which are basically the outcome of the dozens of health-checks performed around the SQL.
That brings another important change. When for some reason or another it is hard to install SQLT on a system, I am recommending to use SQLHC. The former requires to install a schema SQLTXPLAIN and hundreds of objects on it. The latter requires no installation at all. The gap of the diagnostics generated is brutal: SQLT provides all we need to diagnose a SQL performing poorly, while SQLHC provides only some pieces. But these pieces have been carefully selected. During several interactions with a key developer from the Fusion performance team, we agreed to add some important new sections to SQLHC. So in this release of SQLHC (also 11.4.4.7), this script has several new features. It will never compete with SQLT, but now it provides a good starting point to a SQL tuning case. Think SQLHC as a lite version of SQLT. By the way, SQLHC had its own note 1366133.1 on My Oracle Support (MOS), but it is also packaged within SQLT under sqlt/utl.
Parallel Execution new tool (MOS 1460440.1) did not escape a few changes in this 11.4.4.7 release. Mostly minor bugs. Same about profiler.sql (243755.1), which produces a report for PL/SQL Profiler data. Both tools are also packaged inside SQLT under sqlt/utl. This way it is easier to access them. If you are not familiar with them, you may want to go to their own MOS notes and look for the output sample.
I would say the other changes not mentioned explicitly, are less relevant. Anyways, try always to use the latest version of SQLT out of 215187.1. If you are a seasoned user of SQLT you know it changes often, always with some new features and some fixes. I hope you start using 11.4.4.7 soon!
- BUG: Readme report for PeopleSoft was showing “Gathering Statistics” twice.
- BUG: Parameter “_db_file_optimizer_read_count” was showing an incorrect value on Main report.
- BUG: Number of executions above Execution Plans was showing zero when statistics_level was not set to ALL at the time the cursor was parsed.
- BUG: Avoid error below when DBMS_METADATA is not granted to PUBLIC. 1452/29 PLS-00201: identifier ‘DBMS_METADATA’ must be declared
- ENH: New HC to detect bug 14013094 on statistics gathering for partitioned tables and indexes where partition names are created in different order between table and index.
- ENH: New tool parameter tcb_export_data to control value of parameter exportData on API DBMS_SQLDIAG.EXPORT_SQL_TESTCASE.
- ENH: Handle non-stopper error: ORA-06550: line 4, column 18 with “FROM applsys.fnd_product_groups”.
- ENH: Scripts roxtract and roxecute are now deprecated. Use sqlhc.sql and sqlhcxec.sql instead.
- ENH: Main report, sqlhc.sql and sqlhcxec.sql include now Plan Line and Plan summaries for Active Session History from memory and AWR.
- ENH: Scripts sqlhc.sql and sqlhcxec.sql to include ASH summaries, Cursor Sharing, set of SQL Monitor reports, SQL Detail report, SQL Profiles, SQL Plan Baseline and SQL Patches.
- ENH: Readme for PSFT includes now instructions to gather statistics using both PSCBO_STATS and DBMS_STATS.
- ENH: SQL Text now shows in red these keywords: SELECT, INSERT, UPDATE, DELETE, MERGE, FROM, WHERE.
- ENH: COUNT(*) on tables accessed by SQL being analyzed is reduced to a sample size 1% of current values. This will make this COUNT less accurate but much faster.
- ENH: SQLT main methods produce now up to 6 AWR reports for snaphots where the SQL being analyzed produced considerable elapsed time.
- ENH: SQLT main methods produce now up to 12 most recent SQL Monitor reports for the SQL being analyzed.
- ENH: SQLT main methods produce now ASH reports from memory and from AWR.
- ENH: SQLT main methods produce now up to 6 ADDM reports for snaphots where the SQL being analyzed produced considerable elapsed time.
- ENH: Workaround: disconnect ORA-07445 on SYS.DBMS_SQLTUNE_INTERNAL, which happened in some 11.2 systems.
- ENH: SQL Test Case (TC) now provides a script tc_pkg.sql to generate a stand-alone TC out of an implemented SQLT TC.
- ENH: All main methods record now into sqltxhost.log a set of 5 vmstat and sar samples.
SQLTXPLAIN (SQLT) 11.4.4.6 is now available
So what is new in SQLT 11.4.4.6? Honestly, not much: one fix and 6 enhancements:
- BUG: SQLT TC “restore” was allowing a column to be renamed from “a” to “b” then from “b” to “c”, so it was ending up with duplicate rows in histogram among other issues.
- ENH: New tool parameter to control the automatic generation of a script with a Custom SQL Profile for best performing plan. Custom SQL Profiles based on DBMS_SQLTUNE.IMPORT_SQL_PROFILE are now inactive by default.
- ENH: New HC detecting corrupt histograms as per bugs 8543770, 10267075, 12819221 and 12876988.
- ENH: New HC reporting Tables where there are columns referenced in predicates and for which the number of distinct values has changed more than 10% from one statistics version to the next. These abrupt changes can cause an execution plan to also change.
- ENH: New HC about CBO not using partition level histograms if stats were gathered with “INCREMENTAL” and fix for bug 13583722 has been applied.
- ENH: Scripts roxtract.sql and roxecute.sql include now System Parameters with non-default or with modified values. They also report on all initialization paramaters for the connecting instance.
- ENH: All SQLT main methods ask now for the SQLTXPLAIN password in-line instead of using an ACCEPT command.
What is new, is that 3 satellite tools got also a new version. They can be downloaded from their own MOS note. They are also packaged within SQLT under directory sqlt/utl: