Sql query patterns, optimized

121
© 2014 PERCONA MySQL Query Patterns, Optimized Bill Karwin, Percona

description

Let's get into several common types of queries that developers struggle with, showing SQL solutions, and then analyze them for optimal efficiency. I'll cover Exclusion Join, Random Selection, Greatest-Per-Group, Dynamic Pivot, and Relational Division.

Transcript of Sql query patterns, optimized

Page 1: Sql query patterns, optimized

1

©  2014  PERCONA  

MySQL Query Patterns, Optimized

Bill Karwin, Percona

Page 2: Sql query patterns, optimized

1

©  2014  PERCONA  

Welcome Everybody! •  Bill Karwin

–  Senior Knowledge Manager in Percona Support –  Joined Percona in 2010

•  Percona offers MySQL services –  Consulting –  Support for MySQL and variants –  Remote DBA –  Development –  Training

2

Page 3: Sql query patterns, optimized

1

©  2014  PERCONA  

How Do We Optimize? •  Identify queries. •  Measure optimization plan and performance.

–  EXPLAIN –  SHOW SESSION STATUS –  SHOW PROFILES

•  Add indexes and/or redesign the query.

Page 4: Sql query patterns, optimized

1

©  2014  PERCONA  

Testing Environment •  MySQL 5.7.5-m15 •  CentOS 6.5 running under VirtualBox on my

Macbook Pro (non-SSD) •  MySQL Workbench 6.2

Page 5: Sql query patterns, optimized

1

©  2014  PERCONA  

Example Database

cast_info  

name  

char_name  

role_type  >tle  kind_type  

Page 6: Sql query patterns, optimized

1

©  2014  PERCONA  

Common Query Patterns 1.  Exclusion Joins 2.  Random Selection 3.  Greatest per Group 4.  Dynamic Pivot 5.  Relational Division

Page 7: Sql query patterns, optimized

1

©  2014  PERCONA  

EXCLUSION JOINS Query Patterns

Page 8: Sql query patterns, optimized

1

©  2014  PERCONA  

Assignment:

“I want to find recent movies that had no director.”

Page 9: Sql query patterns, optimized

1

©  2014  PERCONA  

Not Exists Solution SELECT t.title!FROM title t!WHERE kind_id = 1 !AND production_year >= 2005 !AND NOT EXISTS (! SELECT * FROM cast_info c! WHERE c.movie_id = t.id ! AND c.role_id = 8 /* director */!);!

Movies

In the range of recent years

Correlated subquery to find a director for each movie

Page 10: Sql query patterns, optimized

1

©  2014  PERCONA  

Not Exists Solution SELECT t.title!FROM title t!WHERE kind_id = 1 !AND production_year >= 2005 !AND NOT EXISTS (! SELECT * FROM cast_info c! WHERE c.movie_id = t.id ! AND c.role_id = 8!);!

???s

I gave up after waiting > 1 hour

Page 11: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Not-Exists Solution id   select_type   table   type   key   ref   rows   Extra  

1   PRIMARY   t   ALL   NULL   NULL   1598319   Using  where  

2   DEPENDENT  SUBQUERY  

c   ALL   NULL   NULL   24149504   Using  where  

The correlated subquery is executed 1.6 M times!

Both tables are table scans

And scans 24 M rows each time, totalling 3.8 × 1013 row comparisons

Dependent subquery executes once for each set of values in outer

Page 12: Sql query patterns, optimized

1

©  2014  PERCONA  

Indexes: the Not-Exists Solution CREATE INDEX k_py !ON title (kind_id, production_year);!!CREATE INDEX m_r!ON cast_info (movie_id, role_id);!

Page 13: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Not-Exists Solution id   select_type   table   type   key   ref   rows   Extra  

1   PRIMARY   t   range   k_py   NULL   189846   Using  index  condi>on;  Using  where  

2   DEPENDENT  SUBQUERY  

c   ref   m_r   t.id,    const  

3   Using  index  

The correlated subquery is executed 189k times!

At least both table references use indexes

A covering index is best—if the index fits in memory

Dependent subquery executes once for each set of values in outer

Page 14: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain in Workbench

Page 15: Sql query patterns, optimized

1

©  2014  PERCONA  

Not Exists Solution SELECT t.title!FROM title t!WHERE kind_id = 1 !AND production_year >= 2005 !AND NOT EXISTS (! SELECT * FROM cast_info c! WHERE c.movie_id = t.id ! AND c.role_id = 8!);!

5.34s

Better, but when the indexes aren’t in memory, it’s still too slow

Page 16: Sql query patterns, optimized

1

©  2014  PERCONA  

Buffer Pool •  It’s crucial that queries read an index from memory;

I/O during an index scan kills performance. [mysqld]!innodb_buffer_pool_size = 64M # wrong!innodb_buffer_pool_size = 2G # better!

Page 17: Sql query patterns, optimized

1

©  2014  PERCONA  

Not Exists Solution SELECT t.title!FROM title t!WHERE kind_id = 1 !AND production_year >= 2005 !AND NOT EXISTS (! SELECT * FROM cast_info c! WHERE c.movie_id = t.id ! AND c.role_id = 8!);!

3.25s

much faster after increasing size of

buffer pool

Page 18: Sql query patterns, optimized

1

©  2014  PERCONA  

SHOW SESSION STATUS •  Shows the real count of row accesses for your

current session. mysql> FLUSH STATUS;!mysql> ... run a query ...!mysql> SHOW SESSION STATUS LIKE 'Handler%';!

Page 19: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Not-Exists Solution +----------------------------+--------+!| Variable_name | Value |!+----------------------------+--------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 6 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 0 |!| Handler_read_key | 186489 |!| Handler_read_last | 0 |!| Handler_read_next | 93244 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 93244 |!| Handler_read_rnd_next | 0 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 0 |!+----------------------------+--------+!

read_key: lookup by index, e.g. each lookup in cast_info, plus the first row in title

read_next: advancing in index order, e.g. the range query for rows in title after the first row

read_rnd: fetch a row from a table based on a row reference (probably by MRR)

Page 20: Sql query patterns, optimized

1

©  2014  PERCONA  

SHOW PROFILE •  Enable query profiler for the current session.

mysql> SET PROFILING = 1;!

•  Run a query. mysql> SELECT t.title FROM title t ...!

•  Query the real execution time. mysql> SHOW PROFILES;!

•  Query detail for a specific query. mysql> SHOW PROFILE FOR QUERY 1;!

Page 21: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Not-Exists Solution +----------------+----------+!| Status | Duration |!+----------------+----------+!| Sending data | 0.000029 |!| executing | 0.000004 |!| Sending data | 0.000075 |!. . .!| executing | 0.000004 |!| Sending data | 0.000023 |!| executing | 0.000004 |!| Sending data | 0.000023 |!| executing | 0.000004 |!| Sending data | 0.000081 |!| executing | 0.000009 |!| Sending data | 0.000029 |!| end | 0.000007 |!| query end | 0.000022 |!| closing tables | 0.000028 |!| freeing items | 0.000344 |!| cleaning up | 0.000025 |!+----------------+----------+!

Thousands of iterations of correlated subqueries caused the profile information to overflow!

Page 22: Sql query patterns, optimized

1

©  2014  PERCONA  

Not-In Solution SELECT title!FROM title!WHERE kind_id = 1 !AND production_year >= 2005 !AND id NOT IN (! SELECT movie_id FROM cast_info! WHERE role_id = 8!);!

1.62s

Not a correlated subquery

Page 23: Sql query patterns, optimized

1

©  2014  PERCONA  

Indexes: the Not-In Solution CREATE INDEX k_py !ON title (kind_id, production_year);!!CREATE INDEX m_r!ON cast_info (movie_id, role_id);!

Page 24: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Not-In Solution id   select_type   table   type   key   ref   rows   Extra  

1   PRIMARY   >tle   range   k_py   NULL   189846   Using  index  condi>on;    Using  where;  Using  MRR  

1   DEPENDENT  SUBQUERY  

cast_info  

index_subquery   m_r   func,  const  

1   Using  index;  Using  where  

But somehow MySQL doesn’t report a different select type

picks only 1 row per subquery execution

Page 25: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 26: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Not-In Solution +----------------------------+--------+!| Variable_name | Value |!+----------------------------+--------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 6 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 0 |!| Handler_read_key | 186489 |!| Handler_read_last | 0 |!| Handler_read_next | 93244 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 93244 |!| Handler_read_rnd_next | 0 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 0 |!+----------------------------+--------+!

Page 27: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Not-In Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000128 |!| checking permissions | 0.000014 |!| checking permissions | 0.000012 |!| Opening tables | 0.000043 |!| init | 0.000059 |!| System lock | 0.000030 |!| optimizing | 0.000022 |!| statistics | 0.000127 |!| preparing | 0.000036 |!| optimizing | 0.000013 |!| statistics | 0.000028 |!| preparing | 0.000017 |!| executing | 0.000008 |!| Sending data | 1.623482 |!| end | 0.000024 |!| query end | 0.000025 |!| closing tables | 0.000020 |!| freeing items | 0.000329 |!| cleaning up | 0.000029 |!+----------------------+----------+!

Most of the time spent in “Sending data” (moving rows around)

Page 28: Sql query patterns, optimized

1

©  2014  PERCONA  

Outer-Join Solution SELECT t.title!FROM title t!LEFT OUTER JOIN cast_info c ! ON t.id = c.movie_id ! AND c.role_id = 8!WHERE t.kind_id = 1 !AND t.production_year >= 2005 !AND c.movie_id IS NULL;!

1.58s

Try to find a director for each movie using a join

If no director is found, that’s the one we want

Page 29: Sql query patterns, optimized

1

©  2014  PERCONA  

Indexes: the Outer-Join Solution CREATE INDEX k_py !ON title (kind_id, production_year);!!CREATE INDEX m_r!ON cast_info (movie_id, role_id);!

Page 30: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Outer-Join Solution id   select_type   table   type   key   ref   rows   Extra  

1   SIMPLE   t   range   k_py   NULL   189846   Using  index  condi>on  

1   SIMPLE   c   ref   m_r   t.id,    const  

1   Using  where;    Using  index;    Not  exists  

Special “not exists” optimization

Page 31: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 32: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Outer-Join Solution +----------------------------+-------+!| Variable_name | Value |!+----------------------------+-------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 4 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 0 |!| Handler_read_key | 93245 |!| Handler_read_last | 0 |!| Handler_read_next | 93244 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 0 |!| Handler_read_rnd_next | 0 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 0 |!+----------------------------+-------+!

Fewest row reads

Page 33: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Outer-Join Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000161 |!| checking permissions | 0.000014 |!| checking permissions | 0.000013 |!| Opening tables | 0.000051 |!| init | 0.000056 |!| System lock | 0.000040 |!| optimizing | 0.000000 |!| statistics | 0.000263 |!| preparing | 0.000064 |!| executing | 0.000012 |!| Sending data | 1.581615 |!| end | 0.000021 |!| query end | 0.000022 |!| closing tables | 0.000016 |!| freeing items | 0.000324 |!| cleaning up | 0.000026 |!+----------------------+----------+!

Page 34: Sql query patterns, optimized

1

©  2014  PERCONA  

Summary: Exclusion Joins Solu7on   Time   Notes  Not-­‐Exists   3.25s   dependent  subquery  Not-­‐In   1.62s   dependent  subquery  Outer-­‐Join   1.58s   “not  exists”  op>miza>on  

Page 35: Sql query patterns, optimized

1

©  2014  PERCONA  

RANDOM SELECTION Query Patterns

Page 36: Sql query patterns, optimized

1

©  2014  PERCONA  

Assignment:

“I want a query that picks a random movie.”

Page 37: Sql query patterns, optimized

1

©  2014  PERCONA  

Naïve Order-By Solution SELECT *!FROM title!WHERE kind_id = 1 /* movie */!ORDER BY RAND()!LIMIT 1;!

0.98s

Page 38: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Order-By Solution id   select_type   table   type   key   ref   rows   Extra  

1   SIMPLE   >tle   ref   k_py   const   790882   Using  temporary;  Using  filesort  

Page 39: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 40: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Order-By Solution +----------------------------+--------+!| Variable_name | Value |!+----------------------------+--------+!| Handler_commit | 2 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 4 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 0 |!| Handler_read_key | 2 |!| Handler_read_last | 0 |!| Handler_read_next | 947164 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 2 |!| Handler_read_rnd_next | 947166 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 947164 |!+----------------------------+--------+!

What is this?

Page 41: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Order-By Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000074 |!| checking permissions | 0.000032 |!| Opening tables | 0.000035 |!| System lock | 0.000012 |!| init | 0.000025 |!| optimizing | 0.000004 |!| statistics | 0.000014 |!| preparing | 0.000010 |!| Creating tmp table | 0.000245 |!| executing | 0.000003 |!| Copying to tmp table | 4.875666 |!| Sorting result | 3.871513 |!| Sending data | 0.000059 |!| end | 0.000005 |!| removing tmp table | 0.058239 |!| end | 0.000018 |!

| query end | 0.000064 |!| closing tables | 0.000034 |!| freeing items | 0.000210 |!| logging slow query | 0.000003 |!| cleaning up | 0.000005 |!+----------------------+----------+!

MySQL 5.5 saves to a temp table, then sorts by filesort.

Page 42: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Order-By Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000111 |!| checking permissions | 0.000015 |!| Opening tables | 0.000030 |!| init | 0.000045 |!| System lock | 0.000019 |!| optimizing | 0.000013 |!| statistics | 0.000106 |!| preparing | 0.000019 |!| Creating tmp table | 0.000050 |!| Sorting result | 0.000007 |!| executing | 0.000005 |!| Sending data | 0.836566 |!| Creating sort index | 0.145061 |!| end | 0.000018 |!| query end | 0.000021 |!| removing tmp table | 0.002427 |!

| query end | 0.000516 |!| closing tables | 0.000037 |!| freeing items | 0.000234 |!| cleaning up | 0.000023 |!+----------------------+----------+!

MySQL 5.7 creates a sort index for the temp table on the fly

Page 43: Sql query patterns, optimized

1

©  2014  PERCONA  

Offset Solution SELECT ROUND(RAND() * COUNT(*)) !FROM title!WHERE kind_id = 1;!!SELECT *!FROM title!WHERE kind_id = 1!LIMIT 1 OFFSET $random;!

0.45s

Page 44: Sql query patterns, optimized

1

©  2014  PERCONA  

Indexes: the Offset Solution CREATE INDEX k!ON title (kind_id);!

Page 45: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Offset Solution id   select_type   table   type   key   ref   rows   Extra  

1   SIMPLE   >tle   ref   k   const   787992  

Page 46: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 47: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Offset Solution +----------------------------+--------+!| Variable_name | Value |!+----------------------------+--------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 2 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 0 |!| Handler_read_key | 1 |!| Handler_read_last | 0 |!| Handler_read_next | 473582 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 0 |!| Handler_read_rnd_next | 0 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 0 |!+----------------------------+--------+!

Query must read OFFSET + COUNT rows. A high random value makes the query take longer.

Page 48: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Offset Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000099 |!| checking permissions | 0.000012 |!| Opening tables | 0.000034 |!| init | 0.000045 |!| System lock | 0.000019 |!| optimizing | 0.000013 |!| statistics | 0.000114 |!| preparing | 0.000021 |!| executing | 0.000005 |!| Sending data | 0.459230 |!| end | 0.000018 |!| query end | 0.000018 |!| closing tables | 0.000017 |!| freeing items | 0.000194 |!| cleaning up | 0.000025 |!+----------------------+----------+!

Many rows moving from storage layer to SQL layer, only to be discarded.

Page 49: Sql query patterns, optimized

1

©  2014  PERCONA  

Primary Key Solution SELECT ROUND(RAND() * MAX(id)) !FROM title!WHERE kind_id = 1;!!SELECT *!FROM title!WHERE kind_id = 1 AND id > $random!LIMIT 1;!

0.0008s

Page 50: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Primary Key Solution id   select_type   table   type   key   ref   rows   Extra  

1   SIMPLE   >tle   ref   k   NULL   268254   Using  index  condi>on  

Strange that the optimizer estimates this many rows

Page 51: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 52: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Primary Key Solution +----------------------------+-------+!| Variable_name | Value |!+----------------------------+-------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 2 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 0 |!| Handler_read_key | 1 |!| Handler_read_last | 0 |!| Handler_read_next | 0 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 0 |!| Handler_read_rnd_next | 0 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 0 |!+----------------------------+-------+!

Just one row read after the index lookup.

Page 53: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Primary Key Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000119 |!| checking permissions | 0.000016 |!| Opening tables | 0.000036 |!| init | 0.000053 |!| System lock | 0.000027 |!| optimizing | 0.000021 |!| statistics | 0.000180 |!| preparing | 0.000112 |!| executing | 0.000016 |!| Sending data | 0.000175 |!| end | 0.000010 |!| query end | 0.000017 |!| closing tables | 0.000015 |!| freeing items | 0.000028 |!| cleaning up | 0.000028 |!+----------------------+----------+!

Everything is fast when we search a index by value rather than by position.

Page 54: Sql query patterns, optimized

1

©  2014  PERCONA  

Summary: Random Selection Solu7on   Time   Notes  Order-­‐By  Solu>on   0.98s  Offset  Solu>on   0.45s   Requires  the  COUNT()  Primary  Key  Solu>on   0.0008s   Requires  the  MAX()  

Page 55: Sql query patterns, optimized

1

©  2014  PERCONA  

GREATEST PER GROUP Query Patterns

Page 56: Sql query patterns, optimized

1

©  2014  PERCONA  

Assignment:

“I want the last episode of every TV series.”

Page 57: Sql query patterns, optimized

1

©  2014  PERCONA  

Getting the Last Episode SELECT tv.title, ep.title, MAX(ep.episode_nr) AS last_ep !FROM title ep!JOIN title tv ON tv.id = ep.episode_of_id!WHERE ep.kind_id = 7 /* TV show */!GROUP BY ep.episode_of_id ORDER BY NULL;!

This is not the title of the last episode!

Page 58: Sql query patterns, optimized

1

©  2014  PERCONA  

Why Isn’t It? •  The query doesn’t necessarily return the title from

the row where MAX(ep.episode_nr) occurs. •  Should the following return the title of the first

episode or the last episode? SELECT tv.title, ep.title, MIN(ep.episode_nr) AS first_ep ! MAX(ep.episode_nr) AS last_ep !FROM . . .!

Page 59: Sql query patterns, optimized

1

©  2014  PERCONA  

Exclusion Join Solution SELECT tv.title, ep1.title, ep1.episode_nr!FROM title ep1!LEFT OUTER JOIN title ep2 ! ON ep1.kind_id = ep2.kind_id! AND ep1.episode_of_id = ep2.episode_of_id! AND ep1.episode_nr < ep2.episode_nr!JOIN title tv ON tv.id = ep1.episode_of_id!WHERE ep1.kind_id = 7 ! AND ep1.episode_of_id IS NOT NULL! AND ep1.episode_nr >= 1! AND ep2.episode_of_id IS NULL;!

71.20

Try to find a row ep2 for the same show with a greater episode_nr

If no such row is found, then ep1 must be the last episode for the show

Page 60: Sql query patterns, optimized

1

©  2014  PERCONA  

Indexes: the Exclusion-Join Solution CREATE INDEX k_ep_nr !ON title (kind_id, episode_of_id, episode_nr);!

Page 61: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Exclusion-Join Solution id   select_type   table   type   key   ref   rows   Extra  

1   SIMPLE   ep1   ref   k_py   const   787992   Using  where  

1   SIMPLE   tv   eq_ref   PRIMARY   ep1.episode_of_id   1  

1   SIMPLE   ep2   ref   k_ep_nr   const,  ep1.episode_of_id  

22   Using  where;  Using  index  

Page 62: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 63: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Exclusion-Join Solution +----------------------------+-----------+!| Variable_name | Value |!+----------------------------+-----------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 6 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 0 |!| Handler_read_key | 693046 |!| Handler_read_last | 0 |!| Handler_read_next | 254373071 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 0 |!| Handler_read_rnd_next | 0 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 0 |!+----------------------------+-----------+!

Unfortunately, this seems to be O(n2)

Page 64: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Exclusion-Join Solution +----------------------+-----------+!| Status | Duration |!+----------------------+-----------+!| starting | 0.000147 |!| checking permissions | 0.000009 |!| checking permissions | 0.000004 |!| checking permissions | 0.000009 |!| Opening tables | 0.000038 |!| init | 0.000049 |!| System lock | 0.000032 |!| optimizing | 0.000025 |!| statistics | 0.000195 |!| preparing | 0.000045 |!| executing | 0.000006 |!| Sending data | 71.195693 |!| end | 0.000021 |!| query end | 0.000021 |!| closing tables | 0.000017 |!| freeing items | 0.000864 |!| logging slow query | 0.000135 |!| cleaning up | 0.000027 |!+----------------------+-----------+!

A lot of time is spent moving rows around

Page 65: Sql query patterns, optimized

1

©  2014  PERCONA  

Derived-Table Solution SELECT tv.title, ep.title, ep.episode_nr!FROM (! SELECT kind_id, episode_of_id, ! MAX(episode_nr) AS episode_nr! FROM title! WHERE kind_id = 7! GROUP BY kind_id, episode_of_id!) maxep!JOIN title ep USING (kind_id, episode_of_id, episode_nr)!JOIN title tv ON tv.id = ep.episode_of_id;!

0.60s

Generate a list of the greatest episode number per show

Page 66: Sql query patterns, optimized

1

©  2014  PERCONA  

Indexes: the Derived-Table Solution CREATE INDEX k_ep_nr !ON title (kind_id, episode_of_id, episode_nr);!

Page 67: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Derived-Table Solution id   select_type   table   type   key   ref   rows   Extra  

1   PRIMARY   <derived2>   ALL   NULL   NULL   751752   Using  where  

1   PRIMARY   tv   eq_ref   PRIMARY   maxep.episode_of_id   1   NULL  

1   PRIMARY   ep   ref   k_ep_nr   maxep.kind_id,  maxep.episode_id,  maxep.episode_nr  

2   NULL  

2   DERIVED   >tle   range   k_ep_nr   NULL   24544   Using  where;    Using  index  for  group-­‐by  

Page 68: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 69: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Derived-Table Solution +----------------------------+--------+!| Variable_name | Value |!+----------------------------+--------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 6 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 0 |!| Handler_read_key | 110312 |!| Handler_read_last | 1 |!| Handler_read_next | 28989 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 0 |!| Handler_read_rnd_next | 30324 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 30323 |!+----------------------------+--------+!

Evidence of a temporary table, even though EXPLAIN didn’t report it

Page 70: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Derived-Table Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000600 |!| checking permissions | 0.000013 |!| checking permissions | 0.000007 |!| checking permissions | 0.000009 |!| Opening tables | 0.000111 |!| init | 0.000037 |!| System lock | 0.000027 |!| optimizing | 0.000006 |!| optimizing | 0.000011 |!| statistics | 0.000196 |!| preparing | 0.000030 |!| Sorting result | 0.000012 |!| statistics | 0.000057 |!| preparing | 0.000023 |!| executing | 0.000016 |!| Sending data | 0.000010 |!

| executing | 0.000004 |!| Sending data | 0.607434 |!| end | 0.000020 |!| query end | 0.000028 |!| closing tables | 0.000005 |!| removing tmp table | 0.000014 |!| closing tables | 0.000070 |!| freeing items | 0.000235 |!| cleaning up | 0.000029 |!+----------------------+----------+!

Evidence of a temporary table, even though EXPLAIN didn’t report it

Page 71: Sql query patterns, optimized

1

©  2014  PERCONA  

Summary: Greatest per Group Solu7on   Time   Notes  Exclusion-­‐join  solu>on   71.20   Bad  when  each  group  has  

many  entries.  Derived-­‐table  solu>on   0.60s  

Page 72: Sql query patterns, optimized

1

©  2014  PERCONA  

DYNAMIC PIVOT Query Patterns

Page 73: Sql query patterns, optimized

1

©  2014  PERCONA  

Assignment:

“I want the count of movies, TV, and video games per year—in columns.”

Page 74: Sql query patterns, optimized

1

©  2014  PERCONA  

Not Like This SELECT k.kind, t.production_year, COUNT(*) AS Count!FROM kind_type k!JOIN title t ON k.id = t.kind_id !WHERE production_year BETWEEN 2005 AND 2009!GROUP BY k.id, t.production_year;!!+-------------+-----------------+--------+!| kind | production_year | Count |!+-------------+-----------------+--------+!| movie | 2005 | 13807 |!| movie | 2006 | 13916 |!| movie | 2007 | 14494 |!| movie | 2008 | 18354 |!| movie | 2009 | 23714 |!| tv series | 2005 | 3248 |!| tv series | 2006 | 3588 |!| tv series | 2007 | 3361 |!| tv series | 2008 | 3026 |!| tv series | 2009 | 2572 |!

Page 75: Sql query patterns, optimized

1

©  2014  PERCONA  

Like This +----------------+-----------+-----------+-----------+-----------+-----------+!| kind | Count2005 | Count2006 | Count2007 | Count2008 | Count2009 |!+----------------+-----------+-----------+-----------+-----------+-----------+!| episode | 36138 | 24745 | 22335 | 16448 | 12917 |!| movie | 13807 | 13916 | 14494 | 18354 | 23714 |!| tv movie | 3541 | 3561 | 3586 | 3025 | 2778 |!| tv series | 3248 | 3588 | 3361 | 3026 | 2572 |!| video game | 383 | 367 | 310 | 300 | 215 |!| video movie | 7693 | 7671 | 6955 | 5808 | 4090 |!+----------------+-----------+-----------+-----------+-----------+-----------+!

Page 76: Sql query patterns, optimized

1

©  2014  PERCONA  

Do It in One Pass SELECT k.kind,! SUM(production_year=2005) AS Count2005, ! SUM(production_year=2006) AS Count2006, ! SUM(production_year=2007) AS Count2007, ! SUM(production_year=2008) AS Count2008, ! SUM(production_year=2009) AS Count2009 !FROM title t!JOIN kind_type k ON k.id = t.kind_id!GROUP BY t.kind_id ORDER BY NULL;!!

0.77s

SUM of 1’s = COUNT where condition is true

Page 77: Sql query patterns, optimized

1

©  2014  PERCONA  

Indexes: the One-Pass Solution CREATE INDEX k_py !ON title (kind_id, production_year);!

Page 78: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the One-Pass Solution id   select_type   table   type   key   ref   rows   Extra  

1   SIMPLE   k   index   kind   NULL   7   Using  index;  Using  temporary  

1   SIMPLE   t   ref   k_py   k.id   167056   Using  index  

reading title table second unfortunately causes the group by to create a temp table

Page 79: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 80: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the One-Pass Solution +----------------------------+---------+!| Variable_name | Value |!+----------------------------+---------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 4 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 1 |!| Handler_read_key | 1543727 |!| Handler_read_last | 0 |!| Handler_read_next | 1543726 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 0 |!| Handler_read_rnd_next | 7 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 1543713 |!| Handler_write | 6 |!+----------------------------+---------+!

title table has 1.5M rows; that’s how many times it increments counts in the temp table

Page 81: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the One-Pass Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000162 |!| checking permissions | 0.000034 |!| checking permissions | 0.000011 |!| Opening tables | 0.000043 |!| init | 0.000052 |!| System lock | 0.000030 |!| optimizing | 0.000017 |!| statistics | 0.000112 |!| preparing | 0.000025 |!| Creating tmp table | 0.000069 |!| executing | 0.000009 |!| Sending data | 0.772317 |!| end | 0.000025 |!| query end | 0.000022 |!| removing tmp table | 0.000036 |!| query end | 0.000007 |!

| closing tables | 0.000018 |!| freeing items | 0.000485 |!| cleaning up | 0.000030 |!+----------------------+----------+!

majority of time spent building temp table

Page 82: Sql query patterns, optimized

1

©  2014  PERCONA  

One-Pass with Straight-Join Optimizer Override

SELECT STRAIGHT_JOIN k.kind,! SUM(production_year=2005) AS Count2005, ! SUM(production_year=2006) AS Count2006, ! SUM(production_year=2007) AS Count2007, ! SUM(production_year=2008) AS Count2008, ! SUM(production_year=2009) AS Count2009 !FROM title t!JOIN kind_type k ON k.id = t.kind_id!GROUP BY t.kind_id ORDER BY NULL;!!

7.18s

Page 83: Sql query patterns, optimized

1

©  2014  PERCONA  

Indexes: the Straight-Join Solution CREATE INDEX k_py !ON title (kind_id, production_year);!

Page 84: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Straight-Join Solution id   select_type   table   type   key   ref   rows   Extra  

1   SIMPLE   t   index   k_py   NULL   1537429   Using  index  

1   SIMPLE   k   eq_ref   PRIMARY   t.kind_id   1  

no "Using temporary" because forcing title table to be read first means it scans the index in index order, avoiding the temp table—in this case

Page 85: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 86: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Straight-Join Solution +----------------------------+---------+!| Variable_name | Value |!+----------------------------+---------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 4 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 1 |!| Handler_read_key | 7 |!| Handler_read_last | 0 |!| Handler_read_next | 1543719 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 0 |!| Handler_read_rnd_next | 0 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 0 |!+----------------------------+---------+!!

really one-pass

Page 87: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Straight-Join Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000172 |!| checking permissions | 0.000176 |!| checking permissions | 0.000012 |!| Opening tables | 0.000169 |!| init | 0.000067 |!| System lock | 0.000034 |!| optimizing | 0.000018 |!| statistics | 0.000055 |!| preparing | 0.000036 |!| Sorting result | 0.000016 |!| executing | 0.000007 |!| Sending data | 7.277106 |!| end | 0.000022 |!| query end | 0.000024 |!| closing tables | 0.000019 |!| freeing items | 0.000236 |!

| cleaning up | 0.000026 |!+----------------------+----------+!

majority of time spent just moving rows

no temporary table!

Page 88: Sql query patterns, optimized

1

©  2014  PERCONA  

Scalar Subquery Solution SELECT k.kind,!(SELECT COUNT(*) FROM title WHERE kind_id = k.id AND production_year = 2005) AS Count2005,!(SELECT COUNT(*) FROM title WHERE kind_id = k.id AND production_year = 2006) AS Count2006,!(SELECT COUNT(*) FROM title WHERE kind_id = k.id AND production_year = 2007) AS Count2007,!

(SELECT COUNT(*) FROM title WHERE kind_id = k.id AND production_year = 2008) AS Count2008,!(SELECT COUNT(*) FROM title WHERE kind_id = k.id AND production_year = 2009) AS Count2009!FROM kind_type k;!

0.001

Page 89: Sql query patterns, optimized

1

©  2014  PERCONA  

Indexes: the Scalar Subquery Solution CREATE INDEX k_py !ON title (kind_id, production_year)!!CREATE UNIQUE INDEX kind !ON kind_type (kind);!

Page 90: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Scalar Subquery Solution id   select_type   table   type   key   ref   rows   Extra  

1   PRIMARY   k   index   kind   NULL   7   Using  index  

6   DEPENDENT  SUBQUERY  

>tle   ref   k_py   k.id,    const  

1781   Using  index  

5   DEPENDENT  SUBQUERY  

>tle   ref   k_py   k.id,    const  

1781   Using  index  

4   DEPENDENT  SUBQUERY  

>tle   ref   k_py   k.id,    const  

1781   Using  index  

3   DEPENDENT  SUBQUERY  

>tle   ref   k_py   k.id,    const  

1781   Using  index  

2   DEPENDENT  SUBQUERY  

>tle   ref   k_py   k.id,    const  

1781   Using  index  

Page 91: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 92: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Scalar Subquery Solution +----------------------------+--------+!| Variable_name | Value |!+----------------------------+--------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 12 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 1 |!| Handler_read_key | 36 |!| Handler_read_last | 0 |!| Handler_read_next | 262953 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 0 |!| Handler_read_rnd_next | 0 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 0 |!+----------------------------+--------+!

good use of indexes

Page 93: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Scalar Subquery Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| checking permissions | 0.000005 |!| checking permissions | 0.000010 |!| Opening tables | 0.000143 |!| init | 0.000071 |!| System lock | 0.000047 |!| optimizing | 0.000007 |!| statistics | 0.000022 |!| preparing | 0.000020 |!| executing | 0.000005 |!| Sending data | 0.000620 |!. . .!| executing | 0.000013 |!| Sending data | 0.001620 |!| executing | 0.000008 |!| Sending data | 0.000978 |!| executing | 0.000010 |!| Sending data | 0.000384 |!| end | 0.000011 |!| query end | 0.000027 |!| closing tables | 0.000019 |!| freeing items | 0.000460 |!| cleaning up | 0.000030 |!+----------------------+----------+!

scary long profile, but still fast

Page 94: Sql query patterns, optimized

1

©  2014  PERCONA  

Summary: Dynamic Pivot Solu7on   Time   Notes  One-­‐pass  solu>on   0.77s  Straight-­‐join  solu>on   7.18s   was  much  bejer  in  5.5  Scalar  Subquery  solu>on   0.001s  

Page 95: Sql query patterns, optimized

1

©  2014  PERCONA  

RELATIONAL DIVISION Query Patterns

Page 96: Sql query patterns, optimized

1

©  2014  PERCONA  

Assignment:

“I want to see movies with all three of keywords espionage, nuclear-bomb,

and ejector-seat.”

Page 97: Sql query patterns, optimized

1

©  2014  PERCONA  

Not Movies with One Keyword SELECT t.title, k.keyword FROM keyword k !JOIN movie_keyword mk ON k.id = mk.keyword_id !JOIN title t ON mk.movie_id = t.id !

WHERE k.keyword IN ('espionage', 'nuclear-bomb', 'ejector-seat');!!+--------------------------+--------------+!| title | keyword |!

+--------------------------+--------------+!| 2 Fast 2 Furious | ejector-seat |!| Across the Pacific | espionage |!| Action in Arabia | espionage |!

. . .!| You Only Live Twice | espionage |!| Zombie Genocide | nuclear-bomb |!

| Zombies of the Strat | espionage |!+--------------------------+--------------+!705 rows in set (12.97 sec)!

Page 98: Sql query patterns, optimized

1

©  2014  PERCONA  

This Won’t Work SELECT t.title, k.keyword !FROM keyword k !JOIN movie_keyword mk ON k.id = mk.keyword_id !

JOIN title t ON mk.movie_id = t.id !WHERE k.keyword = 'espionage'! AND k.keyword = 'nuclear-bomb'! AND k.keyword = 'ejector-seat';!!0 rows in set (12.97 sec)!

!

It’s impossible for one column to have three values on a given row

Page 99: Sql query patterns, optimized

1

©  2014  PERCONA  

Only Movies with All Three +------------+-------------------------------------+!| title | keywords |!+------------+-------------------------------------+!| Goldfinger | ejector-seat,espionage,nuclear-bomb |!+------------+-------------------------------------+!

Page 100: Sql query patterns, optimized

1

©  2014  PERCONA  

Group-by Solution SELECT t.title, GROUP_CONCAT(k.keyword) AS keywords!FROM title t !JOIN movie_keyword mk ON t.id = mk.movie_id !JOIN keyword k ON k.id = mk.keyword_id!WHERE k.keyword IN ! ('espionage', 'nuclear-bomb', 'ejector-seat')!GROUP BY mk.movie_id!HAVING COUNT(DISTINCT mk.keyword_id) = 3 !ORDER BY NULL;!!

0.02s

Page 101: Sql query patterns, optimized

1

©  2014  PERCONA  

Indexes CREATE INDEX k_i!ON keyword (keyword, id);!!CREATE INDEX k_m!ON movie_keyword (keyword_id, movie_id);!

Page 102: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Group-by Solution id   select_type   table   type   key   ref   rows   Extra  

1   SIMPLE   k   range   k_i   NULL   3   Using  where;    Using  index;    Using  temporary;    Using  filesort    

1   SIMPLE   mk   ref   k_m   k.id   23   Using  index  

1   SIMPLE   t   eq_ref   PRIMARY   mk.movie_id   1   NULL  

Page 103: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 104: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Group-by Solution +----------------------------+-------+!| Variable_name | Value |!+----------------------------+-------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 6 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 0 |!| Handler_read_key | 710 |!| Handler_read_last | 0 |!| Handler_read_next | 708 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 705 |!| Handler_read_rnd_next | 706 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 705 |!+----------------------------+-------+!

building and reading a temporary table

Page 105: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Group-by Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000267 |!

| checking permissions | 0.000010 |!| checking permissions | 0.000004 |!| checking permissions | 0.000008 |!

| Opening tables | 0.000041 |!| init | 0.000078 |!| System lock | 0.000037 |!| optimizing | 0.000024 |!

| statistics | 0.000166 |!| preparing | 0.000028 |!| Creating tmp table | 0.000199 |!

| Sorting result | 0.000013 |!| executing | 0.000005 |!| Sending data | 0.009532 |!

| Creating sort index | 0.010310 |!| end | 0.000066 |!| query end | 0.000027 |!| removing tmp table | 0.000176 |!

| query end | 0.000021 |!| removing tmp table | 0.000010 |!| query end | 0.000006 |!

| closing tables | 0.000022 |!| freeing items | 0.000016 |!| removing tmp table | 0.000007 |!| freeing items | 0.000009 |!

| removing tmp table | 0.000005 |!| freeing items | 0.000449 |!| cleaning up | 0.000028 |!

+----------------------+----------+!

building & tearing down temp table

Page 106: Sql query patterns, optimized

1

©  2014  PERCONA  

Self-Join Solution SELECT t.title, CONCAT_WS(',', k1.keyword, k2.keyword, k3.keyword) AS keywords !FROM title t !JOIN movie_keyword mk1 ON t.id = mk1.movie_id !JOIN keyword k1 ON k1.id = mk1.keyword_id !JOIN movie_keyword mk2 ON mk1.movie_id= mk2.movie_id !JOIN keyword k2 ON k2.id = mk2.keyword_id !JOIN movie_keyword mk3 ON mk1.movie_id = mk3.movie_id !JOIN keyword k3 ON k3.id = mk3.keyword_id !WHERE (k1.keyword, k2.keyword, k3.keyword) ! = ('espionage', 'nuclear-bomb', 'ejector-seat');!

0.015s

Page 107: Sql query patterns, optimized

1

©  2014  PERCONA  

EXPLAIN: the Self-Join Solution id   select_type   table   type   key   ref   rows   Extra  

1   SIMPLE   k1   ref   k_i   const   1   Using  index  

1   SIMPLE   k2   ref   k_i   const   1   Using  index  

1   SIMPLE   k3   ref   k_i    

const   1   Using  index  

1   SIMPLE   mk1   ref   k_m   k1.id   17   Using  index  

1   SIMPLE   t   eq_ref   PRIMARY   mk1.movie_id   1   NULL  

1   SIMPLE   mk2   ref   k_m   k2.id,    mk1.movie_id  

1   Using  index  

1   SIMPLE   mk3   ref   k_m   k3.id,  mk1.movie_id  

1   Using  index  

Page 108: Sql query patterns, optimized

1

©  2014  PERCONA  

Visual Explain

Page 109: Sql query patterns, optimized

1

©  2014  PERCONA  

Status: the Self-Join Solution +----------------------------+-------+!| Variable_name | Value |!+----------------------------+-------+!| Handler_commit | 1 |!| Handler_delete | 0 |!| Handler_discover | 0 |!| Handler_external_lock | 14 |!| Handler_mrr_init | 0 |!| Handler_prepare | 0 |!| Handler_read_first | 0 |!| Handler_read_key | 1218 |!| Handler_read_last | 0 |!| Handler_read_next | 613 |!| Handler_read_prev | 0 |!| Handler_read_rnd | 0 |!| Handler_read_rnd_next | 0 |!| Handler_rollback | 0 |!| Handler_savepoint | 0 |!| Handler_savepoint_rollback | 0 |!| Handler_update | 0 |!| Handler_write | 0 |!+----------------------------+-------+!

minimal rows, good index usage

Page 110: Sql query patterns, optimized

1

©  2014  PERCONA  

Profile: the Self-Join Solution +----------------------+----------+!| Status | Duration |!+----------------------+----------+!| starting | 0.000205 |!| checking permissions | 0.000011 |!| checking permissions | 0.000006 |!| checking permissions | 0.000005 |!| checking permissions | 0.000005 |!| checking permissions | 0.000005 |!| checking permissions | 0.000005 |!| checking permissions | 0.000010 |!| Opening tables | 0.000061 |!| init | 0.000069 |!| System lock | 0.000056 |!| optimizing | 0.000029 |!| statistics | 0.000106 |!| preparing | 0.000049 |!| executing | 0.000008 |!| Sending data | 0.013485 |!

| end | 0.000018 |!| query end | 0.000022 |!| closing tables | 0.000020 |!| freeing items | 0.000683 |!| cleaning up | 0.000034 |!+----------------------+----------+!

who says joins are slow?

Page 111: Sql query patterns, optimized

1

©  2014  PERCONA  

Summary: Relational Division Solu7on   Time   Notes  Group-­‐by  solu>on   0.02s   much  improved  in  5.7  Self-­‐join  solu>on   0.015s  

Page 112: Sql query patterns, optimized

1

©  2014  PERCONA  

PERFORMANCE SCHEMA Query Patterns

Page 113: Sql query patterns, optimized

1

©  2014  PERCONA  

Performance Schema •  New tools to instrument and profile queries.

–  Use PERFORMANCE_SCHEMA with MySQL 5.6+

•  SHOW PROFILES is deprecated in 5.6.7+ and will be removed in a future release

Page 114: Sql query patterns, optimized

1

©  2014  PERCONA  

Sys Schema •  Get the MySQL-sys schema for handy views,

functions, and procedures. –  https://github.com/MarkLeith/mysql-sys

Page 115: Sql query patterns, optimized

1

©  2014  PERCONA  

Sys Schema Caveats •  However, it’s still incomplete and tricky…

–  Installation fails; the views reference some P_S tables that don’t exist yet as of MySQL 5.7.5 (e.g. memory_%).

–  Documentation includes at least one sys procedure that isn’t in the download (ps_trace_statement_digest()).

•  We all need to use it and give feedback to Mark!

Page 116: Sql query patterns, optimized

1

©  2014  PERCONA  

Sys Schema Setup mysql> SOURCE sys_57.sql;!!mysql> CALL sys.ps_setup_enable_instrument('stage/%');!

+-------------------------+!| summary |!+-------------------------+!| Enabled 108 instruments |!+-------------------------+!!

mysql> CALL sys.ps_truncate_all_tables(false);!+---------------------+!| summary |!+---------------------+!| Truncated 31 tables |!+---------------------+!

!

Page 117: Sql query patterns, optimized

1

©  2014  PERCONA  

Statement Analysis mysql> select * from sys.statement_analysis limit 1\G!*************************** 1. row ***************************! query: SELECT `t` . `title` FROM `ti ... id` AND `c` . `role_id` = ? ) ! db: imdb! full_scan: ! exec_count: 1! err_count: 0! warn_count: 0! total_latency: 2.27 s! max_latency: 2.27 s! avg_latency: 2.27 s! lock_latency: 296.00 us! rows_sent: 6056! rows_sent_avg: 6056! rows_examined: 180432!rows_examined_avg: 180432! tmp_tables: 0! tmp_disk_tables: 0! rows_sorted: 0!sort_merge_passes: 0! digest: 4f8e3695ecf7c8518a1e1defe2ff323c! first_seen: 2014-09-28 02:21:21! last_seen: 2014-09-28 02:21:21!

similar to pt-query-digest output with Percona’s verbose slow query log

Page 118: Sql query patterns, optimized

1

©  2014  PERCONA  

SHOW PROFILE Workalike mysql> select * from sys.x$user_summary_by_stages;!+------+--------------------------------+-------+---------------+-----------+!| user | event_name | total | wait_sum | wait_avg |!+------+--------------------------------+-------+---------------+-----------+!| root | stage/sql/Sending data | 93246 | 2053496638000 | 22022000 |!| root | stage/sql/executing | 93247 | 214310855000 | 2298000 |!| root | stage/sql/System lock | 27 | 6200477000 | 229647000 |!| root | stage/sql/Opening tables | 126 | 3940994000 | 31277000 |!| root | stage/sql/checking permissions | 31 | 2309620000 | 74503000 |!| root | stage/sql/closing tables | 126 | 697965000 | 5539000 |!| root | stage/sql/statistics | 3 | 452872000 | 150957000 |!| root | stage/sql/freeing items | 2 | 421142000 | 210571000 |!| root | stage/sql/query end | 126 | 384577000 | 3052000 |!| root | stage/sql/starting | 2 | 271533000 | 135766000 |!| root | stage/sql/preparing | 3 | 119699000 | 39899000 |!| root | stage/sql/init | 3 | 82758000 | 27586000 |!| root | stage/sql/optimizing | 4 | 49758000 | 12439000 |!| root | stage/sql/removing tmp table | 1 | 8568000 | 8568000 |!| root | stage/sql/cleaning up | 2 | 7443000 | 3721000 |!| root | stage/sql/Sorting result | 1 | 7039000 | 7039000 |!| root | stage/sql/end | 2 | 6986000 | 3493000 |!+------+--------------------------------+-------+---------------+-----------+!

totals for all queries by user, not just in current session

Page 119: Sql query patterns, optimized

1

©  2014  PERCONA  

CONCLUSIONS Query Patterns

Page 120: Sql query patterns, optimized

1

©  2014  PERCONA  

Conclusions •  Use all tools to measure query performance

–  EXPLAIN –  Session Status –  Query Profiler –  Performance Schema and Sys Schema

•  Test with real-world data, because the best solution depends on the volume of data you’re querying.

•  Allocate enough memory to buffers so the indexes you need stay resident in RAM.

Page 121: Sql query patterns, optimized

1

©  2014  PERCONA  

License and Copyright Copyright 2014 Percona

Released under a Creative Commons 3.0 License: http://creativecommons.org/licenses/by-nc-nd/3.0/

You are free to share—to copy, distribute and transmit this work, under the following conditions:

Attribution. ���You must attribute this work to

Percona

Noncommercial. ���You may not use this work for

commercial purposes.

No Derivative Works. ���You may not alter, transform, or build

upon this work.