Stop the Chaos! Get Real Oracle Performance by Query Tuning Part 2

Preview:

Citation preview

Stop the Chaos! Get Real Oracle Performance by Query Tuning – Part 2Janis Griffin

Senior DBA / Performance Evangelist

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Who am I?

• Senior DBA / Performance Evangelist for SolarWinds• Janis.Griffin@solarwinds.com• Twitter® - @DoBoutAnything• Current – 25+ Years in Oracle®, DB2®, ASE, SQL Server®, MySQL®• DBA and Developer

• Specialize in performance Tuning• Review database performance for customers and prospects• Common question: “How do I tune it?”

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Agenda

• A quick review of part 1• Using wait time analysis (WTA)• Reviewing the execution plan

• Understanding the optimizer• Finding the expensive steps

• Discuss several tuning techniques• SQL diagramming• Partitioning and other useful features

• How to identify coding mistakes • Engineer out the stupid

• What to do if you can’t change the code• Third-party applications• Avoiding hints

• Who registered yesterday for SQL Tuning?

SELECT s.fname, s.lname, r.signup_dateFROM student s

INNER JOIN registration r ON s.student_id = r.student_idINNER JOIN class c ON r.class_id = c.class_id

WHERE c.name = 'SQL TUNING'AND r.signup_date BETWEEN :beg_date AND :end_dateAND r.cancelled = 'N‘

• Execution Stats – 21,829 Buffer Gets• Execution Time – 22 seconds to execute• Wait Events – Waits 90% direct path read

Case Study for Review

User

complained.

Query spent

over 3 hours in

the database.© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Execution Plan

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Relationship Diagram

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• Recommends – three new indexes

Tuning Advisor

DECLARE

l_sql_tune_task_id VARCHAR2(100);

BEGIN

l_sql_tune_task_id := DBMS_SQLTUNE.create_tuning_task ( sql_id => '&sql_id',

scope => DBMS_SQLTUNE.scope_comprehensive, time_limit => 60,

task_name => '&sql_id', description => 'Tuning task for class registration query');

DBMS_OUTPUT.put_line('l_sql_tune_task_id: ' || l_sql_tune_task_id);

END;

/

EXEC DBMS_SQLTUNE.execute_tuning_task(task_name => '&sql_id');

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• Need to know the size of the actual data sets for each step in execution plan• In Joins (Right, Left, Outer)• What are the filtering predicates?• When is each filtering predicate applied?

• Try to filter earlier rather than later

• Compare size of final result set with data read• Find the driving table

• To reduce buffer gets

Tune the Query

SELECT s.fname, s.lname, r.signup_date

FROM student s

INNER JOIN registration r ON s.student_id = r.student_id

INNER JOIN class c ON r.class_id = c.class_id

WHERE c.name = 'SQL TUNING'

AND r.signup_date BETWEEN :beg_date AND :end_date

AND r.cancelled = 'N'

Joins

Filtering

Predicates

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• “SQL Tuning” by Dan Tow• Great book that teaches SQL Diagramming• http://www.singingsql.com

SQL Diagramming

registration

student class

5

1

30

1

4%

.1%

select count(1) from registration where cancelled = 'N'

and signup_date between '2016-12-10 00:00' and '2016-12-11 00:00'

64112 / 1783066 * 100 = 3.59 or 4%

select count(1) from class where name = 'SQL TUNING'

2 / 1,267 * 100 = .1%

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• CREATE INDEX cl_name ON class(name);

• Execution Stats – 20,348 buffer gets• Why is a full table scan still occurring on REGISTRATION?

New Execution Plan

• CLASS_ID not left leading in index

• Execution Stats – 20,348 buffer gets• Twice the work to use Primary Key Index on REGISTRATION

Review Index Order

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• CREATE INDEX reg_alt ON registration(class_id);

• Execution Stats – 3000 Buffer Gets / Average Execs - .008 Secs

New Execution Plan

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• CREATE INDEX reg_cancel_signup ON registration(cancelled,signup_date);

Tuning Advisor Suggested Index

Execution Stats:

1107 Buffer Gets

Avg Executions:

0.140 Secs

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• CREATE INDEX reg_alt ON registration(class_id,signup_date, cancelled);

• Execution Stats – 445 Buffer Gets / Average Execs - .002 Secs

Better Execution Plan

Performance Improved

reg_canceled_signup index

Average Wait Time per Execution for SQL Statement Class_Registration | CECE_JGRIFFIN-2

January 27, 2015

Daily Time Range: 1:00 PM-10:00 PM

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Case Study – Current Pay Check For Specific Employees

SELECT e.first_name, e.last_name, l.region_name

FROM emp e

INNER JOIN dept d ON e.department_id = d.department_id

INNER JOIN loc l ON l.location_id = d.location_id

WHERE (e.last_name LIKE :b1)

AND e.employee_id IN (

SELECT employee_id

FROM wage_pmt w

WHERE w.employee_id = e.employee_id

AND w.pay_date>= trunc(sysdate)-31);

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Wait Time Analysis

Almost 100% on PGA

memory allocation wait. New

wait event in 12.2 – not

documented

No statistics,

Unique indexes

Added PKs and Fks

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Execution Plan

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Understanding the Underlying Objects

ACCEPT SQL_ID CHAR PROMPT 'Enter SQL_ID> 'DECLAREl_sql_tune_task_id VARCHAR2(100);

BEGINl_sql_tune_task_id := DBMS_SQLTUNE.create_tuning_task ( sql_id => '&sql_id',scope => DBMS_SQLTUNE.scope_comprehensive, time_limit => 60,task_name => '&sql_id', description => 'Tuning task for Current Paycheck');DBMS_OUTPUT.put_line('l_sql_tune_task_id: ' || l_sql_tune_task_id);

END;/EXEC DBMS_SQLTUNE.execute_tuning_task(task_name => '&sql_id');SELECT DBMS_SQLTUNE.report_tuning_task('&sql_id') AS recommendations FROM dual;EXEC DBMS_SQLTUNE.drop_tuning_task('&sql_id');

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Ask the Tuning Advisor

No recommendations

SQL Diagramming

select count(1) from wage_pmt

where pay_date >= sysdate – 31

2,184 / 142,708 * 100 = 1.5%

select avg(cnt) from (select substr(last_name,1,3), count(*) cnt

from emp group by substr(last_name,1,3))

278.5 / 3,899 * 100 = .7.14% With full last nume = .1%

emp

dept

wage_pmt

975

1

37

1

.7%

2%

loc1

1

.1%

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

No change?

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Add Index on WAGE_PMT(employee_id)

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Adjust Index on WAGE_PMT

CREATE INDEX idx_emp_dateON wage_pmt(employee_id, pay_date);

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Improved Performance

Created index

IDX_EMP_DATE

• FETCH FIRST n ROWS ONLY • Retrieves first rows without scanning everything• Faster than using rownum

• OFFSET n ROWS FETCH FIRST n ROWS ONLY• Skip some number of rows

• 12.2 Approximate Query Processing• Used for approximate ‘count distinct’ values

• And adds percentile aggregation

• Allows for faster processing of large data sets• Not exact but usually within 95%+ range

• Three new parameters – alter system/session • approx_for_aggregation Default=FALSE• approx_for_count_distinct Default=FALSE• approx_for_percentile Default=NONE

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Other Tuning Tips

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Approximate SQL Example Without Changing Code

Why is it exact?

Need to

set both

• Convert non-partitioned table • To a partitioned table ONLINE

• Many new partition options• Automatic Lists• Multi-column Lists• Partitioned external tables• Other maintenance options

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

New 12.2 Partitioning Features

UPDATE INDEXES clause is

optional. Indexes with SYS

names will be generated if not

used.

• Look for performance inhibitors• Cursor or row by row processing • Parallel processing

• Don’t use in an OLTP environment• Use only when accessing large data sets and additional resources can be allocated

• Nested views that use db_links• Abuse of Wild Cards (*) or No Where Clause

• Select ONLY those columns in a query which are required. • Extra columns cause more I/O on the database and increase network traffic• Code-based SQL Generators (e.g. Hibernate)

• Using functions on indexed columns (SUBSTR, TO_CHAR, UPPER, TRUNC)• Optimizer can’t use the index

• Instead move the function to the constant or variable side of equation• Consider creating a function based index

• Hard-coded hints

Engineer Out the Stupid

select… where upper(last_name) = ‘GRIFFIN’

Better way: select … where last_name = upper(:b1);

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• Reduce SORT operations as they slow down your queries• Don’t use the UNION operator if you can use UNION ALL• Avoid the DISTINCT keyword if you don’t need it

• Ensure the left-leading column of a multi-column index is reference• Otherwise an INDEX SKIP SCAN may occur

• Often no better than a FULL TABLE SCAN

• Try to avoid Cartesian product queries• Use bind variables instead of literal values

• Reduces repeated parsing of the same statement

• If using sub-queries, make use of the EXISTS operator when possible• Optimizer will stop with a match and avoid a FULL TABLE SCAN

• Try to use an index if less than 5% of the data needs to be accessed• Exception: small table are best accessed through a FULL TABLE SCAN

• Consider keeping in memory

More Do’s and Don’ts

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• Use equi-joins whenever possible• Try not to use ‘not in’, !=, <>, not null, etc.• Optimizer has more choices to choose from

• Avoid complex expressions such as NVL(col1,0), TO_DATE(), TO_NUMBER()• They prevent the optimizer from assigning valid cardinality or selectivity estimates • Can affect the overall plan and the join methods

• Avoid joining complex views• May instantiate all views to run query against (reading too much data)• Querying views requires all tables from the view to be accessed

• If they aren’t required, then don’t use the view

• Use the partition key in the ‘WHERE’ clause if querying a partitioned table • Partition pruning will be used to reduce the amount of data read

Avoid Common Pitfalls

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• If you can hint it, baseline it (per Tom Kyte)• Alternative to using hints

• Hints difficult to manage over time• Once added, usually forgotten about

• Third-party software – can’t modify code• Example:Merge Join Cartesian > Nested Loop

select /* jg */ p.product_name

from order_items o, product p

where o.unit_price = :b1

and o.quantity > :b2

and o.product_id = p.product_id

and p.product_id = :b3;

What to Do If You Can't Change the Query

select /*+ USE_NL(o p) */ /* jg */ p.product_name

from order_items o, product p

where o.unit_price = :b1

and o.quantity > :b2

and o.product_id = p.product_id

and p.product_id = :b3;

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• Create baseline of original planAlter session set optimizer_capture_sql_plan_baselines = TRUE;• Or dbms_spm.load_plans_from_cursor_cache

• Example next slide

How to Change the Baseline

From cache Baseline

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• Manually run hinted query• Hint = /*+ USE_NL(p) +/

Change the Baseline – Cont.

Get SQL_ID, Plan hash value from cache Load from cache into baseline

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

SELECT c_last, c_first, c_street_1, c_city, c_state, c_zip,c_phone, o_entry_d, d_name, ol_delivery_d, ol_quantity, ol_amount

FROM order_line, orders, district, customer, stockWHERE o_id = ol_o_idAND o_c_id=c_idAND s_i_id = ol_i_idAND d_id = ol_d_idAND ol_w_id = :B2AND ol_d_id = :B4AND (ol_o_id < :B3 )AND ol_o_id >= (:B3 - 20)AND s_w_id = :B2AND s_quantity < :B1AND d_id = :B4AND c_last like :B5 ;

Another Case Study – Orders by Customer Last Name

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Review the Execution Plan

select * from table (dbms_xplan.display_cursor(null,null, format=> '+report'));

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• Stock:

Get Object Information

create index stock_idx

on stock

(s_i_id, s_w_id, s_quantity);

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• Orders:

Get Object Information

Actual Rows = 60,000

Find the Driving Table

orders

stock

district

100

130

1

.7%

.03

select count(*) from order_line

where ol_o_id < 200 and ol_o_id >= 200-20;

3941 / 600916 * 100 = .6558%

select avg(cnt) from (select c_last, count(*) cnt

from customer group by c_last);

20 / 60000 * 100 = .03333%

Filter on Stock: 3109 / 283000 * 100 = 1%

order_line

customerwarehouse

WHERE o_id = ol_o_id

AND o_c_id=c_id

AND s_i_id = ol_i_id

AND d_id = ol_d_id

AND ol_w_id = :B2

AND ol_d_id = :B4

AND (ol_o_id < :B3 )

AND ol_o_id >= (:B3 - 20)

AND s_w_id = :B2

AND s_quantity < :B1

AND d_id = :B4

AND c_last like :B5 ;

1

20

1

1

660092

10

1%

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

• create index stock_idx on stock (s_i_id, s_w_id, s_quantity);

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Engineer Out the Stupid

• create index orders_i2 on orders(o_id,o_c_id, o_entry_d);

Add Index on Orders to INCLUDE Customer

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Did Performance Improve?

Added Index on

Stock

Added Index on

Orders

• Make sure you are tuning the correct query• Use wait time analysis

• Understand the execution plan • Focus on the costly steps• Know what the optimizer knows

• Try these tuning techniques • SQL diagraming to find the best execution plan• Consider new fetch, approximate and partitioning features

• Engineer out the stupid• Be on the lookout for coding mistakes

• If you can’t change the code• Try using baselines or patches

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Summary

• Try Database Performance Analyzer FREE for 14 days• Improve root cause of slow performance

• Quickly identify root cause of issues that impact end-user response time• See historical trends over days, months, and years• Understand impact of VMware® performance • Agentless architecture with no dependence on Oracle Packs, installs in minutes

© 2017 SolarWinds Worldwide, LLC. All rights reserved.

Resolve performance issues quickly - free trial

www.solarwinds.com/dpa-download/

The SolarWinds, SolarWinds & Design, Orion, and THWACK trademarks are the exclusive

property of SolarWinds Worldwide, LLC or its affiliates, are registered with the U.S.

Patent and Trademark Office, and may be registered or pending registration in other

countries. All other SolarWinds trademarks, service marks, and logos may be common

law marks or are registered or pending registration. All other trademarks mentioned

herein are used for identification purposes only and are trademarks of (and may be

registered trademarks) of their respective companies.

Thank You!!!

Recommended