EXPLAIN FORMAT=JSON
Why to use?
April, 19, 2016
Sveta Smirnova
•The appearance
•Innovations
•Grouping and ordering
•Subqueries and unions
•Troubleshooting sugar
Table of Contents
2
• Has nice structured view
• Easily machine-readable
• Allows MySQL Workbench draw nice
graphs
EXPLAIN FORMAT=JSON was introduced in 5.6
3
• It has more information than regular
EXPLAIN
• Can replace OPTIMIZER TRACE for some
cases
Trendy? - Not only!
4
Innovations
5
• According to user manual
• The percentage of rows that were actually
needed, against the equal or bigger number
of resolved rows.
• Is this good or bad?
What number of ”filtered” rows mean?
6
• According to user manual
• Example query
mysql> select * from Country where Name=’Russian Federation’G
*************************** 1. row ***************************
Code: RUS
Name: Russian Federation
Continent: Europe
...
HeadOfState: Vladimir Putin
Capital: 3580
Code2: RU
1 row in set (0.00 sec)
What number of ”filtered” rows mean?
6
• According to user manual
• Example query
• Regular EXPLAIN
mysql> explain select * from Country where Name=’Russian Federation’;
+-----+------+----------+-------------+
| ... | rows | filtered | Extra |
+-----+------+----------+-------------+
| ... | 239 | 10.00 | Using where |
+-----+------+----------+-------------+
• What happened with rows?
• Why they were filtered?
What number of ”filtered” rows mean?
6
• According to user manual
• Example query
• Regular EXPLAIN
• EXPLAIN FORMAT=JSON has the answer
"table": {
"table_name": "Country",
"access_type": "ALL",
"rows_examined_per_scan": 239,
"rows_produced_per_join": 23,
"filtered": "10.00",
What number of ”filtered” rows mean?
6
• Covered indexes
• Usually use more than one column
• Contain all fields, necessary to resolve the
query
When to use covered indexes?
7
• Covered indexes
• Example
mysql> select count(*) from Country
-> where Continent=’Africa’ and Population > 1000000;
+----------+
| count(*) |
+----------+
| 47 |
+----------+
1 row in set (0,00 sec)
• Will the query use Primary Key?
• Or * will look through all rows?
When to use covered indexes?
7
• Covered indexes
• Example
• EXPLAIN FORMAT=JSON has the answer
mysql> explain format=json select count(*) from Country
-> where Continent=’Africa’ and Population > 1000000G
*************************** 1. row ***************************
...
"used_columns": [
"Continent",
"Population"
],
...
When to use covered indexes?
7
• Covered indexes
• Example
• EXPLAIN FORMAT=JSON has the answer
• We only need Continent and Population
columns for the index
When to use covered indexes?
7
• Example queries
SELECT first_name, last_name FROM employees WHERE
first_name=’Steve’;
...first_name=’Steve’ and last_name like ’V%’;
...first_name=’Steve’ and last_name like ’V%’ and hire_date>’1990-01-01’;
Which part of index used?
8
• Example queries
• Candidate indexes
CREATE INDEX comp1(first_name);
CREATE INDEX comp2(first_name, last_name);
CREATE INDEX comp3(first_name, last_name, hire_date);
• Last one seem to fit all the queries
Which part of index used?
8
• Example queries
• Candidate indexes
• Index effectiveness
mysql> explain format=json SELECT first_name, last_name FROM employees
-> WHERE first_name=’Steve’ and last_name like ’V%’
-> and hire_date > ’1990-01-01’G
*************************** 1. row ***************************
EXPLAIN: {
...
"used_key_parts": [
"first_name",
"last_name"
],
Which part of index used?
8
• Example queries
• Candidate indexes
• Index effectiveness
• Column hire date is not used!
• Most effective index would be on
(first name, last name)
Which part of index used?
8
• Use case
• Table has two or more indexes
• All can be used to resolve the query
• But only one is chosen
• Why?
Why index chosen?
9
• Use case
• Example
• Table ‘titles‘ in ‘employees‘ database
• The query:
select distinct title from titles where year(from_date) > ’1990’;
• Two indexes:
PRIMARY KEY (‘emp no‘,‘title‘,‘from date‘)
KEY ‘emp no‘ (‘emp no‘)
Why index chosen?
9
• Use case
• Example
• Index emp no has been chosen
mysql> explain select distinct title from titles
-> where year(from_date) > ’1990’G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: titles
partitions: NULL
type: index
possible_keys: PRIMARY,emp_no
key: emp_no
Why index chosen?
9
• Use case
• Example
• Index emp no has been chosen
• The reason
• Cost statistics for emp no
mysql> explain format=json select distinct title
-> from titles where year(from_date) > ’1990’G
...
"cost_info": {
"query_cost": "89796.80"
},
...
Why index chosen?
9
• Use case
• Example
• Index emp no has been chosen
• The reason
"duplicates_removal": {
...
"cost_info": {
"read_cost": "1252.00",
"eval_cost": "88544.80",
"prefix_cost": "89796.80",
"data_read_per_join": "27M"
},
Why index chosen?
9
• Use case
• Example
• Index emp no has been chosen
• The reason
• Cost statistics for PRIMARY KEY
mysql> explain format=json select distinct title from titles
-> force index(primary) where year(from_date) > ’1990’G
...
"cost_info": {
"query_cost": "531269.80"
},
...
Why index chosen?
9
• Use case
• Example
• Index emp no has been chosen
• The reason
• Access by PRIMARY KEY is 531269.80 /
89796.80 = 6 times more expensive!
Why index chosen?
9
Grouping and ordering
10
• Hierarchical structure
"grouping_operation": {
"using_filesort": false,
"table": {
...
How GROUP BY proceed
11
• Hierarchical structure
• Allows to distinguish operations
"grouping_operation": {
"using_temporary_table": true,
"using_filesort": true,
"cost_info": {
"sort_cost": "26.41"
},
"nested_loop": [
{
...
How GROUP BY proceed
11
• Hierarchical structure
• Allows to distinguish operations
• Separate member for DISTINCT
"duplicates_removal": {
"using_temporary_table": true,
"using_filesort": false,
"grouping_operation": {
"using_temporary_table": true,
"using_filesort": true,
...
How GROUP BY proceed
11
• Clearly shows which job required
• Is temporary table for ORDER BY?
mysql> explain select distinct last_name
-> from employees order by last_name ascG
*************************** 1. row ***************************
...
type: ALL
...
rows: 299379
filtered: 100.00
Extra: Using temporary; Using filesort
1 row in set, 1 warning (0.00 sec)
Ordering operations
12
• Clearly shows which job required
• Is temporary table for ORDER BY?
• Lets check what really happened:
mysql> explain format=json select distinct last_name
-> from employees order by last_name ascG
...
"ordering_operation": {
"using_filesort": false, - No temporary table here!
"duplicates_removal": {
"using_temporary_table": true,
"using_filesort": true,
...
Ordering operations
12
• Clearly shows which job required
• Is temporary table for ORDER BY?
• Lets check what really happened:
• Confirmation
mysql> explain format=json select last_name
-> from employees order by last_name ascG
...
"ordering_operation": {
"using_filesort": true,
"cost_info": {
"sort_cost": "299379.00"
...
Ordering operations
12
Subqueries and unions
13
• attached subqueries
• Subqueries which are not converted to JOIN
mysql> explain format=json select emp_no from salaries
-> where salary > (select avg(salary) from salaries)G
*************************** 1. row ***************************
...
"attached_subqueries": [
{
"dependent": false,
"cacheable": true,
"query_block": {
"select_id": 2,
...
Everything about subqueries
14
• attached subqueries
• optimized away subqueries
• Subqueries that were executed only once and
were replaced by their result.
mysql> explain format=json select emp_no, salary from salaries
-> order by (select max(salary) from salaries)G
...
"optimized_away_subqueries": [
{
"dependent": false,
"cacheable": true,
...
Everything about subqueries
14
• attached subqueries
• optimized away subqueries
• Compare with regular EXPLAIN
mysql> explain select emp_no, salary from salaries
-> order by (select max(salary) from salaries)G
...
*************************** 2. row ***************************
id: 2 | key: NULL
select_type: SUBQUERY | key_len: NULL
table: salaries | ref: NULL
partitions: NULL | rows: 2838525
type: ALL | filtered: 100.00
possible_keys: NULL | Extra: NULL
Everything about subqueries
14
• attached subqueries
• optimized away subqueries
• materialized from subquery
• Materialized subquery
mysql> explain format=json select dept_name from departments where
-> dept_no in (select dept_no from dept_manager
-> where to_date is not null)G
...
"table": { | "materialized_from_subquery": {
"table_name": "<subquery2>",| "using_temporary_table": true,
"access_type": "eq_ref", | "query_block": {
...
Everything about subqueries
14
• order by subqueries
• group by subqueries
• having subqueries
• Dependent or not
• Cacheable or not
• If was optimized away
• Information about nested subqueries
Subqueries and GROUP/ORDER BY
15
• All members in parent object union result
• Each query exists in query specifications
array
mysql> explain format=json select Name from City
-> union select Capital from CountryG
*************************** 1. row ***************************
EXPLAIN: { | "table": {
"query_block": { | "table_name": "City",
"union_result": { | ...
... | "table": {
"query_specifications": [ | "table_name": "Country",
{
...
UNION details
16
Troubleshooting sugar
17
• The issue: temporary table
• SQL BUFFER RESULT always uses it
• But how to ensure if original query does not?
mysql> explain select sql_buffer_result emp_no, salary/avg(salary)
-> from salaries group by emp_no, salaryG
*************************** 1. row ***************************
id: 1 | key_len: NULL
select_type: SIMPLE | ref: NULL
table: salaries | rows: 2557022
partitions: NULL | filtered: 100.00
type: ALL | Extra: Using temporary; Using filesort
possible_keys: NULL
key: NULL
SQL BUFFER RESULT is not hidden
18
• The issue: temporary table
• EXPLAIN FORMAT=JSON distinguish
buffer and original query
mysql> explain format=json select sql_buffer_result emp_no,
-> salary/avg(salary) from salaries group by emp_no, salaryG
...
"grouping_operation": {
"using_temporary_table": true,
"using_filesort": true,
...
"buffer_result": {
"using_temporary_table": true,
...
SQL BUFFER RESULT is not hidden
18
• Issue: complicated queries
• Regular EXPLAIN lists tables in rows
mysql> explain select * from employees join dept_manager using
(emp_no) where emp_no in (select emp_no from (select ...
*************************** 1. row ***************************
table: <subquery2>
*************************** 2. row ***************************
table: dept_manager
*************************** 3. row ***************************
table: employees
*************************** 4. row ***************************
table: <derived3>
...
nested loop and JOIN hierarchy
19
• Issue: complicated queries
• Solution: hierarchical structure
mysql> explain format=json select * from employees join dept_manager
-> using (emp_no) where emp_no in (select emp_no...
...
"nested_loop": [
{
"table": { "table_name": "<subquery2>",
...
"nested_loop": [ { "table": { "table_name": "titles",
...
{
"table": { "table_name": "dept_manager", ...
nested loop and JOIN hierarchy
19
• Provides more details than regular
EXPLAIN
• However only necessary information for
DBA
• Elegant way to show it
• Machine-readable
• Trace details in easy-to-view format
EXPLAIN FORMAT=JSON is Cool!
20
• EXPLAIN in the official user manual
• EXPLAIN FORMAT=JSON is Cool! series
More information
21
???
Place for your questions
22
http://www.slideshare.net/SvetaSmirnova
https://twitter.com/svetsmirnova
Thank you!
23

Why Use EXPLAIN FORMAT=JSON?

  • 1.
    EXPLAIN FORMAT=JSON Why touse? April, 19, 2016 Sveta Smirnova
  • 2.
    •The appearance •Innovations •Grouping andordering •Subqueries and unions •Troubleshooting sugar Table of Contents 2
  • 3.
    • Has nicestructured view • Easily machine-readable • Allows MySQL Workbench draw nice graphs EXPLAIN FORMAT=JSON was introduced in 5.6 3
  • 4.
    • It hasmore information than regular EXPLAIN • Can replace OPTIMIZER TRACE for some cases Trendy? - Not only! 4
  • 5.
  • 6.
    • According touser manual • The percentage of rows that were actually needed, against the equal or bigger number of resolved rows. • Is this good or bad? What number of ”filtered” rows mean? 6
  • 7.
    • According touser manual • Example query mysql> select * from Country where Name=’Russian Federation’G *************************** 1. row *************************** Code: RUS Name: Russian Federation Continent: Europe ... HeadOfState: Vladimir Putin Capital: 3580 Code2: RU 1 row in set (0.00 sec) What number of ”filtered” rows mean? 6
  • 8.
    • According touser manual • Example query • Regular EXPLAIN mysql> explain select * from Country where Name=’Russian Federation’; +-----+------+----------+-------------+ | ... | rows | filtered | Extra | +-----+------+----------+-------------+ | ... | 239 | 10.00 | Using where | +-----+------+----------+-------------+ • What happened with rows? • Why they were filtered? What number of ”filtered” rows mean? 6
  • 9.
    • According touser manual • Example query • Regular EXPLAIN • EXPLAIN FORMAT=JSON has the answer "table": { "table_name": "Country", "access_type": "ALL", "rows_examined_per_scan": 239, "rows_produced_per_join": 23, "filtered": "10.00", What number of ”filtered” rows mean? 6
  • 10.
    • Covered indexes •Usually use more than one column • Contain all fields, necessary to resolve the query When to use covered indexes? 7
  • 11.
    • Covered indexes •Example mysql> select count(*) from Country -> where Continent=’Africa’ and Population > 1000000; +----------+ | count(*) | +----------+ | 47 | +----------+ 1 row in set (0,00 sec) • Will the query use Primary Key? • Or * will look through all rows? When to use covered indexes? 7
  • 12.
    • Covered indexes •Example • EXPLAIN FORMAT=JSON has the answer mysql> explain format=json select count(*) from Country -> where Continent=’Africa’ and Population > 1000000G *************************** 1. row *************************** ... "used_columns": [ "Continent", "Population" ], ... When to use covered indexes? 7
  • 13.
    • Covered indexes •Example • EXPLAIN FORMAT=JSON has the answer • We only need Continent and Population columns for the index When to use covered indexes? 7
  • 14.
    • Example queries SELECTfirst_name, last_name FROM employees WHERE first_name=’Steve’; ...first_name=’Steve’ and last_name like ’V%’; ...first_name=’Steve’ and last_name like ’V%’ and hire_date>’1990-01-01’; Which part of index used? 8
  • 15.
    • Example queries •Candidate indexes CREATE INDEX comp1(first_name); CREATE INDEX comp2(first_name, last_name); CREATE INDEX comp3(first_name, last_name, hire_date); • Last one seem to fit all the queries Which part of index used? 8
  • 16.
    • Example queries •Candidate indexes • Index effectiveness mysql> explain format=json SELECT first_name, last_name FROM employees -> WHERE first_name=’Steve’ and last_name like ’V%’ -> and hire_date > ’1990-01-01’G *************************** 1. row *************************** EXPLAIN: { ... "used_key_parts": [ "first_name", "last_name" ], Which part of index used? 8
  • 17.
    • Example queries •Candidate indexes • Index effectiveness • Column hire date is not used! • Most effective index would be on (first name, last name) Which part of index used? 8
  • 18.
    • Use case •Table has two or more indexes • All can be used to resolve the query • But only one is chosen • Why? Why index chosen? 9
  • 19.
    • Use case •Example • Table ‘titles‘ in ‘employees‘ database • The query: select distinct title from titles where year(from_date) > ’1990’; • Two indexes: PRIMARY KEY (‘emp no‘,‘title‘,‘from date‘) KEY ‘emp no‘ (‘emp no‘) Why index chosen? 9
  • 20.
    • Use case •Example • Index emp no has been chosen mysql> explain select distinct title from titles -> where year(from_date) > ’1990’G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: titles partitions: NULL type: index possible_keys: PRIMARY,emp_no key: emp_no Why index chosen? 9
  • 21.
    • Use case •Example • Index emp no has been chosen • The reason • Cost statistics for emp no mysql> explain format=json select distinct title -> from titles where year(from_date) > ’1990’G ... "cost_info": { "query_cost": "89796.80" }, ... Why index chosen? 9
  • 22.
    • Use case •Example • Index emp no has been chosen • The reason "duplicates_removal": { ... "cost_info": { "read_cost": "1252.00", "eval_cost": "88544.80", "prefix_cost": "89796.80", "data_read_per_join": "27M" }, Why index chosen? 9
  • 23.
    • Use case •Example • Index emp no has been chosen • The reason • Cost statistics for PRIMARY KEY mysql> explain format=json select distinct title from titles -> force index(primary) where year(from_date) > ’1990’G ... "cost_info": { "query_cost": "531269.80" }, ... Why index chosen? 9
  • 24.
    • Use case •Example • Index emp no has been chosen • The reason • Access by PRIMARY KEY is 531269.80 / 89796.80 = 6 times more expensive! Why index chosen? 9
  • 25.
  • 26.
    • Hierarchical structure "grouping_operation":{ "using_filesort": false, "table": { ... How GROUP BY proceed 11
  • 27.
    • Hierarchical structure •Allows to distinguish operations "grouping_operation": { "using_temporary_table": true, "using_filesort": true, "cost_info": { "sort_cost": "26.41" }, "nested_loop": [ { ... How GROUP BY proceed 11
  • 28.
    • Hierarchical structure •Allows to distinguish operations • Separate member for DISTINCT "duplicates_removal": { "using_temporary_table": true, "using_filesort": false, "grouping_operation": { "using_temporary_table": true, "using_filesort": true, ... How GROUP BY proceed 11
  • 29.
    • Clearly showswhich job required • Is temporary table for ORDER BY? mysql> explain select distinct last_name -> from employees order by last_name ascG *************************** 1. row *************************** ... type: ALL ... rows: 299379 filtered: 100.00 Extra: Using temporary; Using filesort 1 row in set, 1 warning (0.00 sec) Ordering operations 12
  • 30.
    • Clearly showswhich job required • Is temporary table for ORDER BY? • Lets check what really happened: mysql> explain format=json select distinct last_name -> from employees order by last_name ascG ... "ordering_operation": { "using_filesort": false, - No temporary table here! "duplicates_removal": { "using_temporary_table": true, "using_filesort": true, ... Ordering operations 12
  • 31.
    • Clearly showswhich job required • Is temporary table for ORDER BY? • Lets check what really happened: • Confirmation mysql> explain format=json select last_name -> from employees order by last_name ascG ... "ordering_operation": { "using_filesort": true, "cost_info": { "sort_cost": "299379.00" ... Ordering operations 12
  • 32.
  • 33.
    • attached subqueries •Subqueries which are not converted to JOIN mysql> explain format=json select emp_no from salaries -> where salary > (select avg(salary) from salaries)G *************************** 1. row *************************** ... "attached_subqueries": [ { "dependent": false, "cacheable": true, "query_block": { "select_id": 2, ... Everything about subqueries 14
  • 34.
    • attached subqueries •optimized away subqueries • Subqueries that were executed only once and were replaced by their result. mysql> explain format=json select emp_no, salary from salaries -> order by (select max(salary) from salaries)G ... "optimized_away_subqueries": [ { "dependent": false, "cacheable": true, ... Everything about subqueries 14
  • 35.
    • attached subqueries •optimized away subqueries • Compare with regular EXPLAIN mysql> explain select emp_no, salary from salaries -> order by (select max(salary) from salaries)G ... *************************** 2. row *************************** id: 2 | key: NULL select_type: SUBQUERY | key_len: NULL table: salaries | ref: NULL partitions: NULL | rows: 2838525 type: ALL | filtered: 100.00 possible_keys: NULL | Extra: NULL Everything about subqueries 14
  • 36.
    • attached subqueries •optimized away subqueries • materialized from subquery • Materialized subquery mysql> explain format=json select dept_name from departments where -> dept_no in (select dept_no from dept_manager -> where to_date is not null)G ... "table": { | "materialized_from_subquery": { "table_name": "<subquery2>",| "using_temporary_table": true, "access_type": "eq_ref", | "query_block": { ... Everything about subqueries 14
  • 37.
    • order bysubqueries • group by subqueries • having subqueries • Dependent or not • Cacheable or not • If was optimized away • Information about nested subqueries Subqueries and GROUP/ORDER BY 15
  • 38.
    • All membersin parent object union result • Each query exists in query specifications array mysql> explain format=json select Name from City -> union select Capital from CountryG *************************** 1. row *************************** EXPLAIN: { | "table": { "query_block": { | "table_name": "City", "union_result": { | ... ... | "table": { "query_specifications": [ | "table_name": "Country", { ... UNION details 16
  • 39.
  • 40.
    • The issue:temporary table • SQL BUFFER RESULT always uses it • But how to ensure if original query does not? mysql> explain select sql_buffer_result emp_no, salary/avg(salary) -> from salaries group by emp_no, salaryG *************************** 1. row *************************** id: 1 | key_len: NULL select_type: SIMPLE | ref: NULL table: salaries | rows: 2557022 partitions: NULL | filtered: 100.00 type: ALL | Extra: Using temporary; Using filesort possible_keys: NULL key: NULL SQL BUFFER RESULT is not hidden 18
  • 41.
    • The issue:temporary table • EXPLAIN FORMAT=JSON distinguish buffer and original query mysql> explain format=json select sql_buffer_result emp_no, -> salary/avg(salary) from salaries group by emp_no, salaryG ... "grouping_operation": { "using_temporary_table": true, "using_filesort": true, ... "buffer_result": { "using_temporary_table": true, ... SQL BUFFER RESULT is not hidden 18
  • 42.
    • Issue: complicatedqueries • Regular EXPLAIN lists tables in rows mysql> explain select * from employees join dept_manager using (emp_no) where emp_no in (select emp_no from (select ... *************************** 1. row *************************** table: <subquery2> *************************** 2. row *************************** table: dept_manager *************************** 3. row *************************** table: employees *************************** 4. row *************************** table: <derived3> ... nested loop and JOIN hierarchy 19
  • 43.
    • Issue: complicatedqueries • Solution: hierarchical structure mysql> explain format=json select * from employees join dept_manager -> using (emp_no) where emp_no in (select emp_no... ... "nested_loop": [ { "table": { "table_name": "<subquery2>", ... "nested_loop": [ { "table": { "table_name": "titles", ... { "table": { "table_name": "dept_manager", ... nested loop and JOIN hierarchy 19
  • 44.
    • Provides moredetails than regular EXPLAIN • However only necessary information for DBA • Elegant way to show it • Machine-readable • Trace details in easy-to-view format EXPLAIN FORMAT=JSON is Cool! 20
  • 45.
    • EXPLAIN inthe official user manual • EXPLAIN FORMAT=JSON is Cool! series More information 21
  • 46.
    ??? Place for yourquestions 22
  • 47.