er
-
Upload
tamella-paulauskiene -
Category
Documents
-
view
117 -
download
15
Transcript of er
ASE 15 Query Tuning
MAY 2011
JEFF TALLMAN
SENIOR SOFTWARE ENGINEER/ARCHITECT
The 4 Stages of Query TuningThe 4 Stages of Query Tuning
1. Finding Query with Issues
2. Analyzing QP Optimization
3. Manually Tuning Query
4. Deploying the Fix
2 – © 2011 Sybase, Inc – May 3, 2011
Today’s Session will focus on the primarily on the first two
with a look at how to use a couple of ASE 15’s new features
for the third topic.
Finding Queries With Issues
3 – Sybase Confidential – May 3, 2011
Tools For Finding Query IssuesTools For Finding Query Issues
• QP Metrics (aka sysquerymetrics)
� good for adhoc queries
� Basis for QP Tune utility
� Filter to focus in on just slow queries
� Use a login trigger to limit to just a representative number of users
• MDA
� Good for stored procs, statement cache, batch processes
4 – © 2011 Sybase, Inc – May 3, 2011
� Good for stored procs, statement cache, batch processes� monSysStatement for stored procs & batch processes� monCachedStatement for statement cache
• App Tracing
� Good for interactively capturing query syntax and diagnostics
� Due to potential output volume, this should be used only for narrow windows of time
• This part of today’s session will focus on QP Metrics
� With some look at App Tracing as this ties into QP optimization
QP Metrics aka sysquerymetricsQP Metrics aka sysquerymetrics
• Advantages over MDA
� No “loss” due to over-running the MDA pipe size
� Very easy to implement
� Easy to identify statements across sessions (due to hashkey)
� Ability to limit collection to just one or a few sessions (vs. everyone)
• Comments/Gotcha’s
� Can consume a lot of disk space in system segment
� Can impact query execution speed
5 – © 2011 Sybase, Inc – May 3, 2011
� Can impact query execution speed� There is a buffer of metrics (similar to audit queue)� But if this gets full, each new captured query will need to wait until buffer is destaged
…slows query exec� Enabling statement cache/literal autoparam can help
� Restricted to each database individually � <database>..sysquerymetrics is view on <database>..sysqueryplans
� Best with ad-hoc queries� Not very good with stored procedures or cached statements unless they are compiled
after QP metrics enabled� …and only good at finding queries with issues - not runaway processes in tight loop
� Tip: Use MDA/monSysStatement as a starting point� If it points to a bad ad-hoc query as a likely cause - use QP metrics to find the query
What Really Is QP Metrics??What Really Is QP Metrics??
• A repository of queries and their execution metrics
� Captures the query executions and metrics based on configurable
thresholds (e.g. only capture queries that run longer than 5 seconds)
� Stores the query text and metrics in system tables
� Exposes the results via a view in each database
• A utility stored procedure
6 – © 2011 Sybase, Inc – May 3, 2011
• A utility stored procedure
� Allows sets of metrics to be backed up for later comparison
� Allows further filtering of captured metrics
• Can be enabled server level or session level
� Server level via sp_configure
� Session level via ‘set metrics_capture’
Query Plan Metrics SyntaxQuery Plan Metrics Syntax
• SyntaxServer - sp_configure “enable metrics capture”, 1
Session - set metrics_capture on/off
sp_metrics [flush | backup | drop | help], @arg1 [, @arg2]
• Accessing
7 – © 2011 Sybase, Inc – May 3, 2011
• Accessing
� QP metrics are captured in default “running” group� gid = 1
� Move to different group with sp_metrics ‘backup’� More on sp_metrics on later slides
� View with sysquerymetrics view� Each db has it’s own sysquerymetrics
– Built on <db>..sysqueryplans
sysquerymetrics view sysquerymetrics view -- ColumnsColumns
Field Definition
uid User IDgid Group IDhashkey The hashkey over the SQL query textid Unique IDsequence Sequence number for a row when multiple rows are
required for the SQL text exec_min Minimum execution timeexec_max Maximum execution timeexec_avg Average execution time
Sorts gone amok
8 – © 2011 Sybase, Inc – May 3, 2011
exec_avg Average execution timeelap_min Minimum elapsed timeelap_max Maximum elapsed timeelap_avg Average elapsed timelio_min Minimum logical IOlio_max Maximum logical IOlio_avg Average logical IOpio_min Minumum physical IOpio_max Maximum physical IOpio_avg Average physical IOcnt Number of times the query has been executed.abort_cnt Number of times a query was aborted by Resource
Governor as a resource limit was exceeded.qtext query text
Bad QP or QEXEC
How to Limit What is Captured (3 Ways)How to Limit What is Captured (3 Ways)
• Use sp_configure to set thresholds
� enable metrics capture = DEFAULT (1)
� metrics lio max = DEFAULT
� metrics pio max = DEFAULT
� metrics elap max = 2000
� metrics exec max = DEFAULT (1000)
• Use login trigger with ‘set metrics_capture’
� If number of users and query volume would be too high, in addition to the
9 – © 2011 Sybase, Inc – May 3, 2011
� If number of users and query volume would be too high, in addition to the filters, you should consider using a login trigger and only enable metrics capture on some number sessions
� Use sysprocesses attributes such as application name or mod of SPID as mechanism to know which users to limit the collection to.
• Use multiple narrow capture windows
� Run consecutive 10 or 15 minute metrics capture
� Filter results using sp_metrics ‘filter’
� Backup results to a new group # using sp_metrics ‘backup’
� Start next capture period
sp_metrics Commandssp_metrics Commands
• help ���� sp_metrics ‘help’
� get help on a command
• flush ���� sp_metrics ‘flush’
� flush all metrics from memory to system tables
� What memory? Proc cache of course!!!
• backup ���� sp_metrics ‘backup’, ‘6’
� move the metrics in the default group to a backup group
� @arg1 is an int in char form representing group #
10 – © 2011 Sybase, Inc – May 3, 2011
� @arg1 is an int in char form representing group #
� Must be higher than 1 (1 is always current group)
• drop ���� sp_metrics ‘drop’, ‘2’
� drop a metric ID or a group of metrics
� @arg1 is an int in char form representing the first metrics group to drop in the range (must be a valid group)
• Filter ���� sp_metrics ‘filter’, ‘1’, ‘lio_max < 100’
� @arg1 is an int in char form representing the group #
� @arg2 is the predicate to apply
After the Collection….Use sp_metricsAfter the Collection….Use sp_metrics
• ‘flush’ to flush all the stats to disk
• ‘backup’ to a new group
� to keep around for regression testing
• ‘filter’ to remove noise
� Works in reverse, so sp_metrics ‘filter’, <gid>, ‘lio_max < 100’ removes
everything with less than 100 logical IOs
11 – © 2011 Sybase, Inc – May 3, 2011
everything with less than 100 logical IOs
� Generally recommend only one filter set via sp_configure - so this
technique is how to apply a second filter� exec sp_configure ‘metrics elap max’, 2000
� exec sp_metrics ‘filter’,’1’,’exec_avg < 500’
� Result: anything that takes more than 2 secs elapsed and more than 500ms cpu
time
Typical runTypical run
• Enable metrics captureset metrics_capture on
set statement_cache on
set literal_autoparam on
sp_configure ‘metrics elap max’, <number>
• Run test/application
• Flush/backup metrics & disable
Optional: Allows us to compare
plans/queries without worrying if
SARGS match
12 – © 2011 Sybase, Inc – May 3, 2011
• Flush/backup metrics & disablesp_metrics ‘flush’
sp_metrics ‘backup’, ‘<number>’
set metrics_capture off
• Analyzeselect * from sysquerymetrics …
sp_metrics ‘filter’, … --remove unwanted results
• Dropsp_metrics ‘drop’, ‘<number>’
Note that the # is quoted
Analyzing QP MetricsAnalyzing QP Metrics
• Pretty flexible by using normal SQL on view
• Some examples:
� Find high incidence queries firstselect * from sysquerymetrics where gid=-1*<gid>order by cnt desc, elap_avg desc, hashkey, id, sequence
� Compare the metrics for same query (regression testing)select * from sysquerymetrics m1, sysquerymetrics m2where m1.hashkey = m2.hashkeyand m1.gid != m2.gid
13 – © 2011 Sybase, Inc – May 3, 2011
and m1.gid != m2.gidand m1.sequence = m2.sequence
order by m1.hashkey, gid, m1.uniqueid, m1.sequence
• Some tips:
� Sort metrics by total LIO or total elapsed by muliplying cnt*lio_avg or similar - helps to balance repeat bad queries vs. the one odd execution that happens only once per day
� Select a list of known approved query hashkeys (e.g. report queries) into a table and then only select queries not in that list to eliminate queries that you know legitimately exceed the desired limit
Quick Review of AdQuick Review of Ad--hoc Query Detectionhoc Query Detection
• Start with MDA to determine if long running query is cause
� monSysStatement and monProcessStatement
• Dev/Test
� Enable QP metrics capture globally with decreasing filters on elapsed time
� Fix the obvious problems (missing stats, etc.)
� Consider login trigger to change optgoal or set/disable opt criteria
14 – © 2011 Sybase, Inc – May 3, 2011
� Consider login trigger to change optgoal or set/disable opt criteria
� Use QP Tune on queries left to find out if fix is possible without rewriting the query
• Production
� Crisis mode: � Use QP metrics as above to try to isolate critical problems/queries� Enable/disable quickly to avoid needle in haystack problem
� Post-upgrade monitoring:� Use a login trigger and ‘set metrics_capture on’ for a representative set of users.
What is QP Tune?What is QP Tune?
• A Tuning Utility for Semi-Automating some of the Tuning Steps
� Can identify and fix missing statistics
� Core features are query capture and metrics comparison
• Based on QP Metrics
� It locates query issues via QP Metrics/sysquerymetrics
� As a result, it might not work well on store procs/statement cache
� Locates missing statistics via sp_configure
15 – © 2011 Sybase, Inc – May 3, 2011
• Focus is on query capture/metrics comparison
� Unfortunately, you get to test your meddle with supplying optgoals/criteria to try
� …and executing the iterations to try
� Generates abstract query plan for QPOD
� Compares results and tells you which ones worked better � Arguably, the main feature
• Requires sa_role or sso_role for capture/fix
� Other users can only do compare
� Why? Because QP Tune exec’s sp_configure
QP Tune SyntaxQP Tune Syntax
• Location
� $SYBASE/ASE-15_0/qptune
• Syntax
� QPTune� [-U <username>] [-P <password>] [-S <hostname:port/database>] [-J <charset>] � [-A <action [start|collect(_full)|compare|fix|(start|collect|fix|undo_fix)_stats]>]� [-M <mode>] � [-T <appTime>]
16 – © 2011 Sybase, Inc – May 3, 2011
� [-T <appTime>] � [-i <inputFile>]� [-o <outputFile>] � [-f <fileList(,)>]� [-c <configFile>]� [-l <limit>]� [-e <evalField>] � [-d <diff%(,diff_abs)>]� [-m <missingCount>]� [-n <login>] � [-N (noexec)]� [-g (applyOptgoal)]� [-v (verbose)] [-s (sort)] [-h (help)]
Using QP Tune….Using QP Tune….
• VERY IMPORTANT
� Download/read ASE 15.0.3 Migration Technology Guide � Chapter 2 - QP Tune
• Can it be used in production?
� Possibly - but think about it…� it may be making server changes that affect everything and in addition repeatedly
reissuing queries with known issues - artificially inflating CPU costs, IO patterns and data cache utilization
� …and really high usage of system segment/sysquery plans
17 – © 2011 Sybase, Inc – May 3, 2011
� …and really high usage of system segment/sysquery plans� …and if app changes data with insert/update/delete….did you book that trade 20
times??
• Recommendation
� Use QP Metrics/MDA to find queries with issues in production
� Run QP Tune on just those queries in DEV/QA
� Determine how you are going to fix each query
� Deploy fixes to PROD manually� Either by copying AQP created by QP Tune to PROD� …making suggested optgoal/criteria changes in login trigger� …making suggested config changes to PROD server
The Need for Application TracingThe Need for Application Tracing
• During development
� Frequently the developer hits a query issue � Fun job of finding which query is the cause….
� …then has to extract the SQL, application testing values
� DBA attempts to reproduce� Sometimes hindered by not having same environment
• During production
18 – © 2011 Sybase, Inc – May 3, 2011
• During production
� Hard to identify the exact query being run by the user at the point in
time� Most techniques do server wide capturing for all processes (MDA)
� Diagnostic traces require submitting ‘set options’ ahead of SQL – difficult to modify application to do so
– Even more difficult to capture the output
Applicating Tracing: 'set tracefile' (15.0.2)Applicating Tracing: 'set tracefile' (15.0.2)
• set tracefile { filepath | off } [ for spid ]� Saves all SQL text to a flat file until turned off
� Session tracing is enabled for the spid/session being traced….� ….it is not the perspective that the sa session is tracing a spid, but rather that a spid is
being traced
� Restrictions� You cannot save the SQL text for system tasks (housekeeper, checkpoint process, etc.).� You must have the sa or sso roles, or be granted set tracing permission� set tracefile is not allowed to open an existing file as a tracefile.� During an SA or SSO session, if you enable set tracfile for a specific spid, all subsequent
19 – © 2011 Sybase, Inc – May 3, 2011
� During an SA or SSO session, if you enable set tracfile for a specific spid, all subsequent tracing commands executed take effect on that spid, not the SA or SSO spid.
� You cannot trace more than one session at a time from a single sa or sso session. � You cannot trace the same session from multiple sa or sso sessions.
� Notes� The file storing the trace output is closed when the session being traced quits or when
you disable tracing.� Before you allocate resources for tracing, keep in mind that each tracing requires one file
descriptor per engine.
• sp_helpapptrace
� System proc that reports sessions being traced and the session doing the tracing
sp_helpapptracesp_helpapptrace
20 – © 2011 Sybase, Inc – May 3, 2011
set tracefile & set optionsset tracefile & set options
• Tracing a session
� Once you have enabled tracing for a session, any further set commands associated with QP debugging take effect for the session being traced vs. the session issuing the commands
• Set options that affect tracing
� set show_sqltext [on | off] (requires trace flag 3604 to be on)
� set showplan [on | off]
� set statistics io, time, plancost, resource [on | off]
� set option show [normal | brief | long | on | off]
� set option show_lop [normal | brief | long | on | off]
21 – © 2011 Sybase, Inc – May 3, 2011
� set option show_parallel [normal | brief | long | on | off]
� set option show_search_engine [normal | brief | long | on | off]
� set option show_counters [normal | brief | long | on | off]
� set option show_managers [normal | brief | long | on | off]
� set option show_histograms [normal | brief | long | on | off]
� set option show_abstract_plan [normal | brief | long | on | off]
� set option show_best_plan [normal | brief | long | on | off]
� set option show_code_gen [normal | brief | long | on | off]
� set option show_pio_costing [normal | brief | long | on | off]
� set option show_lio_costing [normal | brief | long | on | off]
� set option show_log_props [normal | brief | long | on | off]
� set option show_elimination [normal | brief | long | on | off]
� set option show_missing_stats [normal | brief | long | on | off]
Using Application TracingUsing Application Tracing
• Scenario
� Fred calls up complaining of a slow query
• Steps for Application Tracing
� Use sp_who to find Fred’s spid (e.g. 123)
� set tracefile '/home/sybase/my_tracetest_20100914.out' for 123
� Enable desired options� Dbcc traceon(3604)
22 – © 2011 Sybase, Inc – May 3, 2011
� Dbcc traceon(3604)� Set show_sqltext on� Set showplan on� Set statistics time, io on� Set statistics plancost, resource on� Set option show_missing_stats on� Set option show on -- optional
� Check tracing (sp_helpapptrace)
� (run test)
� set tracefile off for 123
Sample tracefile outputSample tracefile output
(some output omitted for brevity)
2010/09/14 16:33:22.80
SQL Text: select *
into #temp
from telco_facts
where number_of_lines > 1
==================== Lava Operator Tree ====================
Emit
(VA = 2)
r:3.665e+06 er:2.376e+06
cpu: 10300
/
Insert
#temp
23 – © 2011 Sybase, Inc – May 3, 2011
#temp
(VA = 1)
r:3.665e+06 er:2.376e+06
/
TableScan
telco_facts
(VA = 0)
r:3.665e+06 er:2.376e+06
l:1.029e+05 el:0
p:1.029e+05 ep:0
============================================================
Table: telco_facts scan count 1, logical reads: (regular=102858 apf=0 total=102858), physical reads: (regular=21509 apf=81349 total=102858), apf IOs used=81349
Table: #temp00001110015196818 scan count 0, logical reads: (regular=3671702 apf=0 total=3671702), physical reads: (regular=0 apf=0 total=0), apf IOs used=0
Total writes for this command: 407
Execution Time 103.
Analyzing QP Optimization
24 – Sybase Confidential – May 3, 2011
Tools for Analyzing QP OptimizationTools for Analyzing QP Optimization
• Old tools (ASE 12.5 and previous)
� Set showplan
� Set statistics time, io
� Dbcc traceon (302, 310, 311, …)
• New tools
� Enhanced showplan (join strategy, sorts, etc.)
� Set option show (replaces dbcc traces - now deprecated)
� Capture missing statistics
25 – © 2011 Sybase, Inc – May 3, 2011
� Capture missing statistics
� Set statistics plancost, resource
• What TS will need (so you might as well use as well)
� Showplan, set statistics plancost
� Set option show
� Schema, optdiag, data (if possible)
• You will need to take more care in testing
� Having statement cache enabled can be frustrated during testing as it may grab a previously cached good/bad plan….
� Showplan will reveal if using a previously cached plan
Example: Multiple Join KeysExample: Multiple Join Keys
• Sample SQL:select count(*)
from tableA, tableB
where tableA.TranID = tableB.TranID
and tableA.OriginCode = tableB.OriginCode
and tableB.Status <> ‘Y’
• Comments:
26 – © 2011 Sybase, Inc – May 3, 2011
• Comments:
� Since tableA doesn’t have any SARG conditions, the entire table is
likely required - and a table scan would be expected.
� Assuming a normal pkey/fkey relationship, however, we would expect
tableB to use the index on the fkey relationship {TranID, OriginCode}.
� If most Status values in TableB are not ‘Y’, having column statistics
might help - even if column not indexed
Showplan: Multiple Join KeysShowplan: Multiple Join Keys
The type of query is SELECT.
ROOT:EMIT Operator
|SCALAR AGGREGATE Operator
| Evaluate Ungrouped COUNT AGGREGATE.
|
| |MERGE JOIN Operator (Join Type: Inner Join)
| | Using Worktable2 for internal storage.
| | Key Count: 1
| | Key Ordering: ASC
| |
| | |SCAN Operator
| | | FROM TABLE
| | | tableB
| | | Table Scan.
Actual key count should have been 2
since join is on both TranID and
OriginCode. This is a key clue that
update index statistics needs to be
run.
27 – © 2011 Sybase, Inc – May 3, 2011
| | | Table Scan.
| | | Forward Scan.
| | | Positioning at start of table.
| | | Using I/O Size 16 Kbytes for data pages.
| | | With LRU Buffer Replacement Strategy for data pages.
| |
| | |SORT Operator
| | | Using Worktable1 for internal storage.
| | |
| | | |SCAN Operator
| | | | FROM TABLE
| | | | tableA
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Using I/O Size 16 Kbytes for data pages.
| | | | With LRU Buffer Replacement Strategy for data pages.
Since TableB is bigger and we have to scan
it, we make it the outer table….this means
we sort the smaller table - but it is still a
sort. In this case a clustered index on
TableA may have been all that was
necessary to turn a SMJ into a MJ (faster
than NLJ)
Set option show….Set option show….
• set option show <normal/brief/long/on/off>
� Basic syntax common to all
� Execute this one first prior to any of the below when used together (other
wise it will reset the below to the level of detail for show command itself)
• Common ones used (full list is in the documentation)
� … show Shows all the details (TS will want this one)
� … show_abstract_plan <…> Shows the details of an abstract plan.
28 – © 2011 Sybase, Inc – May 3, 2011
� … show_abstract_plan <…> Shows the details of an abstract plan.
� … show_search_engine <…> Shows the details of a search engine.
� … show_best_plan <…> Shows the details of the best QP plan.
� … show_code_gen <…> Shows the details of code generation.
� … show_pio_costing <…> Shows estimates of physical I/O
� … show_lio_costing <…> Shows estimates of logical input/output.
� … show_elimination <…> Shows partition elimination.
� … show_missing_stats <…> Shows columns with missing stats.
For example…For example…
• We want to…
� See the query plan
� …make sure we aren’t missing obvious statistics
� …get the abstract plan � so we can do a ‘create plan’ and fix the app w/o changing codeset showplan on
set option show_missing_stats on
29 – © 2011 Sybase, Inc – May 3, 2011
set option show_missing_stats on
set option show_abstract_plan on
• We want to…debug index & hordes of detail
� Like we used to with 302,310,315,317, etc.dbcc traceon(3604)
set showplan on
set option show long
Histograms and ASE 15Histograms and ASE 15
• ASE 15 is much more susceptible to
� Missing histograms
� Missing indexes
• #1 Cause for issues
� Forgetting to first delete statistics and then run update index statistics on all tables after upgrading (part of the TS checklist)
• Rationale
� In ASE 12.5, we often only had a single technique for performing certain
30 – © 2011 Sybase, Inc – May 3, 2011
� In ASE 12.5, we often only had a single technique for performing certain operations - such as joins, distincts, unions, etc.
� ASE 15 often now has 2-3 methods for any one of these� The decision as to which one is best to use will often depend upon the estimate for
query costing� If we don’t have any histograms on the SARGs/Joins/Distincts, we will be forced to use
the “magic values”…true in 12.x - still true in 15.x– There are more “magic” values than just the 10%/25%/33% for SARGS– “magic” values are also used for joins (i.e. equijoin translates to 10%)– “magic” values for distincts often used 256*datatype length
� As database volumes have grown, these large percentages for magic values often translates into really high estimates for LIO, PIO or CPU
� Result is that ASE will often pick a DSS type technique for processing large volumes of data instead of an OLTP technique
Finding Missing HistogramsFinding Missing Histograms
• New Config in ASE 15.0.3 ESD #1
� sp_configure ‘capture missing statistics’, [0 | 1]
� In pre-15.0.3, similar functionality with set option show_missing_stats on, but output has to be grabbed/grep'd from ASE errorlog
• Used by QP Tune
� Problem: QP Tune is not recommended for production� Okay for stats, but then you have to remember not to use it for queries - best bet is not to use it
for production at all
� Solution: Analyze sysstatistics directly
31 – © 2011 Sybase, Inc – May 3, 2011
� Solution: Analyze sysstatistics directly� Config option is dynamic - turn on, sample, turn off
• sysstatistics
� formatid = 110
� colidarray � the columns without stats� One row for each column without statistics (column stats)� One row for each combination of columns without statistics (density stats)
– But colidarray may only list the first two columns
� Can easily be decoded with a SQL function; see blog at http://tinyurl.com/3sop2dj
� Column 'C0' � number of times column referenced without stats
Missing Stats in SysstatisticsMissing Stats in Sysstatistics
32 – © 2011 Sybase, Inc – May 3, 2011
Identify Missing StatisticsIdentify Missing Statistics
• Syntaxsp_configure ‘capture missing statistics’, [0 | 1]
set option show_missing_stats on
• Has been really useful in identifying statistics & indexing issues
� Limited to missing statistics, vs. pointing out when not enough statistics
steps or stale statistics exists….but…
� Used extensively internally on customer repro's
33 – © 2011 Sybase, Inc – May 3, 2011
� Helps point out when adding statistics on some columns that statistics are
currently not available for may help� Consider difference between update statistics and update index statistics
� May point out when skewed low cardinality columns need stats even when not indexed
for queries looking for the low incidence values– E.g. if we have stats on the column “is_color_blind=‘Y’”, the optimizer can make a better choice
about which table to join in which order as it knows number of rows qualifying
• Useful for identifying missing indexes
� Missing statistics � missing single column indexes
� Missing density stats � missing composite column indexes
Let’s Take An Example (purge delete):Let’s Take An Example (purge delete):
delete margin_activity_fin
from margin_activity ma,
margin_activity_fin maf
where ma.ssb_fund = 'IVX6'
and ma.margin_date = '20100601'
and ma.transaction_type <> 'ADJ CASH'
and maf.ssb_fund = ma.ssb_fund
and maf.margin_date = ma.margin_date
and maf.opening_trade_id = ma.opening_trade_id
and isnull (maf.closing_trade_id, 'OPEN') = isnull (ma.closing_trade_id, 'OPEN')
and not exists (select * from margin_approval_summ
34 – © 2011 Sybase, Inc – May 3, 2011
and not exists (select * from margin_approval_summ
where appr_key1_value = ma.ssb_fund
and appr_key2_value = ma.settle_currency
and appr_key5_value = ma.clearing_broker_nbr
and margin_date = ma.margin_date
and ((transaction_type <> 'ADJ CASH' and approved_flg = 'Y')
or cash_flg = 'C‘
)
)
Resulting Ugly Query PlanResulting Ugly Query PlanSTEP 1
The type of query is DELETE.
10 operator(s) under root
|ROOT:EMIT Operator (VA = 10)
|
| |DELETE Operator (VA = 9)
| | The update mode is deferred.
| |
| | |SQFILTER Operator (VA = 8) has 2 children.
| | |
| | | |MERGE JOIN Operator (Join Type: Inner Join) (VA = 4)
| | | | Using Worktable1 for internal storage.
| | | | Key Count: 1
| | | | Key Ordering: ASC
| | | |
| | | | |RESTRICT Operator (VA = 1)(0)(0)(0)(0)(3)
| | | | |
| | | | | |SCAN Operator (VA = 0)
| | | | | | FROM TABLE
| | | | | | Keys are:
| | | | | | ssb_fund ASC
| | | | | | margin_date ASC
| | | | | | Using I/O Size 2 Kbytes for index leaf pages.
| | | | | | With LRU Buffer Replacement Strategy for index leaf pages.
| | | | | | Using I/O Size 16 Kbytes for data pages.
| | | | | | With LRU Buffer Replacement Strategy for data pages.
| | |
| | | Run subquery 1 (at nesting level 1).
| | |
| | | QUERY PLAN FOR SUBQUERY 1 (at nesting level 1 and at line 10).
| | |
| | | Correlated Subquery.
| | | Subquery under an EXISTS predicate.
| | |
| | | |SCALAR AGGREGATE Operator (VA = 7)
| | | | Evaluate Ungrouped ANY AGGREGATE.
| | | | Scanning only up to the first qualifying row.
| | | |
| | | | |RESTRICT Operator (VA = 6)(0)(0)(0)(10)(0)
35 – © 2011 Sybase, Inc – May 3, 2011
| | | | | | FROM TABLE
| | | | | | margin_activity_fin
| | | | | | maf
| | | | | | Using Clustered Index.
| | | | | | Index : margin_activity_fin_key
| | | | | | Forward Scan.
| | | | | | Positioning by key.
| | | | | | Keys are:
| | | | | | ssb_fund ASC
| | | | | | margin_date ASC
| | | | | | Using I/O Size 2 Kbytes for index leaf pages.
| | | | | | With LRU Buffer Replacement Strategy for index leaf pages.
| | | | | | Using I/O Size 16 Kbytes for data pages.
| | | | | | With LRU Buffer Replacement Strategy for data pages.
| | | |
| | | | |RESTRICT Operator (VA = 3)(0)(0)(0)(0)(3)
| | | | |
| | | | | |SCAN Operator (VA = 2)
| | | | | | FROM TABLE
| | | | | | margin_activity
| | | | | | Using Clustered Index.
| | | | | | Index : pkey
| | | | | | Forward Scan.
| | | | | | Positioning by key.
| | | | |RESTRICT Operator (VA = 6)(0)(0)(0)(10)(0)
| | | | |
| | | | | |SCAN Operator (VA = 5)
| | | | | | FROM TABLE
| | | | | | margin_approval_summ
| | | | | | Using Clustered Index.
| | | | | | Index : ix1
| | | | | | Forward Scan.
| | | | | | Positioning by key.
| | | | | | Keys are:
| | | | | | appr_key1_value ASC
| | | | | | margin_date ASC
| | | | | | appr_key2_value ASC
| | | | | | Using I/O Size 2 Kbytes for index leaf pages.
| | | | | | With LRU Buffer Replacement Strategy for index leaf pages.
| | | | | | Using I/O Size 16 Kbytes for data pages.
| | | | | | With LRU Buffer Replacement Strategy for data pages.
| | |
| | | END OF QUERY PLAN FOR SUBQUERY 1.
| |
| | TO TABLE
| | margin_activity_fin
| | Using I/O Size 2 Kbytes for data pages.
Why Was It Ugly???Why Was It Ugly???
• Problems
� Merge join on 1 key column of multiple column join is a real tip off
that statistics are missing or join is not a real equijoin
� It appeared to pick a incompletely indexed join on subquery� Subquery correlation on
– {appr_key1_value, appr_key2_value, appr_key5_value, margin_date}
� Indexed columns were only on – {appr_key1_value, margin_date , appr_key2_value}
36 – © 2011 Sybase, Inc – May 3, 2011
– {appr_key1_value, margin_date , appr_key2_value}
• Results of show_missing_statsNO STATS on column margin_activity_fin.opening_trade_id
NO STATS on column margin_activity_fin.margin_date
NO STATS on column margin_activity.transaction_type
NO STATS on column margin_approval_summ.cash_flg
NO STATS on column margin_approval_summ.approved_flg
NO STATS on column margin_approval_summ.transaction_type
NO STATS on column margin_approval_summ.appr_key5_value
NO STATS on density set for margin_approval_summ={margin_date, appr_key5_value,
appr_key2_value, appr_key1_value}
NO STATS on density set for margin_activity={clearing_broker_nbr, settle_currency,
margin_date, ssb_fund}
NO STATS on density set for margin_activity={clearing_broker_nbr, settle_currency}
Let’s Break It Down (1)Let’s Break It Down (1)
• All those “flag” columns in subqueryNO STATS on column margin_approval_summ.cash_flg
NO STATS on column margin_approval_summ.approved_flg
NO STATS on column margin_approval_summ.transaction_type
• But are the stats really missing???
� These are low cardinality columns - having an index likely will not help
� However, the question is how many of the otherwise qualified rows in
the join would be disqualified by these “flags”
37 – © 2011 Sybase, Inc – May 3, 2011
the join would be disqualified by these “flags”� Is any one of these values or the combination of them fairly discrete enough that it
would reduce the rows that qualify for the join to just a small percentage
� If so, while an index may not be beneficial, having column stats may be– And you may not need to update those column stats that often if the relative ratio of
statistics skew stays the same….just update on a less periodic basis
Let’s Break It Down (2)Let’s Break It Down (2)
• A much bigger problemNO STATS on column margin_activity_fin.opening_trade_id
NO STATS on column margin_activity_fin.margin_date
NO STATS on column margin_activity.transaction_type
• These are 2 of the 4 join columns for outer query
� Plus one of the SARGS - likely low cardinality
� But from the join perspective, remember, one of the join conditions
was isnull (maf.closing_trade_id, 'OPEN') = isnull (ma.closing_trade_id,
38 – © 2011 Sybase, Inc – May 3, 2011
was isnull (maf.closing_trade_id, 'OPEN') = isnull (ma.closing_trade_id,
'OPEN')� Unless you had a functional index, not a valid SARG or useful for join optimization
� Probably should have been written as a UNION ALL
� Regardless - this means for the join, we will only use the stats on the
leading column in the index because that is what we have� For the rest of the join conditions (2 cols above) we will use magic number of 10%
in combination with range density to try to get an estimate….but…..
� …hence the MERGE join with 1 column key
Let’s Break It Down (3)Let’s Break It Down (3)
• Possible Indexing IssuesNO STATS on column margin_approval_summ.appr_key5_value
NO STATS on density set for margin_approval_summ={margin_date, appr_key5_value,
appr_key2_value, appr_key1_value}
NO STATS on density set for margin_activity={clearing_broker_nbr, settle_currency,
margin_date, ssb_fund}
NO STATS on density set for margin_activity={clearing_broker_nbr, settle_currency}
• All point to possible indexing issues with subquery
� First two are pointing out that appr_key5_value is missing from the
39 – © 2011 Sybase, Inc – May 3, 2011
� First two are pointing out that appr_key5_value is missing from the
index on the subquery table
� The next two point out that while there are stats on the individual
columns, there isn’t an index that helps correlate the outer query with
the subquery� If there columns weren’t indexed at all or had missing stats, we would see
individual missing stats on the specified columns - which we don’t…so there are
stats
� But since we don’t have stats on the combination of columns, that means there
isn’t a covering index since density stats are created with an index (and updated
when update statistics is run)
set statistics plancost/resourceset statistics plancost/resource
• Adding an index or column stats may avoid a query rewrite
• Use statistics plancost to find out where
� Look for abnormally high row estimates 2 orders of magnitude higher than next higher node when reduction not due to aggregation/distinct
� Look for sorts where estimated/actual rows exceed ‘lava buffers per operator’ as it likely spills to disk (see physical IOs on node)� Focus on ‘buf ct’ and p/ep stats
� Focus on trying to get the row estimates as accurate as possible as low in
40 – © 2011 Sybase, Inc – May 3, 2011
� Focus on trying to get the row estimates as accurate as possible as low in the tree as possible
� Stats or indexes on low cardinality columns actually may be useful in ASE 15.0.x - especially due to frequency cells (histogram tuning)
• Use set statistics resource
� Look for high proc cache usage. If no sorting, number of histograms may be too high - slowing query optimization
� Correlate tempdb hwm with nodes - if you can’t - likely is worktable� High tempdb = sorts or worktable� Large worktable � unindexed join later - most likely becomes a SMJ.
set statistics plancost onset statistics plancost on
Emit(VA = 7)12 rows est: 1200cpu: 500
/GroupSorted(VA = 6)12 rows est: 1200
/NestLoopJoinInner Join(VA = 5)242704 rows est: 244857
Query:
select S.service_key, M.year, M.fiscal_period,count(*)
from telco_facts T,month M, service S
where T.month_key=M.month_key
and T.service_key = S.service_key
and S.call_waiting_flag='Y'
and S.caller_id_flag='Y'
and S.voice_mail_flag='Y'
group by M.year, M.fiscal_period, S.service_key
order by M.year, M.fiscal_period, S.service_key
go
41 – © 2011 Sybase, Inc – May 3, 2011
/ \Sort IndexScan(VA = 3) month_svc_idx (T)72 rows est: 24 (VA = 4)lio: 6 est: 6 242704 rows est: 244857pio: 0 est: 0 lio: 1116 est: 0cpu: 0 bufct: 16 pio: 0 est: 0
/NestLoopJoinInner Join(VA = 2)72 rows est: 24
/ \TableScan TableScanmonth (M) service (S)(VA = 0) (VA = 1)24 rows est: 24 72 rows est: 24lio: 1 est: 0 lio: 24 est: 0pio: 0 est: 0 pio: 0 est: 0
set statistics resource on set statistics resource on
• set statistics resource { on | off }
� Shows compilation and execution resources used� Tempdb, proc cache, sorting, etc….
• Example:
42 – © 2011 Sybase, Inc – May 3, 2011
• Example:
Statement: 1 Compile time resource usage: (est worker
processes=0 proccache=81), Execution time resource
usage: (worker processes=0 auxsdesc=0 plansize=10
proccache=15 proccache hwm=21 tempdb hwm=2)
Private buffer count: 24,Private HWM buffer count: 24
Manually Tuning Queries
43 – Sybase Confidential – May 3, 2011
Tools for Manually Tuning OptimizationTools for Manually Tuning Optimization
• Queries impacted due to optimization time
� Enable statement cache + literal autoparameterization
� Reduce optimization time
� Disable procedure deferred compilation
• Queries impacted due to resource usage
� Histogram tuning
44 – © 2011 Sybase, Inc – May 3, 2011
� Histogram tuning
� Server configuration settings
• Queries impacted due to plan changes
� Query rewrite (stored procs) (especially if bad SQL)
� Set statistics plancost
� Optgoals & controls
� Abstract Query Plans
A Commonly Reported Issue:A Commonly Reported Issue:
From: [email protected] [mailto:[email protected]]
Sent: Wednesday, April 15, 2009 11:27 AM
To: [email protected]; [email protected]
Subject: Case # 1152#### in TS-NA-FAST queue. This is a P3 / System Not Down ASE 15.x case
Message from ClaSS!
Case # 1152#### has been dispatched/forwarded to the TS-NA-FAST queue.
This is a P3 / System Not Down case
45 – © 2011 Sybase, Inc – May 3, 2011
This is a P3 / System Not Down case
Site Name: (customer name redacted)
Case Description : We have a sql code that is using a table with more than 1 million rows. The table
has several indexes, but the optimizer is doing a full table scan instead of using the indexes. Can
you please help.
Adaptive Server Enterprise/15.0.2/EBF 15959 ESD#6/P/
A Commonly Reported Issue…Uh OhA Commonly Reported Issue…Uh Oh
• Assumption: Query is slower (it doesn’t state this)
� If just worried about table scan, MJ and HJ may do table scans
� ….so table scans are not necessarily the ‘evil’ they were in 12.x
• Most likely cause
� Customer forgot to run update index statistics� Yes, there are other likely causes - but this is the most common
46 – © 2011 Sybase, Inc – May 3, 2011
� Result is that ASE is picking a merge join� Maybe because this is the best option anyhow (assume not)
� Because of the time involved, it likely is sorting after the table scan
• What customer needs to do
� Besides the missing histograms…..
� Learn what needs to be collected for query performance issues
� See if the table scan is really an issue…� We will assume it is or else they would not have called TS….
Merge Join
• What is it– Alternating scans of outer and inner
tables, matching rows in the process
– Scans each row in outer and inner table once
– Requires that outer and inner inputs are sorted in join key order (i.e. the join columns should be indexed)
• Query Tip
47 – © 2011 Sybase, Inc – May 3, 2011
• Query Tip– If you see this in a bad query plan, a
possible cause is lack of statistics on 2nd � nth join column� Update index statistics
– Most likely will be seen when two temp tables are joined (allrows_mixed)
– Watch for sorts….no-sort MJ likely not a problem
Merge Joins w/ NonMerge Joins w/ Non--Selective PredicatesSelective Predicates
• Indexed or Not
• Sample Queries
� pubtune database (salesdetail with ~1.3M rows)
� Query (37 row result)select stores.stor_name, stores.city, sales.date,
salesdetail.title_id, salesdetail.qty, salesdetail.discount
from stores, sales, salesdetail
48 – © 2011 Sybase, Inc – May 3, 2011
from stores, sales, salesdetail
where stores.stor_id = sales.stor_id
and sales.stor_id=salesdetail.stor_id
and sales.ord_num=salesdetail.ord_num
and salesdetail.discount > 50
The The ShowplansShowplans (allrows_mix vs. (allrows_mix vs. oltpoltp))
|ROOT:EMIT Operator (VA = 7)
|
| |MERGE JOIN Operator (Join Type: Inner Join) (VA = 6)
| | Using Worktable4 for internal storage.
| | Key Count: 2
| | Key Ordering: ASC ASC
| |
| | |MERGE JOIN Operator (Join Type: Inner Join) (VA = 3)
| | | Using Worktable2 for internal storage.
| | | Key Count: 1
| | | Key Ordering: ASC
| | |
| | | |SCAN Operator (VA = 0)
| | | | FROM TABLE
| | | | sales
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Using I/O Size 32 Kbytes for data pages.
| | | | With LRU Buffer Replacement Strategy for data pages.
|ROOT:EMIT Operator (VA = 4)
|
| |N-ARY NESTED LOOP JOIN Operator (VA = 3) has 3 children.
| |
| | |SCAN Operator (VA = 0)
| | | FROM TABLE
| | | stores
| | | Table Scan.
| | | Forward Scan.
| | | Positioning at start of table.
| | | Using I/O Size 32 Kbytes for data pages.
| | | With LRU Buffer Replacement Strategy for data pages.
| |
| | |SCAN Operator (VA = 1)
| | | FROM TABLE
| | | sales
| | | Using Clustered Index.
| | | Index : salesind
| | | Forward Scan.
| | | Positioning by key.
49 – © 2011 Sybase, Inc – May 3, 2011
| | | | With LRU Buffer Replacement Strategy for data pages.
| | |
| | | |SORT Operator (VA = 2)
| | | | Average Row width is 42.394478
| | | | Using Worktable1 for internal storage.
| | | |
| | | | |SCAN Operator (VA = 1)
| | | | | FROM TABLE
| | | | | stores
| | | | | Table Scan.
| | | | | Forward Scan.
| | | | | Positioning at start of table.
| | | | | Using I/O Size 32 Kbytes for data pages.
| | | | | With LRU Buffer Replacement Strategy for data pages.
| |
| | |SORT Operator (VA = 5)
| | | Average Row width is 37.544621
| | | Using Worktable3 for internal storage.
| | |
| | | |SCAN Operator (VA = 4)
| | | | FROM TABLE
| | | | salesdetail
| | | | Table Scan.
| | | | Forward Scan.
| | | | Positioning at start of table.
| | | | Using I/O Size 32 Kbytes for data pages.
| | | | With MRU Buffer Replacement Strategy for data pages.
| | | Positioning by key.
| | | Keys are:
| | | stor_id ASC
| | | Using I/O Size 4 Kbytes for data pages.
| | | With LRU Buffer Replacement Strategy for data pages.
| |
| | |SCAN Operator (VA = 2)
| | | FROM TABLE
| | | salesdetail
| | | Index : salesdetailind
| | | Forward Scan.
| | | Positioning by key.
| | | Keys are:
| | | stor_id ASC
| | | ord_num ASC
| | | Using I/O Size 4 Kbytes for index leaf pages.
| | | With LRU Buffer Replacement Strategy for index leaf pages.
| | | Using I/O Size 4 Kbytes for data pages.
| | | With LRU Buffer Replacement Strategy for data pages.
NLJ is looking so much better!!!NLJ is looking so much better!!!
The ResultsThe Results
Query 1 NLJ SMJ ∆%
Estimated IO Cost 2,927,215 1,310,046 -55
Proc cache (opt) 78 86 10
Proc cache hwm (exec) 11 63 472
Private Buffers 0 103
Actual IO Cost 1,909,689 66,516 -96
50 – © 2011 Sybase, Inc – May 3, 2011
Actual IO Cost 1,909,689 66,516 -96
Total Writes 107 0 -100
CPU Time 15,700 100 -99
Elapsed Time 22,703 3,906 -83
Whoa Whoa -- NLJ got skunked by a Merge JoinNLJ got skunked by a Merge Join!!!!!!(even with the sorting…)(even with the sorting…)
ASE 15.0.3 ESD #1 with 8 engines and 9GB of default cache
Set statistics Set statistics plancostplancost -- allrows_oltpallrows_oltp
==================== Lava Operator Tree ====================
Emit
(VA = 4)
r:32 er:439703
cpu: 15700
/
NaryNLJoin
(VA = 3)
r:32 er:439703
/ \
TableScan NaryNLJoin
51 – © 2011 Sybase, Inc – May 3, 2011
TableScan NaryNLJoin
stores (VA = 3)
(VA = 0) r:32 er:439703
r:507 er:507
l:13 el:13
p:0 ep:2
/ \
IndexScan NaryNLJoin
salesind (VA = 3)
(VA = 1) r:32 er:439703
r:132357 er:132363
l:2637 el:2128
p:15 ep:1115
/
IndexScan
salesdetailind
(VA = 2)
r:32 er:439703
l:809882 el:905313
p:11370 ep:27533
Remember, we are hitting every row in the
table for every store - so we are hitting some
pages more than once…so with 1.3M rows in
the table, if we have 2 levels of indexing, we
are doing 3 IO’s per row…”er” is based on an
unbounded range of 33%....
No SARG - every row
No SARG - every row
(l/el and p/ep stats) +
10% magic number
on 1.3M for row
estimate
Set statistics Set statistics plancostplancost -- allrows_mixallrows_mix
==================== Lava Operator Tree ====================
Emit
(VA = 7)
r:32 er:439703
cpu: 100
/
MergeJoin
Inner Join
(VA = 6)
r:32 er:439703
/ \
MergeJoin Sort
Inner Join (VA = 5)
Assuming we can’t change the
query (by adding SARGS) one way
to speed up query over all is to
eliminate the sorts….clustered
index change most likely
52 – © 2011 Sybase, Inc – May 3, 2011
Inner Join (VA = 5)
(VA = 3) r:32 er:445585
r:1858 er:132363 l:6 el:4906
p:0 ep:9802
cpu: 100 bufct: 79
/ \ /
TableScan Sort TableScan
sales (VA = 2) salesdetail
(VA = 0) r:8 er:507 (VA = 4)
r:1858 er:132357 l:6 el:6 r:32 er:445585
l:16 el:1115 p:0 ep:0 l:14854 el:14854
p:0 ep:140 cpu: 0 bufct: 24 p:1470 ep:1870
/
TableScan
stores
(VA = 1)
r:507 er:507
l:13 el:13
p:0 ep:2
We had an unbounded range (discount >
50) so the row estimate is 33% of 1.3M
rows - or ~450K rows
No SARG for sales, so
estimate is based on
10% of 1.3M due to
equijoin
The Indexes on The Indexes on SalesDetailSalesDetail
53 – © 2011 Sybase, Inc – May 3, 2011
PlancostPlancost: Post Clustered Index Change: Post Clustered Index Change
==================== Lava Operator Tree ====================
Emit
(VA = 6)
r:32 er:439703
cpu: 100
/
MergeJoin
Inner Join
(VA = 5)
r:32 er:439703
Sort is gone - straight merge join
thanks to creating a clustered
index with the join keys as leading
columns….
54 – © 2011 Sybase, Inc – May 3, 2011
/ \
MergeJoin TableScan
Inner Join salesdetail
(VA = 3) (VA = 4)
r:1858 er:132363 r:32 er:445585
l:14365 el:14368
p:0 ep:1796
/ \
TableScan Sort
sales (VA = 2)
(VA = 0) r:8 er:507
r:1858 er:132357 l:6 el:6
l:16 el:1115 p:0 ep:0
p:0 ep:140 cpu: 0 bufct: 24
/
TableScan
stores
(VA = 1)
r:507 er:507
l:13 el:13
p:0 ep:2
No SARG for sales or stores, so we are going to table
scan stores…thankfully it is small - but can we do
better - i.e. if tablescanning, why are we doing a sort
- we should scan a clustered index for MJ vs. SMJ
Remember
these l/el
numbers!!!
The Indexes on StoresThe Indexes on Stores
55 – © 2011 Sybase, Inc – May 3, 2011
Uhhh….we are joining on stor_id -
guess we forgot to index that
one…that explains the one merge join
& sort!!!
PlancostPlancost: Post Indexing Join Column: Post Indexing Join Column
==================== Lava Operator Tree ====================
Emit
(VA = 5)
r:32 er:337410
cpu: 300
/
MergeJoin
Inner Join
(VA = 4)
r:32 er:337410
Yehaw! SMJ gone!! Note the
join order changed….
stores � salesdetail � sales
instead of:
sales � stores � salesdetail
56 – © 2011 Sybase, Inc – May 3, 2011
/ \
NestLoopJoin TableScan
Inner Join sales
(VA = 2) (VA = 3)
r:32 er:342041 r:1858 er:132357
l:16 el:1115
p:0 ep:140
/ \
TableScan IndexScan
stores salesdetailind
(VA = 0) (VA = 1)
r:507 er:507 r:32 er:342041
l:12 el:13 l:16397 el:12548
p:0 ep:2 p:0 ep:1379
sales � stores � salesdetail
Yowsa! Note the increase in logical
IO’s …don’t get excited though - this is
due to NLJ….this explains the higher
cpu
The New ResultsThe New Results
Query 1 NLJ Old SMJ Final
Estimated IO Cost 2,927,215 1,310,046 311,465
Proc cache (opt) 78 86 95
Proc cache hwm (exec) 11 63 15
Private Buffers 0 103 0
Actual IO Cost 1,909,689 66,516 32,850
57 – © 2011 Sybase, Inc – May 3, 2011
Actual IO Cost 1,909,689 66,516 32,850
Total Writes 107 0 0
CPU Time 15,700 100 300
Elapsed Time 22,703 3,906 263
SWEET!!!SWEET!!!
Think About It for a SecondThink About It for a Second
• Logically:
� The join order stores � sales � salesdetail makes sense
� This would be the natural progression access method master � detail
� And since sales has a clustered index on stor_id, ord_num, the join
stores � sales should use an MJ
• So, let’s force it and see what happens….
58 – © 2011 Sybase, Inc – May 3, 2011
• So, let’s force it and see what happens….
� First we use ‘set option show_abstract_plan on’ to get a full AQP
� Then we just use the parts we want and modify it slightly:select stores.stor_name, stores.city, sales.date,
salesdetail.title_id, salesdetail.qty, salesdetail.discount
from stores, sales, salesdetail
where stores.stor_id = sales.stor_id
and sales.stor_id=salesdetail.stor_id
and sales.ord_num=salesdetail.ord_num
and salesdetail.discount > 50
plan "( m_join
(m_join ( scan stores ) ( scan sales ) )
( scan salesdetail ) )"
The AQP The AQP �������� PLAN ClausePLAN Clause
• The Actual AQP from show_abstract_plan� ( m_join ( nl_join ( i_scan stor_id_idx stores ) ( i_scan salesdetailind salesdetail ) ) (
i_scan salesind sales ) ) ( prop stores ( parallel 1 ) ( prefetch 32 ) ( lru ) ) ( prop salesdetail ( parallel 1 ) ( prefetch 32 ) ( lru ) ) ( prop sales (parallel 1 ) ( prefetch 32 ) ( lru ) )
• Coming up with our plan
� We only care about the joins…so we can loose the IO & Parallel� ( m_join ( nl_join ( i_scan stor_id_idx stores ) ( i_scan salesdetailind salesdetail ) ) (
i_scan salesind sales ) )
59 – © 2011 Sybase, Inc – May 3, 2011
i_scan salesind sales ) )
� We are using table scan MJ (due to no SARGS) so we can replace i_scan with scan and drop the index names� ( m_join ( nl_join ( scan stores ) ( scan salesdetail ) ) ( scan sales ) )
� Then reorder them stores � sales � salesdetail� ( m_join ( nl_join ( scan stores ) ( scan sales) ) ( scan salesdetail ) )
� …and change the nl_join to m_join� ( m_join ( m_join ( scan stores ) ( scan sales) ) ( scan salesdetail ) )
• Viola!
PlancostPlancost: Forcing MJ’s Using PLAN Clause: Forcing MJ’s Using PLAN Clause
==================== Lava Operator Tree ====================
Emit
(VA = 5)
r:32 er:337410
cpu: 100
/
MergeJoin
Inner Join
(VA = 4)
r:32 er:337410
l:0 el:7.634e+006
p:0 ep:4118
All MJ’s (table scans) - no SMJ’s
Some stats:
Est IO Cost: 24,653,975!!!!
Proc Cache: 77 & 31 (opt & exec)
Actual IO: 28,764
Elapsed Time: 110ms!!!
Yowsa!!!! Look at the EL!!! This is why the
Estimate IO cost was so high and optimizer
tossed this plan - which is actually the best one.
Estimates from show_lio_costing were:
FINAL PLAN ( total cost = 2.465398e+007 ):
60 – © 2011 Sybase, Inc – May 3, 2011
p:0 ep:4118
/ \
MergeJoin TableScan
Inner Join salesdetail
(VA = 2) (VA = 3)
r:1858 er:101570 r:32 er:445585
l:14365 el:14368
p:0 ep:1796
/ \
TableScan TableScan
stores sales
(VA = 0) (VA = 1)
r:8 er:507 r:1858 er:132357
l:1 el:13 l:16 el:1115
p:0 ep:2 p:0 ep:140
FINAL PLAN ( total cost = 2.465398e+007 ):
lio=7649315 pio=6054.449 cpu=9.203984e+007
Why the Huge Estimate???Why the Huge Estimate???
• Set statistics io results shows:� Table: stores scan count 1, logical reads: (regular=1 apf=0 total=1), physical reads:
(regular=0 apf=0 total=0), apf IOs used=0
� Table: sales scan count 1, logical reads: (regular=16 apf=0 total=16), physical reads:
(regular=0 apf=0 total=0), apf IOs used=0
� Table: salesdetail scan count 1, logical reads: (regular=14365 apf=0 total=14365),
physical reads: (regular=0 apf=0 total=0), apf IOs used=0
� Total actual I/O cost for this command: 28764.
� Total writes for this command: 0
61 – © 2011 Sybase, Inc – May 3, 2011
� Total writes for this command: 0
• Assume:
� Join stores � sales is outer - estimate is 101570 rows
� We know the final join with MJ table scan is 14365 pages
� Since the scan count is 1 for a table scan MJ, somehow it is estimating
really badly….
• Wait and See
HmmmmHmmmm……
• In our database, discount is not part of an index
� Therefore no stats….
� Set option show_missing_stats proves this� NO STATS on column salesdetail.discount
• What happens if add column stats - but not an index
� Doesn’t affect the overhead of DML as there is no extra index or index
columns to maintain
62 – © 2011 Sybase, Inc – May 3, 2011
columns to maintain
� With a small number of distinct values, an index might not be all that
useful anyhow….� But the column stats will drop the gross over estimation of 33% to something
realistic
� Is this an argument for sliding scale magic numbers???
� Does affect maintenance in that we occasionally will need to run� update statistics salesdetail (discount)
� If we don’t like the answer, we can drop them…..� delete statistics salesdetail (discount)
PlancostPlancost: After Adding Column Stats: After Adding Column Stats
==================== Lava Operator Tree ====================
Emit
(VA = 4)
r:32 er:25
cpu: 200
/
NaryNLJoin
(VA = 3)
r:32 er:25
/ \
TableScan NaryNLJoin
stores (VA = 3)
All NaryNLJ’s - no MJ’s
Some stats:
Est IO Cost: 164,351!!!!
Proc Cache: 95 & 11 (opt & exec)
Actual IO: 33,010
Elapsed Time: 286ms
“single” NaryNLJ
“grandparent”
63 – © 2011 Sybase, Inc – May 3, 2011
stores (VA = 3)
(VA = 0) r:32 er:25
r:507 er:507
l:12 el:13
p:0 ep:2
/ \
IndexScan NaryNLJoin
salesdetailind (VA = 3)
(VA = 1) r:32 er:25
r:32 er:25
l:16397 el:12548
p:0 ep:1379
/
IndexScan
salesind
(VA = 2)
r:32 er:25
l:96 el:74
p:0 ep:27
Elapsed Time: 286ms
Interesting - only 32 rows instead
of 33% of 1.3M…maybe an index
is worth it after all???
“parent”
“child”(with SARGS)
PlancostPlancost: After Adding Index: After Adding Index
==================== Lava Operator Tree ====================
Emit
(VA = 6)
r:32 er:25
cpu: 0
/
NestLoopJoin
Inner Join
(VA = 5)
r:32 er:25
/ \
MergeJoin IndexScan
Inner Join salesind
(VA = 3) (VA = 4)
Some stats:
Est IO Cost: 1,206!!!!
Proc Cache: 93 & 25 (opt & exec)
Actual IO: 222
Elapsed Time: 80ms
Sort is required due to getting
rows into stor_id order for the MJ
Why isn’t this an MJ - theoretically the
left tree is sorted in stor_id order - and
we have a clustered index???
Answer: This is a low cardinality join
(child � parent is 1:1) with a low
estimate << 10% of 1.3M and very high
selectivity - an MJ using a table scan
doesn’t make sense
64 – © 2011 Sybase, Inc – May 3, 2011
(VA = 3) (VA = 4)
r:32 er:25 r:32 er:25
l:96 el:74
p:0 ep:27
/ \
Sort TableScan
(VA = 1) stores
r:32 er:33 (VA = 2)
l:6 el:6 r:8 er:507
p:0 ep:0 l:1 el:13
cpu: 0 bufct: 24 p:0 ep:2
/
IndexScan
discount_idx
(VA = 0)
r:32 er:33
l:14 el:5
p:0 ep:5
rows into stor_id order for the MJ
- but since this is not a table scan
MJ, the sort is less a concern
doesn’t make sense
The New ResultsThe New Results
Query 1 NLJ Old SMJ Old Final Add Index
Estimated IO Cost 2,927,215 1,310,046 311,465 1,206
Proc cache (opt) 78 86 95 93
Proc cache hwm (exec) 11 63 15 25
Private Buffers 0 103 0 0
Actual IO Cost 1,909,689 66,516 32,850 222
65 – © 2011 Sybase, Inc – May 3, 2011
Actual IO Cost 1,909,689 66,516 32,850 222
Total Writes 107 0 0 0
CPU Time 15,700 100 300 ~0
Elapsed Time 22,703 3,906 263 80
SWEETER YET!!!SWEETER YET!!!
After Adding Index + Forcing MJ via PLANAfter Adding Index + Forcing MJ via PLAN
==================== Lava Operator Tree ====================
Emit
(VA = 5)
r:32 er:25
cpu: 200
/
MergeJoin
Inner Join
(VA = 4)
r:32 er:25
Some stats:
Est IO Cost: 277,701
Proc Cache: 77 & 31 (opt & exec)
Actual IO: 28,764
Elapsed Time: 110ms
Our gross over estimation
is now fixed as well!!!!
66 – © 2011 Sybase, Inc – May 3, 2011
/ \
MergeJoin TableScan
Inner Join salesdetail
(VA = 2) (VA = 3)
r:1858 er:101570 r:32 er:33
l:14365 el:14368
p:0 ep:1796
/ \
TableScan TableScan
stores sales
(VA = 0) (VA = 1)
r:8 er:507 r:1858 er:132357
l:1 el:13 l:16 el:1115
p:0 ep:2 p:0 ep:140
Why didn’t ASE pick it??
Answer: Cost. Remember the formula cost =
2LIO + 25PIO + 0.1CPU
FINAL PLAN ( total cost = 277701.3 ):
lio=15496 pio=1937 cpu=1982843
Why That Huge EstimateWhy That Huge Estimate
• New in ASE 15.0:
� Sp_configure…
� cost of a logical io = DEFAULT
� cost of a physical io = DEFAULT
� cost of a cpu unit = DEFAULT
• Cost formula
� Only with defaults� Plan cost = 2*LIO + 25*PIO + 0.1*CPU
67 – © 2011 Sybase, Inc – May 3, 2011
� Plan cost = 2*LIO + 25*PIO + 0.1*CPU
� Actual formula is:� Plan cost = lio_cost * LIO + pio_cost * PIO + (100/cpu_cost) * CPU
� Result it is a bit fun….you have to lower cpu cost to increase plan cost
• Our problem was the 7M LIO’s due to ….???
� Sorts or Join estimates (see next slide)
• So, one way to avoid sorts???
� Set the cost of a cpu unit lower until sort cost is too expensive� Because of the inverse relationship, this will increase cost of sort (e.g. 500 or 250)
� Haven’t tested this in real life yet - so just a possibility
Remember Me?Remember Me?
==================== Lava Operator Tree ====================
Emit
(VA = 5)
r:32 er:337410
cpu: 100
/
MergeJoin
Inner Join
(VA = 4)
r:32 er:337410
l:0 el:7.634e+006
p:0 ep:4118
Focus on the er….why were the join estimates so high???
Well, since we don’t have stats on discount and no other SARGS exist,
we have to assume a 1-to-many join for each of the join.
The formula for estimating rows is
Outer rows * inner rows * join column scan selectivity (inner rows) for
index used on the join
So for the first join (stores����sales) we would have 507 * 132357 *
68 – © 2011 Sybase, Inc – May 3, 2011
p:0 ep:4118
/ \
MergeJoin TableScan
Inner Join salesdetail
(VA = 2) (VA = 3)
r:1858 er:101570 r:32 er:445585
l:14365 el:14368
p:0 ep:1796
/ \
TableScan TableScan
stores sales
(VA = 0) (VA = 1)
r:8 er:507 r:1858 er:132357
l:1 el:13 l:16 el:1115
p:0 ep:2 p:0 ep:140
So for the first join (stores����sales) we would have 507 * 132357 *
selectivity of stor_id in sales table….which for a table scan is either
indid of 1 or 0 ….set option show on reveals:
Estimating selectivity of index 'sales.salesind', indid 1
stor_id = stor_id
Estimated selectivity for stor_id,
selectivity = 0.001513594,
scan selectivity 0.001513594, filter selectivity 0.001513594
...so we have 507 * 132357 * 0.001513594 = 101569.72
…which of course is rounded up to 101,570 rows….
…now for the next join
Remember Me? (cont)Remember Me? (cont)
==================== Lava Operator Tree ====================
Emit
(VA = 5)
r:32 er:337410
cpu: 100
/
MergeJoin
Inner Join
(VA = 4)
r:32 er:337410
l:0 el:7.634e+006
p:0 ep:4118
…that’s (previous page) the outer table in the join to ����
salesdetail….which we had 1.3M rows at 33% for 445585
rows…checking the …set option show on output….
Estimating selectivity of index 'salesdetail.salesdetailind', indid 1
stor_id = stor_id, stor_id
ord_num = ord_num
Estimated selectivity for stor_id,
selectivity = 0.001948715,
Estimated selectivity for ord_num,
selectivity = 7.505242e-006,
69 – © 2011 Sybase, Inc – May 3, 2011
p:0 ep:4118
/ \
MergeJoin TableScan
Inner Join salesdetail
(VA = 2) (VA = 3)
r:1858 er:101570 r:32 er:445585
l:14365 el:14368
p:0 ep:1796
/ \
TableScan TableScan
stores sales
(VA = 0) (VA = 1)
r:8 er:507 r:1858 er:132357
l:1 el:13 l:16 el:1115
p:0 ep:2 p:0 ep:140
selectivity = 7.505242e-006,
Clustered Index Data Row Filtering Predicates
discount > 50
Estimated selectivity for discount,
selectivity = 0.33,
scan selectivity 7.505169e-006, filter selectivity 2.476706e-006
Again, a bit of math 101570 outer rows * 445585 inner rows *
7.505169e-006 = 339669.45…likely something else considered but you
can see how we are in the same ballpark….
….so wildly huge join estimates way above actual rows points to poorly
indexed join.
Controlling Merge JoinsControlling Merge Joins
• Step 1: Eliminate the “sorts” on table scan SMJ
� Sorting involves quite a bit of cpu time - and memory
� Reducing sorts can dramatically increase response times as well as improve system concurrency as more cpu available for others
� Be careful - not all MJ’s use table scan - if inner table is using a different SARG index, the sort may be necessary as the SARG index is helpful
• Make sure join keys all have stats
� No missing stats on join keys
• Make a SMJ into a plain MJ
70 – © 2011 Sybase, Inc – May 3, 2011
• Make a SMJ into a plain MJ
� Tip: create a clustered index with the join keys as leading columns
• Make a SMJ ���� NLJ
� Make sure the join is indexed
� Typically more of an issue for #temps - which only get used once� ...but if used more than once consider an index on #temp
• Step 2: Reduce the row estimates
� Consider stats or an index on low cardinality column� Use stats first to see if estimated rows is worth it
� Debate the tradeoff of extra index overhead for DML vs. query perf
What If This Doesn’t Help???What If This Doesn’t Help???
• Try changing the OptGoal
� This varies which subsets of optimization criteria are used
� Some optgoals are specific to particular CR’s
• Try different opt criteria
� Look for opt criteria in the showplan or plancost that involve sorting
or other high resource usage
71 – © 2011 Sybase, Inc – May 3, 2011
or other high resource usage
� In an index didn’t resolve the sort - optimizer may not be allowing it
for some other reason
� See what optcriteria is currently in use
� Try the query with a different criteria� E.g. hash_union_distinct vs. merge_union_distinct
OptgoalsOptgoals & Join & Join Methods/FeaturesMethods/Features
NL
J
SM
J
HJ
N-a
ry N
LJ
Pro
xy T
ab
le/C
IS
Para
llel Q
uery
Sta
r Sch
em
a
Bu
sh
y J
oin
Eag
er A
gg
reg
atio
n
72 – © 2011 Sybase, Inc – May 3, 2011
Eag
er A
gg
reg
atio
n
allrows_oltp ���� * ����
allrows_mix (default) ���� ���� ���� ���� ���� ����
allrows_dss ���� ���� ���� ���� ���� ���� ���� ���� ����
* May still use merge join in some cases according to ASE PSE
master..sysoptionsmaster..sysoptions
73 – © 2011 Sybase, Inc – May 3, 2011
Which OptCriteria To TryWhich OptCriteria To Try
• Joins� nl_join, nary_nl_join,
merge_join, hash_join
� bushy_space_search,
alternative_greedy_search
• Union� merge_union_distinct,
hash_union_distinct
• Index/Reformatting� multi_table_store_ind
� store_index
• Index Selection/Order By� mnc_full_index_filter
� streaming_sort, order_sorting
� index_union, index_intersection
• Group By/Aggregates
74 – © 2011 Sybase, Inc – May 3, 2011
hash_union_distinct
• Union All� append_union_all,
merge_union_all
• Distinct� no_stats_distinctness
� opportunistic_distinct_view
� distinct_sorting, distinct_sorted,
distinct_hashing
• Group By/Aggregates� group_sorted, group_inserting,
group_hashing
� advanced_aggregation
Deploying Query Tuning Fixes
75 – Sybase Confidential – May 3, 2011
Tools for Deploying Query Fixes Tools for Deploying Query Fixes
• Query Rewrite
� Best option for bad SQL/SQL tuned for 12.5 optimizer
� Best solution for stored procedures
• Stored Procedure optgoals/controls
� Good solution for stored procs with a large number of queries with similar issues
• Query PLAN Clause
76 – © 2011 Sybase, Inc – May 3, 2011
• Query PLAN Clause
� Good for isolating a fix to a specific query - especially when the SARG’s vary widely from one invocation to the next
• Abstract Query Plans
� Good for fixing a common query with consistent SARG criteria
• Login Trigger
� Best way to enable optimization features/controls based on application generic requirements
Login TriggerLogin Trigger
• Your BEST friend in a migration (other than testing)
• Allows you to control and set “optimization profiles”
� …for OLTP vs. reporting apps
� …for batch programs
� …for electronic feeds
• Allows you to gradually introduce ASE 15 optimization to a
77 – © 2011 Sybase, Inc – May 3, 2011
• Allows you to gradually introduce ASE 15 optimization to a
less than thoroughly tested application
� Start with compatibility mode or allrows_oltp
� Turn off compatibility mode or increase optgoal� Application by application, or…
� …appserver by appserver (25%, 50%, 75%, 100% of user population)
� …IP address ranges for clients
� …login name
� …mod of the SPID (if nothing else)
� …anything visible in master..sysprocesses
Example Optimization Profile ScenariosExample Optimization Profile Scenarios
• Replication Server
� Login trigger detects replication_role� Enables statement cache + literal parameterization
� Sets optgoal allrows_oltp
� Sets delayed commit on
� Dynamic listeners allow RS to connect while blocking other users� E.g. warm standby
• Batch Load Program
78 – © 2011 Sybase, Inc – May 3, 2011
• Batch Load Program
� Login trigger sets delayed commit on
• Report Daemon
� Login trigger detects application name or login name� Disables statement cache
� Sets optgoal allrows_dss
� Increases optimization timeout limit
� Enables qp metrics for long running queries
� Application/login binding to separate tempdb (DSS optimized devices)
Sample Login Trigger (1)Sample Login Trigger (1)
-- created in master to allow prevent login failures in case the server needs to
-- be booted with traceflag to recover master database only
use master
go
create table optimization_profile (
appname varchar(30) not null,
opt_goal varchar(20) not null,
parallel_deg tinyint not null,
use_stmtcache bit not null,
use_mergejoin bit not null,
use_hashjoin bit not null,
79 – © 2011 Sybase, Inc – May 3, 2011
use_hashjoin bit not null,
use_idxunion bit not null,
compatibility_mode bit not null,
delayed_commit bit not null,
-- other attributes/opt criteria as desired/necessary
primary key (appname)
)
go
exec sp_logintrigger 'drop'
go
if exists (select 1 from sysobjects where name='sp_optimization_profile’ and type='P' and
uid=user_id())
drop procedure sp_optimization_profile
go
print "...creating procedure: 'sp_optimization_profile'"
go
Sample Login Trigger (2)Sample Login Trigger (2)
create procedure sp_optimization_profile
as begin
declare @appname varchar(30),
@opt_goal varchar(20),
@use_stmtcache bit,
@use_mergejoin bit,
@use_hashjoin bit,
@use_idxunion bit,
@compatibility_mode bit,
@delayed_commit bit
if suser_name()='sa' return 0
80 – © 2011 Sybase, Inc – May 3, 2011
if suser_name()='sa' return 0
if has_role('sa_role',0)=1 return 0
select @appname=(case when clientapplname is not null
then clientapplname
else program_name end)
from master..sysprocesses
where spid=@@spid
--because login trigger, all future ‘set’ clauses are automatically exported
if exists (select appname from master..optimization_profile
where appname=@appname)
begin
select @opt_goal=opt_goal,
@use_stmtcache=use_stmtcache, … -- <list of criteria>
from master..optimization_profile
where appname=@appname
Sample Login Trigger (3)Sample Login Trigger (3)
if @opt_goal="allrows_oltp" set plan optgoal allrows_oltp
if @opt_goal="allrows_mix" set plan optgoal allrows_mix
if @opt_goal="allrows_dss" set plan optgoal allrows_dss
if @use_stmtcache=1
begin
set statement_cache on
set literal_autoparam on
end
else
begin
81 – © 2011 Sybase, Inc – May 3, 2011
begin
set statement_cache off
end
if @delayed_commit=1 set delayed_commit on
…
end
return 0
end
go
grant exec on sp_optimization_profile to public
go
exec sp_logintrigger 'master..sp_optimization_profile'
go