MySQL Query Tuning for Dev[Op]s. Introduction
•Basics •Indexes How Do They Work? When MySQL Uses Indexes Optimizer Histograms •Diagnostics EXPLAIN: How Optimizer Works Real Numbers: Inside Storage Engine Real Numbers: Inside the Server •How to Affect Query Plans Table of Contents 2 ©2021 Percona
• Sveta Smirnova • MySQL Support engineer • Author of • MySQL Troubleshooting • JSON UDF functions • FILTER clause for MySQL • Speaker • Percona Live, OOW, Fosdem, DevConf, HighLoad... 3 ©2021 Percona
Basics
Concurrency Environment Query Tuning Troubleshooting Workflow 5 ©2021 Percona
Concurrency Environment Query Tuning Troubleshooting Workflow: This Session 6 ©2021 Percona
$system = System::factory() ->setName($this->form->get(Field::NAME)) ->setDescription( $this->form->get(Field::DESCRIPTION) ); DAO::system()->take($system); The Query 7 ©2021 Percona
$system = System::factory() ->setName($this->form->get(Field::NAME)) ->setDescription( $this->form->get(Field::DESCRIPTION) ); DAO::system()->take($system); The Query 7 ©2021 Percona
cursor = conn.cursor() q = ’’’UPDATE ‘foo‘ SET my_date=NOW(), subject = %s, msg = %s, address = %s, updated_at = NOW() WHERE id=%s ’’’ cursor.execute(q, [ remote_resp.get(’subject’), remote_resp.get(’msg’), remote_resp.get(’address’), my_id ]) The Query 7 ©2021 Percona
cursor = conn.cursor() q = ’’’UPDATE ‘foo‘ SET my_date=NOW(), subject = %s, msg = %s, address = %s, updated_at = NOW() WHERE id=%s ’’’ cursor.execute(q, [ remote_resp.get(’subject’), remote_resp.get(’msg’), remote_resp.get(’address’), my_id ]) The Query 7 ©2021 Percona
SELECT dept_name, title, gender, min(salary) AS mins, max(salary) AS maxs FROM employees JOIN salaries USING(emp_no) JOIN titles USING(emp_no) JOIN dept_emp USING(emp_no) JOIN departments USING(dept_no) JOIN dept_manager USING(dept_no) WHERE dept_manager.to_date = ’9999-01-01’ GROUP BY dept_name, title, gender ORDER BY gender, maxs DESC; The Query 7 ©2021 Percona
SELECT dept_name, title, gender, min(salary) AS mins, max(salary) AS maxs FROM employees JOIN salaries USING(emp_no) JOIN titles USING(emp_no) JOIN dept_emp USING(emp_no) JOIN departments USING(dept_no) JOIN dept_manager USING(dept_no) WHERE dept_manager.to_date = ’9999-01-01’ GROUP BY dept_name, title, gender ORDER BY gender, maxs DESC; The Query 7 ©2021 Percona
• PMM QAN Always Tune Raw Query 8 ©2021 Percona
• PMM QAN • Slow Query Log Always Tune Raw Query 8 ©2021 Percona
• PMM QAN • Slow Query Log • Application log Always Tune Raw Query 8 ©2021 Percona
• PMM QAN • Slow Query Log • Application log • ... Always Tune Raw Query 8 ©2021 Percona
• Mind your data! • 75,000,000 rows • (INT, INT) 75,000,000 * (4 + 4) = 600,000,000 B = 572 MB • (INT, INT, DATETIME, VARCHAR(255), VARCHAR(255)) 75,000,000 * (4 + 4 + 8 + 256 + 256) = 39,600,000,000 bytes = 37 G • 39,600,000,000 / 600,000,000 = 66 Slow is Relative 9 ©2021 Percona
• Mind your data! • Mind use case • Popular website • Admin interface • Weekly cron job Slow is Relative 9 ©2021 Percona
• Mind your data! • Mind use case • Mind location • Server, used by multiple connections • Dedicated for OLAP queries Slow is Relative 9 ©2021 Percona
• MySQL performs a job to execute it Why is the Query Slow 10 ©2021 Percona
• MySQL performs a job to execute it • In the worst case scenario it will do a full table scan • CREATE INDEX • ANALYZE TABLE ... UPDATE HISTOGRAM ON ... Why is the Query Slow 10 ©2021 Percona
• MySQL performs a job to execute it • In the worst case scenario it will do a full table scan • CREATE INDEX • ANALYZE TABLE ... UPDATE HISTOGRAM ON ... • Incorrect index can be used Why is the Query Slow 10 ©2021 Percona
Query sent Connection Pool: Authentication, Caches; SQL interface; Parser Optimizer Storage engines Hardware Query Execution Workflow 11 ©2021 Percona
• Selecting a lot of data • SELECT * FROM many columns table You May not Be Able to Change a Slow Query 12 ©2021 Percona
• Selecting a lot of data • SELECT * FROM many columns table • Badly written • LEFT JOIN instead of INNER JOIN • Many values in IN() • Retrieving large result set, then discarding • Not effective SQL For particular MySQL version You May not Be Able to Change a Slow Query 12 ©2021 Percona
• Selecting a lot of data • SELECT * FROM many columns table • Badly written • LEFT JOIN instead of INNER JOIN • Many values in IN() • Retrieving large result set, then discarding • Not effective SQL For particular MySQL version • We can still improve performance You May not Be Able to Change a Slow Query 12 ©2021 Percona
Indexes
Indexes How Do They Work?
SELECT name FROM users WHERE id=12 1 2 5 6 7 9 12 16 18 21 Full Table Scan 15 ©2021 Percona
SELECT name FROM users WHERE id=12 1 2 5 6 7 9 12 16 18 21 Index Access 16 ©2021 Percona
d001 d003 d008 d009 d003****** d009****** d008****** d009****** d001****** d003****** d009****** d008****** d009****** d001****** d008****** d008****** MySQL Indexes - B-Tree Mostly - LSM Tree - Fractal Tree - R-Tree Spatial - Hash Memory SE - Engine’s 17 ©2021 Percona
• Single column CREATE INDEX index name ON the table(the column) • Multiple columns CREATE INDEX index name ON the table(column1, column2) How to Create an Index 18 ©2021 Percona
• Single column ALTER TABLE table name ADD INDEX [index name] (the column) • Multiple columns ALTER TABLE table name ADD INDEX [index name] (column1, column2) How to Create an Index 18 ©2021 Percona
Indexes When MySQL Uses Indexes
• WHERE the column = a value • WHERE the column IN(value1, value2, value3) • WHERE the column LIKE ’value%’ • WHERE the column LIKE ’%value’ Conditions 20 ©2021 Percona
• WHERE left part = value1 AND right part = value2 • WHERE left part = value1 OR right part = value2 • WHERE right part = value1 AND left part = value2 • WHERE right part = value1 OR left part = value2 Conditions 20 ©2021 Percona
• table1 JOIN table2 ON table1.column1 = table2.column2 Joins 21 ©2021 Percona
• table1 JOIN table2 ON table1.column1 = table2.column2 • Same as FROM table1, table2 WHERE table1.column1 = table2.column2 Joins 21 ©2021 Percona
• GROUP BY the column • GROUP BY left part, right part • GROUP BY right part, left part • GROUP BY the index, another index GROUP BY 22 ©2021 Percona
• ORDER BY the column • ORDER BY left part, right part • ORDER BY right part, left part • ORDER BY the index, another index ORDER BY 23 ©2021 Percona
5.7 ORDER BY left part DESC, right part ASC 8.0 ORDER BY left part DESC, right part ASC • left part must be descending • right part must be ascending • the index(left part DESC, right part ASC) ORDER BY 23 ©2021 Percona
• Deterministic, built-in • Return same value for the same argument • WHERE the column = FLOOR(123.45) Expressions 24 ©2021 Percona
• Deterministic, built-in • Return same value for the same argument • WHERE the column = FLOOR(123.45) • Non-deterministic • Return different values for different invocations • WHERE the column = RAND() ∗ 100 Expressions 24 ©2021 Percona
• Deterministic, built-in • Return same value for the same argument • WHERE the column = FLOOR(123.45) • Non-deterministic • Return different values for different invocations • WHERE the column = RAND() ∗ 100 • Stored functions and UDFs • Indexes are not used Use generated column indexes Expressions 24 ©2021 Percona
• Identify queries not using indexes • Status variables mysql select * from performance_schema.session_status - where variable_name in (’Created_tmp_tables’, - ’Created_tmp_disk_tables’, ’Select_full_join’, - ’Select_full_range_join’, ’Select_range’, - ’Select_range_check’, ’Select_scan’, - ’Sort_merge_passes’, ’Sort_range’, ’Sort_rows’, - ’Sort_scan’) and variable_value 0; +------------------------+----------------+ | VARIABLE_NAME | VARIABLE_VALUE | +------------------------+----------------+ | Select_scan | 2 | +------------------------+----------------+ 1 row in set (0,00 sec) When to Add Indexes? 25 ©2021 Percona
• Identify queries not using indexes mysql show global status like ’Handler_read%’; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_read_first | 31 | | Handler_read_key | 1909 | | Handler_read_last | 0 | | Handler_read_next | 4393 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 1135 | +-----------------------+-------+ When to Add Indexes? 25 ©2021 Percona
• Identify queries not using indexes • PERFORMANCE SCHEMA.EVENTS STATEMENTS * mysql select * from events_statements_history_long - where sql_text like ’select count(*) from employees join %’G *************************** 1. row **************************** ... ROWS_SENT: 1 SELECT_RANGE_CHECK: 0 ROWS_EXAMINED: 541058 SELECT_SCAN: 1 CREATED_TMP_DISK_TABLES: 0 SORT_MERGE_PASSES: 0 CREATED_TMP_TABLES: 0 SORT_RANGE: 0 SELECT_FULL_JOIN: 0 SORT_ROWS: 0 SELECT_FULL_RANGE_JOIN: 0 SORT_SCAN: 0 SELECT_RANGE: 0 NO_INDEX_USED: 0 When to Add Indexes? 25 ©2021 Percona
• Identify queries not using indexes • Slow query log # Time: 2020-10-11T23:34:03.701871Z # User@Host: root[root] @ localhost [127.0.0.1] Id: 506 # Query_time: 0.024106 Lock_time: 0.000091 Rows_sent: 1 Rows_examined: 1000 SET timestamp=1602459243; SELECT c FROM sbtest1 WHERE id=996; When to Add Indexes? 25 ©2021 Percona
• Identify queries not using indexes • QAN in PMM When to Add Indexes? 25 ©2021 Percona
• Identify queries not using indexes • Analyze if adding an index will help When to Add Indexes? 25 ©2021 Percona
• Identify queries not using indexes • Analyze if adding an index will help • Add the index When to Add Indexes? 25 ©2021 Percona
• Identify queries not using indexes • Analyze if adding an index will help • Add the index • Test first! • ADD INDEX is an expensive operation Use pt-online-schema-change When to Add Indexes? 25 ©2021 Percona
Indexes Optimizer Histograms
• Since version 8.0 Histogram Statistics 27 ©2021 Percona
• Since version 8.0 • Collected and used by the Optimizer Histogram Statistics 27 ©2021 Percona
• Since version 8.0 • Collected and used by the Optimizer • Visible in Information Schema mysql select HISTOGRAM from information_schema.column_statistics - where table_name=’example’G *************************** 1. row *************************** HISTOGRAM: {buckets: [[1, 0.6], [2, 0.8], [3, 1.0]], data-type: int, null-values: 0.0, collation-id: 8, last-updated: 2018-11-07 09:07:19.791470, sampling-rate: 1.0, histogram-type: singleton, number-of-buckets-specified: 3} 1 row in set (0.00 sec) Histogram Statistics 27 ©2021 Percona
• Since version 8.0 • Collected and used by the Optimizer • Visible in Information Schema • Affects query execution plan, but not access • SELECT ... FROM a JOIN b vs SELECT ... FROM b JOIN a Histogram Statistics 27 ©2021 Percona
• Since version 8.0 • Collected and used by the Optimizer • Visible in Information Schema • Affects query execution plan, but not access More details Histogram Statistics 27 ©2021 Percona
1 2 3 4 5 6 7 8 9 10 0 200 400 600 800 Indexes: Number of Items with Same Value 28 ©2021 Percona
1 2 3 4 5 6 7 8 9 10 0 200 400 600 800 Indexes: Cardinality 29 ©2021 Percona
1 2 3 4 5 6 7 8 9 10 0 200 400 600 800 Histograms: Number of Values in Each Bucket 30 ©2021 Percona
1 2 3 4 5 6 7 8 9 10 0 0.2 0.4 0.6 0.8 1 Histograms: Data in the Histogram 31 ©2021 Percona
• Data distribution is far from uniform • Query accesses two or more tables • Indexes cannot be used • Maintaining them is too expensive • They are already used for different conditions Create Histograms 32 ©2021 Percona
Diagnostics
Diagnostics EXPLAIN: How Optimizer Works
• EXPLAIN • Estimates the query execution plan 5.6- EXTENDED 5.6- PARTITIONS 5.6+ FORMAT=JSON 8.0+ FORMAT=TREE How to Find how MySQL Uses Indexes 35 ©2021 Percona
• EXPLAIN • Estimates the query execution plan 5.6- EXTENDED 5.6- PARTITIONS 5.6+ FORMAT=JSON 8.0+ FORMAT=TREE • INFORMATION SCHEMA.OPTIMIZER TRACE • Real data, collected when query was executing • Advanced topic How to Find how MySQL Uses Indexes 35 ©2021 Percona
mysql EXPLAIN SELECT first_name, last_name, title, salary FROM employees - JOIN salaries USING(emp_no) JOIN titles USING(emp_no) - WHERE salary = (SELECT MAX(salary) FROM salaries - JOIN titles WHERE titles.to_date CURDATE()); +----+-------------+-----------+------------+------+---------------+---------+---------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | +----+-------------+-----------+------------+------+---------------+---------+---------+ | 1 | PRIMARY | employees | NULL | ALL | PRIMARY | NULL | NULL ... | 1 | PRIMARY | salaries | NULL | ref | PRIMARY | PRIMARY | 4 ... | 1 | PRIMARY | titles | NULL | ref | PRIMARY | PRIMARY | 4 ... | 2 | SUBQUERY | titles | NULL | ALL | NULL | NULL | NULL ... | 2 | SUBQUERY | salaries | NULL | ALL | NULL | NULL | NULL ... +----+-------------+-----------+------------+------+---------------+---------+---------+ +----+-...-+----------------------------+---------+----------+-------------------------------+ | id | ... | ref | rows | filtered | Extra | +----+-...-+----------------------------+---------+----------+-------------------------------+ | 1 | ... | NULL | 299113 | 100.00 | NULL | | 1 | ... | employees.employees.emp_no | 9 | 10.00 | Using where | | 1 | ... | employees.employees.emp_no | 1 | 100.00 | Using index | | 2 | ... | NULL | 442189 | 33.33 | Using where | | 2 | ... | NULL | 2838426 | 100.00 | Using join buffer (hash join) | +----+-...-+----------------------------+---------+----------+-------------------------------+ 5 rows in set, 1 warning (0,01 sec) 299113 * 9 * 1 * 442189 * 2838426 = 3,378,806,408,204,514,738 EXPLAIN: rows 36 ©2021 Percona
mysql EXPLAIN SELECT title, MAX(salary) AS maxs FROM employees - JOIN salaries USING(emp_no) JOIN titles USING(emp_no) - GROUP BY title ORDER BY maxs DESC; +----+-------------+-----------+------------+-------+---------------+---------+---------+ | id | select_type | table | partitions | type | possible_keys | key | key_len +----+-------------+-----------+------------+-------+---------------+---------+---------+ | 1 | SIMPLE | employees | NULL | index | PRIMARY | PRIMARY | 4 ... | 1 | SIMPLE | titles | NULL | ref | PRIMARY,title | PRIMARY | 4 ... | 1 | SIMPLE | salaries | NULL | ref | PRIMARY | PRIMARY | 4 ... +----+-------------+-----------+------------+-------+---------------+---------+---------+ EXPLAIN: type 37 ©2021 Percona
ALL Read all rows Query Execution Type 38 ©2021 Percona
ALL Read all rows index Read all rows from the index Query Execution Type 38 ©2021 Percona
ALL Read all rows index Read all rows from the index range Range: N ... M Query Execution Type 38 ©2021 Percona
ALL Read all rows index Read all rows from the index range Range: N ... M subquery Subqueries index subquery unique subquery Query Execution Type 38 ©2021 Percona
ALL Read all rows index Read all rows from the index range Range: N ... M subquery Subqueries merge Combination of two indexes index merge Query Execution Type 38 ©2021 Percona
ALL Read all rows index Read all rows from the index range Range: N ... M subquery Subqueries merge Combination of two indexes ref Comparision with single value ref or null ref eq ref Query Execution Type 38 ©2021 Percona
ALL Read all rows index Read all rows from the index range Range: N ... M subquery Subqueries merge Combination of two indexes ref Comparision with single value fulltext Fulltext index Query Execution Type 38 ©2021 Percona
ALL Read all rows index Read all rows from the index range Range: N ... M subquery Subqueries merge Combination of two indexes ref Comparision with single value fulltext Fulltext index const Single row Query Execution Type 38 ©2021 Percona
ALL Read all rows index Read all rows from the index range Range: N ... M subquery Subqueries merge Combination of two indexes ref Comparision with single value fulltext Fulltext index const Single row system Table has single row Query Execution Type 38 ©2021 Percona
mysql EXPLAIN SELECT title, MAX(salary) AS maxs FROM employees - JOIN salaries USING(emp_no) JOIN titles USING(emp_no) - GROUP BY title ORDER BY maxs DESC; +----+-------------+-----------+------------+-------+---------------+---------+---------+ | id | select_type | table | partitions | type | possible_keys | key | key_len +----+-------------+-----------+------------+-------+---------------+---------+---------+ | 1 | SIMPLE | employees | NULL | index | PRIMARY | PRIMARY | 4 ... | 1 | SIMPLE | titles | NULL | ref | PRIMARY,title | PRIMARY | 4 ... | 1 | SIMPLE | salaries | NULL | ref | PRIMARY | PRIMARY | 4 ... +----+-------------+-----------+------------+-------+---------------+---------+---------+ EXPLAIN: key and possible keys 39 ©2021 Percona
mysql EXPLAIN SELECT title, MAX(salary) AS maxs FROM employees - JOIN salaries USING(emp_no) - JOIN titles USING(emp_no) - GROUP BY title ORDER BY maxs DESC; +----+-------------+-----------+------------+-------+---------------+---------+---------+ | id | select_type | table | partitions | type | possible_keys | key | key_len +----+-------------+-----------+------------+-------+---------------+---------+---------+ | 1 | SIMPLE | employees | NULL | index | PRIMARY | PRIMARY | 4 ... | 1 | SIMPLE | titles | NULL | ref | PRIMARY,title | PRIMARY | 4 ... | 1 | SIMPLE | salaries | NULL | ref | PRIMARY | PRIMARY | 4 ... +----+-------------+-----------+------------+-------+---------------+---------+---------+ EXPLAIN: JOIN order 40 ©2021 Percona
mysql EXPLAIN SELECT first_name, last_name, title, salary FROM employees - JOIN salaries USING(emp_no) JOIN titles USING(emp_no) - WHERE salary IN (SELECT MAX(salary) FROM salaries - JOIN titles WHERE titles.to_date CURDATE()); ... 5 rows in set, 1 warning (0,01 sec) mysql SHOW WARNINGSG *************************** 1. row *************************** Level: Note Code: 1003 Message: /* select#1 */ select ‘employees‘.‘employees‘.‘first_name‘ AS ‘first_name‘, ‘employees‘.‘employees‘.‘last_name‘ AS ‘last_name‘,‘employees‘.‘salaries‘.‘salary‘ AS ‘salary‘ from ‘employees‘.‘employees‘ join ‘employees‘.‘salaries‘ where ((‘employees‘.‘salaries‘.‘emp_no‘ = ‘employees‘.‘employees‘.‘emp_no‘) and in_optimizer(‘employees‘.‘salaries‘.‘salary‘,‘employees‘.‘salaries‘.‘salary‘ in ( materialize (/* select#2 */ select max(‘employees‘.‘salaries‘.‘salary‘) from ‘employees‘.‘salaries‘ join ‘employees‘.‘titles‘ where (‘employees‘.‘titles‘.‘to_date‘ cache(curdate())) having true ), primary_index_lookup(‘employees‘.‘salaries‘.‘salary‘ in temporary table on auto_distinct_key where ((‘employees‘.‘salaries‘.‘salary‘ = ‘materialized_subquery‘.‘MAX(salary)‘)))))) 1 row in set (0,00 sec) Actual Query 41 ©2021 Percona
mysql explain select * from t1G *************************** 1. row *************************** ... rows: 12 Extra: NULL mysql explain select * from t1 where f2=12G *************************** 1. row *************************** ... key: NULL ... rows: 12 Extra: Using where Effect of Indexes: Before 42 ©2021 Percona
mysql alter table t1 add index(f2); Query OK, 12 rows affected (0.07 sec) Records: 12 Duplicates: 0 Warnings: 0 mysql explain select * from t1 where f2=12G *************************** 1. row *************************** ... key: f2 key_len: 5 ref: const rows: 1 Extra: NULL 1 row in set (0.00 sec) Effect of Indexes: After 43 ©2021 Percona
Diagnostics Real Numbers: Inside Storage Engine
• EXPLAIN is optimistic mysql explain select * from ol - where thread_id=10432 and site_id != 9939 - order by id limit 3G *************************** 1. row *************************** id: 1 | ref: NULL select_type: SIMPLE | rows: 33 table: ol | filtered: 8.07 partitions: NULL | Extra: Using where type: index possible_keys: thread_id key: PRIMARY key_len: 4 1 row in set, 1 warning (0,00 sec) Handler * Status Variables 45 ©2021 Percona
• ’Handler *’ show truth mysql FLUSH STATUS; mysql select * from ol - where thread_id=10432 and site_id != 9939 - order by id limit 3; mysql show status like ’Handler%’; +----------------------------+--------+ | Variable_name | Value | +----------------------------+--------+ ... | Handler_read_first | 1 | | Handler_read_key | 1 | | Handler_read_last | 0 | | Handler_read_next | 100000 | ... Handler * Status Variables 45 ©2021 Percona
Diagnostics Real Numbers: Inside the Server
• SHOW [FULL] PROCESSLIST • INFORMATION SCHEMA.PROCESSLIST • performance schema.THREADS PROCESSLIST 47 ©2021 Percona
• SHOW [FULL] PROCESSLIST • INFORMATION SCHEMA.PROCESSLIST • performance schema.THREADS • Your first alert of performance issue PROCESSLIST 47 ©2021 Percona
• SHOW [FULL] PROCESSLIST • INFORMATION SCHEMA.PROCESSLIST • performance schema.THREADS • Your first alert of performance issue • Shows all running queries PROCESSLIST 47 ©2021 Percona
• Can be seen in the PROCESSLIST mysql show processlistG ************************ 1. row ************************ Id: 7 User: root Host: localhost:48799 db: employees Command: Query Time: 2 State: Sending data Info: select count(*) from employees join titles using(emp_no) where title=’Senior Engineer’ ... Execution Stages 48 ©2021 Percona
• Can be seen in the PROCESSLIST • Very useful when you need to answer on the question: ”What is my server doing now?” Execution Stages 48 ©2021 Percona
• PERFORMANCE SCHEMA.EVENTS STAGES * mysql select eshl.event_name, substr(sql_text, 1, 15) ‘sql‘, - eshl.timer_wait/1000000000000 w_s - from events_stages_history_long - eshl join events_statements_history_long esthl on - (eshl.nesting_event_id = esthl.event_id) where - esthl.current_schema=’employees’ and sql_text like - ’select count(*) from employees%’ - order by eshl.timer_start asc; +--------------------------------+-----------------+--------+ | event_name | sql | w_s | +--------------------------------+-----------------+--------+ | stage/sql/starting | select count(*) | 0.0002 | | stage/sql/checking permissions | select count(*) | 0.0000 | ... Execution Stages 48 ©2021 Percona
• PERFORMANCE SCHEMA.EVENTS STAGES * ... | stage/sql/checking permissions | select count(*) | 0.0000 | | stage/sql/Opening tables | select count(*) | 0.0000 | | stage/sql/init | select count(*) | 0.0001 | | stage/sql/System lock | select count(*) | 0.0000 | | stage/sql/optimizing | select count(*) | 0.0000 | | stage/sql/statistics | select count(*) | 0.0001 | | stage/sql/preparing | select count(*) | 0.0000 | | stage/sql/executing | select count(*) | 0.0000 | | stage/sql/Sending data | select count(*) | 5.4915 | | stage/sql/end | select count(*) | 0.0000 | ... Execution Stages 48 ©2021 Percona
• Status variables mysql flush status; Query OK, 0 rows affected (0,01 sec) mysql select count(*) from employees join titles using(emp_no) - where title=’Senior Engineer’; +----------+ | count(*) | +----------+ | 97750 | +----------+ 1 row in set (5,44 sec) Temporary Tables and Other Job 49 ©2021 Percona
• Status variables mysql select * from performance_schema.session_status - where variable_name in (’Created_tmp_tables’, - ’Created_tmp_disk_tables’, ’Select_full_join’, - ’Select_full_range_join’, ’Select_range’, - ’Select_range_check’, ’Select_scan’, ’Sort_merge_passes’, - ’Sort_range’, ’Sort_rows’, ’Sort_scan’) - and variable_value 0; +------------------------+----------------+ | VARIABLE_NAME | VARIABLE_VALUE | +------------------------+----------------+ | Select_scan | 2 | +------------------------+----------------+ 1 row in set (0,00 sec) Temporary Tables and Other Job 49 ©2021 Percona
• PERFORMANCE SCHEMA.EVENTS STATEMENTS * mysql select * from events_statements_history_long - where sql_text like - ’select count(*) from employees join %’G *************************** 1. row **************************** ... ROWS_SENT: 1 SELECT_RANGE_CHECK: 0 ROWS_EXAMINED: 541058 SELECT_SCAN: 1 CREATED_TMP_DISK_TABLES: 0 SORT_MERGE_PASSES: 0 CREATED_TMP_TABLES: 0 SORT_RANGE: 0 SELECT_FULL_JOIN: 0 SORT_ROWS: 0 SELECT_FULL_RANGE_JOIN: 0 SORT_SCAN: 0 SELECT_RANGE: 0 NO_INDEX_USED: 0 Temporary Tables and Other Job 49 ©2021 Percona
• sys.statement analysis mysql select * from statement_analysis where query like - ’SELECT COUNT ( * ) FROM ‘emplo%’ and db=’employees’G *************************** 1. row *************************** query: SELECT COUNT ( * ) FROM ‘emplo ... ‘emp_no‘ ) WHE... db: employees max_latency: 5.59 s full_scan: avg_latency: 5.41 s exec_count: 7 lock_latency: 2.24 ms err_count: 0 rows_sent: 7 warn_count: 0 rows_sent_avg: 1 total_latency: 37.89 s rows_examined: 3787406 Temporary Tables and Other Job 49 ©2021 Percona
• sys.statement analysis rows_examined_avg: 541058 rows_affected: 0 rows_affected_avg: 0 tmp_tables: 0 tmp_disk_tables: 0 rows_sorted: 0 sort_merge_passes: 0 digest: 4086bc3dc6510a1d9c8f2fe1f59f0943 first_seen: 2016-04-14 15:19:19 last_seen: 2016-04-14 16:13:14 Temporary Tables and Other Job 49 ©2021 Percona
How to Affect Query Plans
• Index statistics • Histogram statistics • Optimizer switches • Bugs in optimizer What has an Effect on Query Optimizer Plans? 51 ©2021 Percona
• Collected by storage engine Index Statistics 52 ©2021 Percona
• Collected by storage engine • Used by Optimizer Index Statistics 52 ©2021 Percona
• Can be examined by SHOW INDEX mysql show index from sbtest1; +---------+----------+-------------+-------------+ | Table | Key_name | Column_name | Cardinality | +---------+----------+-------------+-------------+ | sbtest1 | k_1 | k | 49142 | +---------+----------+-------------+-------------+ mysql select count(distinct id), - count(distinct k) from sbtest1; +--------------------+-------------------+ | count(distinct id) | count(distinct k) | +--------------------+-------------------+ | 100000 | 17598 | +--------------------+-------------------+ Index Statistics 52 ©2021 Percona
• Can be updated • ANALYZE TABLE • If does not help: rebuild the table OPTIMIZE TABLE ALTER TABLE ENGINE=INNODB; ANALYZE TABLE Index Statistics 52 ©2021 Percona
• Since version 8.0 Histogram Statistics 53 ©2021 Percona
• Since version 8.0 • Collected and used by the Optimizer Histogram Statistics 53 ©2021 Percona
• Since version 8.0 • Collected and used by the Optimizer • Visible in Information Schema mysql select HISTOGRAM from information_schema.column_statistics - where table_name=’example’G *************************** 1. row *************************** HISTOGRAM: {buckets: [[1, 0.6], [2, 0.8], [3, 1.0]], data-type: int, null-values: 0.0, collation-id: 8, last-updated: 2018-11-07 09:07:19.791470, sampling-rate: 1.0, histogram-type: singleton, number-of-buckets-specified: 3} 1 row in set (0.00 sec) Histogram Statistics 53 ©2021 Percona
• Since version 8.0 • Collected and used by the Optimizer • Visible in Information Schema More details Histogram Statistics 53 ©2021 Percona
mysql select @@optimizer_switchG *************************** 1. row *************************** @@optimizer_switch: index_merge=on,index_merge_union=on, index_merge_sort_union=on,index_merge_intersection=on, engine_condition_pushdown=on,index_condition_pushdown=on, mrr=on,mrr_cost_based=on, block_nested_loop=on,batched_key_access=off, materialization=on,semijoin=on,loosescan=on,firstmatch=on, duplicateweedout=on,subquery_materialization_cost_based=on, use_index_extensions=on,condition_fanout_filter=on, derived_merge=on 1 row in set (0,00 sec) Optimizer Switches 54 ©2021 Percona
• Turn ON and OFF particular optimization Optimizer Switches 54 ©2021 Percona
• Turn ON and OFF particular optimization • Can be unhelpful • Especially for queries, tuned for previous versions Optimizer Switches 54 ©2021 Percona
• Turn ON and OFF particular optimization • Can be unhelpful • Work with them as with any other option • Turn OFF and try SET optimizer_switch = ’use_index_extensions=off’; SELECT ... EXPLAIN SELECT ... Optimizer Switches 54 ©2021 Percona
• Turn ON and OFF particular optimization • Can be unhelpful • Work with them as with any other option • If helps implement in queries SELECT /*+ SEMIJOIN(FIRSTMATCH, LOOSESCAN) */ * FROM t1 ...; SELECT /*+ BKA(t1) NO_BKA(t2) */ * FROM t1 INNER JOIN t2 WHERE ...; Optimizer Switches 54 ©2021 Percona
• Optimizer choses wrong plan for no reason Bugs in Optimizer 55 ©2021 Percona
• Optimizer choses wrong plan for no reason • Statistics are up to date • Histograms are not usable Bugs in Optimizer 55 ©2021 Percona
• Optimizer choses wrong plan for no reason • Statistics are up to date • Histograms are not usable • Solution • Use index hints FORCE INDEX IGNORE INDEX Bugs in Optimizer 55 ©2021 Percona
• Optimizer choses wrong plan for no reason • Statistics are up to date • Histograms are not usable • Solution • On every upgrade • Remove index hints • Test if the query improved • You must do it even for minor version upgrades! Bugs in Optimizer 55 ©2021 Percona
• EXPLAIN is essential for query tuning • Real job is done by storage engine • Index statistics affect query execution plan • All index hints, optimizer hints and other workarounds must be validated on each upgrade Summary 56 ©2021 Percona
EXPLAIN Syntax EXPLAIN FORMAT=JSON is Cool! Troubleshooting add-ons Optimizer Statistics aka Histograms Optimizer Hints Tracing the Optimizer More Information 57 ©2021 Percona
www.slideshare.net/SvetaSmirnova twitter.com/svetsmirnova github.com/svetasmirnova Thank you! 58 ©2021 Percona

Introduction into MySQL Query Tuning for Dev[Op]s

  • 1.
    MySQL Query Tuning forDev[Op]s. Introduction
  • 2.
    •Basics •Indexes How Do TheyWork? When MySQL Uses Indexes Optimizer Histograms •Diagnostics EXPLAIN: How Optimizer Works Real Numbers: Inside Storage Engine Real Numbers: Inside the Server •How to Affect Query Plans Table of Contents 2 ©2021 Percona
  • 3.
    • Sveta Smirnova •MySQL Support engineer • Author of • MySQL Troubleshooting • JSON UDF functions • FILTER clause for MySQL • Speaker • Percona Live, OOW, Fosdem, DevConf, HighLoad... 3 ©2021 Percona
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
    cursor = conn.cursor() q= ’’’UPDATE ‘foo‘ SET my_date=NOW(), subject = %s, msg = %s, address = %s, updated_at = NOW() WHERE id=%s ’’’ cursor.execute(q, [ remote_resp.get(’subject’), remote_resp.get(’msg’), remote_resp.get(’address’), my_id ]) The Query 7 ©2021 Percona
  • 10.
    cursor = conn.cursor() q= ’’’UPDATE ‘foo‘ SET my_date=NOW(), subject = %s, msg = %s, address = %s, updated_at = NOW() WHERE id=%s ’’’ cursor.execute(q, [ remote_resp.get(’subject’), remote_resp.get(’msg’), remote_resp.get(’address’), my_id ]) The Query 7 ©2021 Percona
  • 11.
    SELECT dept_name, title,gender, min(salary) AS mins, max(salary) AS maxs FROM employees JOIN salaries USING(emp_no) JOIN titles USING(emp_no) JOIN dept_emp USING(emp_no) JOIN departments USING(dept_no) JOIN dept_manager USING(dept_no) WHERE dept_manager.to_date = ’9999-01-01’ GROUP BY dept_name, title, gender ORDER BY gender, maxs DESC; The Query 7 ©2021 Percona
  • 12.
    SELECT dept_name, title,gender, min(salary) AS mins, max(salary) AS maxs FROM employees JOIN salaries USING(emp_no) JOIN titles USING(emp_no) JOIN dept_emp USING(emp_no) JOIN departments USING(dept_no) JOIN dept_manager USING(dept_no) WHERE dept_manager.to_date = ’9999-01-01’ GROUP BY dept_name, title, gender ORDER BY gender, maxs DESC; The Query 7 ©2021 Percona
  • 13.
    • PMM QAN AlwaysTune Raw Query 8 ©2021 Percona
  • 14.
    • PMM QAN •Slow Query Log Always Tune Raw Query 8 ©2021 Percona
  • 15.
    • PMM QAN •Slow Query Log • Application log Always Tune Raw Query 8 ©2021 Percona
  • 16.
    • PMM QAN •Slow Query Log • Application log • ... Always Tune Raw Query 8 ©2021 Percona
  • 17.
    • Mind yourdata! • 75,000,000 rows • (INT, INT) 75,000,000 * (4 + 4) = 600,000,000 B = 572 MB • (INT, INT, DATETIME, VARCHAR(255), VARCHAR(255)) 75,000,000 * (4 + 4 + 8 + 256 + 256) = 39,600,000,000 bytes = 37 G • 39,600,000,000 / 600,000,000 = 66 Slow is Relative 9 ©2021 Percona
  • 18.
    • Mind yourdata! • Mind use case • Popular website • Admin interface • Weekly cron job Slow is Relative 9 ©2021 Percona
  • 19.
    • Mind yourdata! • Mind use case • Mind location • Server, used by multiple connections • Dedicated for OLAP queries Slow is Relative 9 ©2021 Percona
  • 20.
    • MySQL performsa job to execute it Why is the Query Slow 10 ©2021 Percona
  • 21.
    • MySQL performsa job to execute it • In the worst case scenario it will do a full table scan • CREATE INDEX • ANALYZE TABLE ... UPDATE HISTOGRAM ON ... Why is the Query Slow 10 ©2021 Percona
  • 22.
    • MySQL performsa job to execute it • In the worst case scenario it will do a full table scan • CREATE INDEX • ANALYZE TABLE ... UPDATE HISTOGRAM ON ... • Incorrect index can be used Why is the Query Slow 10 ©2021 Percona
  • 23.
    Query sent Connection Pool: Authentication,Caches; SQL interface; Parser Optimizer Storage engines Hardware Query Execution Workflow 11 ©2021 Percona
  • 24.
    • Selecting alot of data • SELECT * FROM many columns table You May not Be Able to Change a Slow Query 12 ©2021 Percona
  • 25.
    • Selecting alot of data • SELECT * FROM many columns table • Badly written • LEFT JOIN instead of INNER JOIN • Many values in IN() • Retrieving large result set, then discarding • Not effective SQL For particular MySQL version You May not Be Able to Change a Slow Query 12 ©2021 Percona
  • 26.
    • Selecting alot of data • SELECT * FROM many columns table • Badly written • LEFT JOIN instead of INNER JOIN • Many values in IN() • Retrieving large result set, then discarding • Not effective SQL For particular MySQL version • We can still improve performance You May not Be Able to Change a Slow Query 12 ©2021 Percona
  • 27.
  • 28.
  • 29.
    SELECT name FROMusers WHERE id=12 1 2 5 6 7 9 12 16 18 21 Full Table Scan 15 ©2021 Percona
  • 30.
    SELECT name FROMusers WHERE id=12 1 2 5 6 7 9 12 16 18 21 Index Access 16 ©2021 Percona
  • 31.
  • 32.
    • Single column CREATEINDEX index name ON the table(the column) • Multiple columns CREATE INDEX index name ON the table(column1, column2) How to Create an Index 18 ©2021 Percona
  • 33.
    • Single column ALTERTABLE table name ADD INDEX [index name] (the column) • Multiple columns ALTER TABLE table name ADD INDEX [index name] (column1, column2) How to Create an Index 18 ©2021 Percona
  • 34.
  • 35.
    • WHERE thecolumn = a value • WHERE the column IN(value1, value2, value3) • WHERE the column LIKE ’value%’ • WHERE the column LIKE ’%value’ Conditions 20 ©2021 Percona
  • 36.
    • WHERE leftpart = value1 AND right part = value2 • WHERE left part = value1 OR right part = value2 • WHERE right part = value1 AND left part = value2 • WHERE right part = value1 OR left part = value2 Conditions 20 ©2021 Percona
  • 37.
    • table1 JOINtable2 ON table1.column1 = table2.column2 Joins 21 ©2021 Percona
  • 38.
    • table1 JOINtable2 ON table1.column1 = table2.column2 • Same as FROM table1, table2 WHERE table1.column1 = table2.column2 Joins 21 ©2021 Percona
  • 39.
    • GROUP BYthe column • GROUP BY left part, right part • GROUP BY right part, left part • GROUP BY the index, another index GROUP BY 22 ©2021 Percona
  • 40.
    • ORDER BYthe column • ORDER BY left part, right part • ORDER BY right part, left part • ORDER BY the index, another index ORDER BY 23 ©2021 Percona
  • 41.
    5.7 ORDER BYleft part DESC, right part ASC 8.0 ORDER BY left part DESC, right part ASC • left part must be descending • right part must be ascending • the index(left part DESC, right part ASC) ORDER BY 23 ©2021 Percona
  • 42.
    • Deterministic, built-in •Return same value for the same argument • WHERE the column = FLOOR(123.45) Expressions 24 ©2021 Percona
  • 43.
    • Deterministic, built-in •Return same value for the same argument • WHERE the column = FLOOR(123.45) • Non-deterministic • Return different values for different invocations • WHERE the column = RAND() ∗ 100 Expressions 24 ©2021 Percona
  • 44.
    • Deterministic, built-in •Return same value for the same argument • WHERE the column = FLOOR(123.45) • Non-deterministic • Return different values for different invocations • WHERE the column = RAND() ∗ 100 • Stored functions and UDFs • Indexes are not used Use generated column indexes Expressions 24 ©2021 Percona
  • 45.
    • Identify queriesnot using indexes • Status variables mysql select * from performance_schema.session_status - where variable_name in (’Created_tmp_tables’, - ’Created_tmp_disk_tables’, ’Select_full_join’, - ’Select_full_range_join’, ’Select_range’, - ’Select_range_check’, ’Select_scan’, - ’Sort_merge_passes’, ’Sort_range’, ’Sort_rows’, - ’Sort_scan’) and variable_value 0; +------------------------+----------------+ | VARIABLE_NAME | VARIABLE_VALUE | +------------------------+----------------+ | Select_scan | 2 | +------------------------+----------------+ 1 row in set (0,00 sec) When to Add Indexes? 25 ©2021 Percona
  • 46.
    • Identify queriesnot using indexes mysql show global status like ’Handler_read%’; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_read_first | 31 | | Handler_read_key | 1909 | | Handler_read_last | 0 | | Handler_read_next | 4393 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 1135 | +-----------------------+-------+ When to Add Indexes? 25 ©2021 Percona
  • 47.
    • Identify queriesnot using indexes • PERFORMANCE SCHEMA.EVENTS STATEMENTS * mysql select * from events_statements_history_long - where sql_text like ’select count(*) from employees join %’G *************************** 1. row **************************** ... ROWS_SENT: 1 SELECT_RANGE_CHECK: 0 ROWS_EXAMINED: 541058 SELECT_SCAN: 1 CREATED_TMP_DISK_TABLES: 0 SORT_MERGE_PASSES: 0 CREATED_TMP_TABLES: 0 SORT_RANGE: 0 SELECT_FULL_JOIN: 0 SORT_ROWS: 0 SELECT_FULL_RANGE_JOIN: 0 SORT_SCAN: 0 SELECT_RANGE: 0 NO_INDEX_USED: 0 When to Add Indexes? 25 ©2021 Percona
  • 48.
    • Identify queriesnot using indexes • Slow query log # Time: 2020-10-11T23:34:03.701871Z # User@Host: root[root] @ localhost [127.0.0.1] Id: 506 # Query_time: 0.024106 Lock_time: 0.000091 Rows_sent: 1 Rows_examined: 1000 SET timestamp=1602459243; SELECT c FROM sbtest1 WHERE id=996; When to Add Indexes? 25 ©2021 Percona
  • 49.
    • Identify queriesnot using indexes • QAN in PMM When to Add Indexes? 25 ©2021 Percona
  • 50.
    • Identify queriesnot using indexes • Analyze if adding an index will help When to Add Indexes? 25 ©2021 Percona
  • 51.
    • Identify queriesnot using indexes • Analyze if adding an index will help • Add the index When to Add Indexes? 25 ©2021 Percona
  • 52.
    • Identify queriesnot using indexes • Analyze if adding an index will help • Add the index • Test first! • ADD INDEX is an expensive operation Use pt-online-schema-change When to Add Indexes? 25 ©2021 Percona
  • 53.
  • 54.
    • Since version8.0 Histogram Statistics 27 ©2021 Percona
  • 55.
    • Since version8.0 • Collected and used by the Optimizer Histogram Statistics 27 ©2021 Percona
  • 56.
    • Since version8.0 • Collected and used by the Optimizer • Visible in Information Schema mysql select HISTOGRAM from information_schema.column_statistics - where table_name=’example’G *************************** 1. row *************************** HISTOGRAM: {buckets: [[1, 0.6], [2, 0.8], [3, 1.0]], data-type: int, null-values: 0.0, collation-id: 8, last-updated: 2018-11-07 09:07:19.791470, sampling-rate: 1.0, histogram-type: singleton, number-of-buckets-specified: 3} 1 row in set (0.00 sec) Histogram Statistics 27 ©2021 Percona
  • 57.
    • Since version8.0 • Collected and used by the Optimizer • Visible in Information Schema • Affects query execution plan, but not access • SELECT ... FROM a JOIN b vs SELECT ... FROM b JOIN a Histogram Statistics 27 ©2021 Percona
  • 58.
    • Since version8.0 • Collected and used by the Optimizer • Visible in Information Schema • Affects query execution plan, but not access More details Histogram Statistics 27 ©2021 Percona
  • 59.
    1 2 34 5 6 7 8 9 10 0 200 400 600 800 Indexes: Number of Items with Same Value 28 ©2021 Percona
  • 60.
    1 2 34 5 6 7 8 9 10 0 200 400 600 800 Indexes: Cardinality 29 ©2021 Percona
  • 61.
    1 2 34 5 6 7 8 9 10 0 200 400 600 800 Histograms: Number of Values in Each Bucket 30 ©2021 Percona
  • 62.
    1 2 34 5 6 7 8 9 10 0 0.2 0.4 0.6 0.8 1 Histograms: Data in the Histogram 31 ©2021 Percona
  • 63.
    • Data distributionis far from uniform • Query accesses two or more tables • Indexes cannot be used • Maintaining them is too expensive • They are already used for different conditions Create Histograms 32 ©2021 Percona
  • 64.
  • 65.
  • 66.
    • EXPLAIN • Estimatesthe query execution plan 5.6- EXTENDED 5.6- PARTITIONS 5.6+ FORMAT=JSON 8.0+ FORMAT=TREE How to Find how MySQL Uses Indexes 35 ©2021 Percona
  • 67.
    • EXPLAIN • Estimatesthe query execution plan 5.6- EXTENDED 5.6- PARTITIONS 5.6+ FORMAT=JSON 8.0+ FORMAT=TREE • INFORMATION SCHEMA.OPTIMIZER TRACE • Real data, collected when query was executing • Advanced topic How to Find how MySQL Uses Indexes 35 ©2021 Percona
  • 68.
    mysql EXPLAIN SELECTfirst_name, last_name, title, salary FROM employees - JOIN salaries USING(emp_no) JOIN titles USING(emp_no) - WHERE salary = (SELECT MAX(salary) FROM salaries - JOIN titles WHERE titles.to_date CURDATE()); +----+-------------+-----------+------------+------+---------------+---------+---------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | +----+-------------+-----------+------------+------+---------------+---------+---------+ | 1 | PRIMARY | employees | NULL | ALL | PRIMARY | NULL | NULL ... | 1 | PRIMARY | salaries | NULL | ref | PRIMARY | PRIMARY | 4 ... | 1 | PRIMARY | titles | NULL | ref | PRIMARY | PRIMARY | 4 ... | 2 | SUBQUERY | titles | NULL | ALL | NULL | NULL | NULL ... | 2 | SUBQUERY | salaries | NULL | ALL | NULL | NULL | NULL ... +----+-------------+-----------+------------+------+---------------+---------+---------+ +----+-...-+----------------------------+---------+----------+-------------------------------+ | id | ... | ref | rows | filtered | Extra | +----+-...-+----------------------------+---------+----------+-------------------------------+ | 1 | ... | NULL | 299113 | 100.00 | NULL | | 1 | ... | employees.employees.emp_no | 9 | 10.00 | Using where | | 1 | ... | employees.employees.emp_no | 1 | 100.00 | Using index | | 2 | ... | NULL | 442189 | 33.33 | Using where | | 2 | ... | NULL | 2838426 | 100.00 | Using join buffer (hash join) | +----+-...-+----------------------------+---------+----------+-------------------------------+ 5 rows in set, 1 warning (0,01 sec) 299113 * 9 * 1 * 442189 * 2838426 = 3,378,806,408,204,514,738 EXPLAIN: rows 36 ©2021 Percona
  • 69.
    mysql EXPLAIN SELECTtitle, MAX(salary) AS maxs FROM employees - JOIN salaries USING(emp_no) JOIN titles USING(emp_no) - GROUP BY title ORDER BY maxs DESC; +----+-------------+-----------+------------+-------+---------------+---------+---------+ | id | select_type | table | partitions | type | possible_keys | key | key_len +----+-------------+-----------+------------+-------+---------------+---------+---------+ | 1 | SIMPLE | employees | NULL | index | PRIMARY | PRIMARY | 4 ... | 1 | SIMPLE | titles | NULL | ref | PRIMARY,title | PRIMARY | 4 ... | 1 | SIMPLE | salaries | NULL | ref | PRIMARY | PRIMARY | 4 ... +----+-------------+-----------+------------+-------+---------------+---------+---------+ EXPLAIN: type 37 ©2021 Percona
  • 70.
    ALL Read allrows Query Execution Type 38 ©2021 Percona
  • 71.
    ALL Read allrows index Read all rows from the index Query Execution Type 38 ©2021 Percona
  • 72.
    ALL Read allrows index Read all rows from the index range Range: N ... M Query Execution Type 38 ©2021 Percona
  • 73.
    ALL Read allrows index Read all rows from the index range Range: N ... M subquery Subqueries index subquery unique subquery Query Execution Type 38 ©2021 Percona
  • 74.
    ALL Read allrows index Read all rows from the index range Range: N ... M subquery Subqueries merge Combination of two indexes index merge Query Execution Type 38 ©2021 Percona
  • 75.
    ALL Read allrows index Read all rows from the index range Range: N ... M subquery Subqueries merge Combination of two indexes ref Comparision with single value ref or null ref eq ref Query Execution Type 38 ©2021 Percona
  • 76.
    ALL Read allrows index Read all rows from the index range Range: N ... M subquery Subqueries merge Combination of two indexes ref Comparision with single value fulltext Fulltext index Query Execution Type 38 ©2021 Percona
  • 77.
    ALL Read allrows index Read all rows from the index range Range: N ... M subquery Subqueries merge Combination of two indexes ref Comparision with single value fulltext Fulltext index const Single row Query Execution Type 38 ©2021 Percona
  • 78.
    ALL Read allrows index Read all rows from the index range Range: N ... M subquery Subqueries merge Combination of two indexes ref Comparision with single value fulltext Fulltext index const Single row system Table has single row Query Execution Type 38 ©2021 Percona
  • 79.
    mysql EXPLAIN SELECTtitle, MAX(salary) AS maxs FROM employees - JOIN salaries USING(emp_no) JOIN titles USING(emp_no) - GROUP BY title ORDER BY maxs DESC; +----+-------------+-----------+------------+-------+---------------+---------+---------+ | id | select_type | table | partitions | type | possible_keys | key | key_len +----+-------------+-----------+------------+-------+---------------+---------+---------+ | 1 | SIMPLE | employees | NULL | index | PRIMARY | PRIMARY | 4 ... | 1 | SIMPLE | titles | NULL | ref | PRIMARY,title | PRIMARY | 4 ... | 1 | SIMPLE | salaries | NULL | ref | PRIMARY | PRIMARY | 4 ... +----+-------------+-----------+------------+-------+---------------+---------+---------+ EXPLAIN: key and possible keys 39 ©2021 Percona
  • 80.
    mysql EXPLAIN SELECTtitle, MAX(salary) AS maxs FROM employees - JOIN salaries USING(emp_no) - JOIN titles USING(emp_no) - GROUP BY title ORDER BY maxs DESC; +----+-------------+-----------+------------+-------+---------------+---------+---------+ | id | select_type | table | partitions | type | possible_keys | key | key_len +----+-------------+-----------+------------+-------+---------------+---------+---------+ | 1 | SIMPLE | employees | NULL | index | PRIMARY | PRIMARY | 4 ... | 1 | SIMPLE | titles | NULL | ref | PRIMARY,title | PRIMARY | 4 ... | 1 | SIMPLE | salaries | NULL | ref | PRIMARY | PRIMARY | 4 ... +----+-------------+-----------+------------+-------+---------------+---------+---------+ EXPLAIN: JOIN order 40 ©2021 Percona
  • 81.
    mysql EXPLAIN SELECTfirst_name, last_name, title, salary FROM employees - JOIN salaries USING(emp_no) JOIN titles USING(emp_no) - WHERE salary IN (SELECT MAX(salary) FROM salaries - JOIN titles WHERE titles.to_date CURDATE()); ... 5 rows in set, 1 warning (0,01 sec) mysql SHOW WARNINGSG *************************** 1. row *************************** Level: Note Code: 1003 Message: /* select#1 */ select ‘employees‘.‘employees‘.‘first_name‘ AS ‘first_name‘, ‘employees‘.‘employees‘.‘last_name‘ AS ‘last_name‘,‘employees‘.‘salaries‘.‘salary‘ AS ‘salary‘ from ‘employees‘.‘employees‘ join ‘employees‘.‘salaries‘ where ((‘employees‘.‘salaries‘.‘emp_no‘ = ‘employees‘.‘employees‘.‘emp_no‘) and in_optimizer(‘employees‘.‘salaries‘.‘salary‘,‘employees‘.‘salaries‘.‘salary‘ in ( materialize (/* select#2 */ select max(‘employees‘.‘salaries‘.‘salary‘) from ‘employees‘.‘salaries‘ join ‘employees‘.‘titles‘ where (‘employees‘.‘titles‘.‘to_date‘ cache(curdate())) having true ), primary_index_lookup(‘employees‘.‘salaries‘.‘salary‘ in temporary table on auto_distinct_key where ((‘employees‘.‘salaries‘.‘salary‘ = ‘materialized_subquery‘.‘MAX(salary)‘)))))) 1 row in set (0,00 sec) Actual Query 41 ©2021 Percona
  • 82.
    mysql explain select* from t1G *************************** 1. row *************************** ... rows: 12 Extra: NULL mysql explain select * from t1 where f2=12G *************************** 1. row *************************** ... key: NULL ... rows: 12 Extra: Using where Effect of Indexes: Before 42 ©2021 Percona
  • 83.
    mysql alter tablet1 add index(f2); Query OK, 12 rows affected (0.07 sec) Records: 12 Duplicates: 0 Warnings: 0 mysql explain select * from t1 where f2=12G *************************** 1. row *************************** ... key: f2 key_len: 5 ref: const rows: 1 Extra: NULL 1 row in set (0.00 sec) Effect of Indexes: After 43 ©2021 Percona
  • 84.
  • 85.
    • EXPLAIN isoptimistic mysql explain select * from ol - where thread_id=10432 and site_id != 9939 - order by id limit 3G *************************** 1. row *************************** id: 1 | ref: NULL select_type: SIMPLE | rows: 33 table: ol | filtered: 8.07 partitions: NULL | Extra: Using where type: index possible_keys: thread_id key: PRIMARY key_len: 4 1 row in set, 1 warning (0,00 sec) Handler * Status Variables 45 ©2021 Percona
  • 86.
    • ’Handler *’show truth mysql FLUSH STATUS; mysql select * from ol - where thread_id=10432 and site_id != 9939 - order by id limit 3; mysql show status like ’Handler%’; +----------------------------+--------+ | Variable_name | Value | +----------------------------+--------+ ... | Handler_read_first | 1 | | Handler_read_key | 1 | | Handler_read_last | 0 | | Handler_read_next | 100000 | ... Handler * Status Variables 45 ©2021 Percona
  • 87.
  • 88.
    • SHOW [FULL]PROCESSLIST • INFORMATION SCHEMA.PROCESSLIST • performance schema.THREADS PROCESSLIST 47 ©2021 Percona
  • 89.
    • SHOW [FULL]PROCESSLIST • INFORMATION SCHEMA.PROCESSLIST • performance schema.THREADS • Your first alert of performance issue PROCESSLIST 47 ©2021 Percona
  • 90.
    • SHOW [FULL]PROCESSLIST • INFORMATION SCHEMA.PROCESSLIST • performance schema.THREADS • Your first alert of performance issue • Shows all running queries PROCESSLIST 47 ©2021 Percona
  • 91.
    • Can beseen in the PROCESSLIST mysql show processlistG ************************ 1. row ************************ Id: 7 User: root Host: localhost:48799 db: employees Command: Query Time: 2 State: Sending data Info: select count(*) from employees join titles using(emp_no) where title=’Senior Engineer’ ... Execution Stages 48 ©2021 Percona
  • 92.
    • Can beseen in the PROCESSLIST • Very useful when you need to answer on the question: ”What is my server doing now?” Execution Stages 48 ©2021 Percona
  • 93.
    • PERFORMANCE SCHEMA.EVENTSSTAGES * mysql select eshl.event_name, substr(sql_text, 1, 15) ‘sql‘, - eshl.timer_wait/1000000000000 w_s - from events_stages_history_long - eshl join events_statements_history_long esthl on - (eshl.nesting_event_id = esthl.event_id) where - esthl.current_schema=’employees’ and sql_text like - ’select count(*) from employees%’ - order by eshl.timer_start asc; +--------------------------------+-----------------+--------+ | event_name | sql | w_s | +--------------------------------+-----------------+--------+ | stage/sql/starting | select count(*) | 0.0002 | | stage/sql/checking permissions | select count(*) | 0.0000 | ... Execution Stages 48 ©2021 Percona
  • 94.
    • PERFORMANCE SCHEMA.EVENTSSTAGES * ... | stage/sql/checking permissions | select count(*) | 0.0000 | | stage/sql/Opening tables | select count(*) | 0.0000 | | stage/sql/init | select count(*) | 0.0001 | | stage/sql/System lock | select count(*) | 0.0000 | | stage/sql/optimizing | select count(*) | 0.0000 | | stage/sql/statistics | select count(*) | 0.0001 | | stage/sql/preparing | select count(*) | 0.0000 | | stage/sql/executing | select count(*) | 0.0000 | | stage/sql/Sending data | select count(*) | 5.4915 | | stage/sql/end | select count(*) | 0.0000 | ... Execution Stages 48 ©2021 Percona
  • 95.
    • Status variables mysqlflush status; Query OK, 0 rows affected (0,01 sec) mysql select count(*) from employees join titles using(emp_no) - where title=’Senior Engineer’; +----------+ | count(*) | +----------+ | 97750 | +----------+ 1 row in set (5,44 sec) Temporary Tables and Other Job 49 ©2021 Percona
  • 96.
    • Status variables mysqlselect * from performance_schema.session_status - where variable_name in (’Created_tmp_tables’, - ’Created_tmp_disk_tables’, ’Select_full_join’, - ’Select_full_range_join’, ’Select_range’, - ’Select_range_check’, ’Select_scan’, ’Sort_merge_passes’, - ’Sort_range’, ’Sort_rows’, ’Sort_scan’) - and variable_value 0; +------------------------+----------------+ | VARIABLE_NAME | VARIABLE_VALUE | +------------------------+----------------+ | Select_scan | 2 | +------------------------+----------------+ 1 row in set (0,00 sec) Temporary Tables and Other Job 49 ©2021 Percona
  • 97.
    • PERFORMANCE SCHEMA.EVENTSSTATEMENTS * mysql select * from events_statements_history_long - where sql_text like - ’select count(*) from employees join %’G *************************** 1. row **************************** ... ROWS_SENT: 1 SELECT_RANGE_CHECK: 0 ROWS_EXAMINED: 541058 SELECT_SCAN: 1 CREATED_TMP_DISK_TABLES: 0 SORT_MERGE_PASSES: 0 CREATED_TMP_TABLES: 0 SORT_RANGE: 0 SELECT_FULL_JOIN: 0 SORT_ROWS: 0 SELECT_FULL_RANGE_JOIN: 0 SORT_SCAN: 0 SELECT_RANGE: 0 NO_INDEX_USED: 0 Temporary Tables and Other Job 49 ©2021 Percona
  • 98.
    • sys.statement analysis mysqlselect * from statement_analysis where query like - ’SELECT COUNT ( * ) FROM ‘emplo%’ and db=’employees’G *************************** 1. row *************************** query: SELECT COUNT ( * ) FROM ‘emplo ... ‘emp_no‘ ) WHE... db: employees max_latency: 5.59 s full_scan: avg_latency: 5.41 s exec_count: 7 lock_latency: 2.24 ms err_count: 0 rows_sent: 7 warn_count: 0 rows_sent_avg: 1 total_latency: 37.89 s rows_examined: 3787406 Temporary Tables and Other Job 49 ©2021 Percona
  • 99.
    • sys.statement analysis rows_examined_avg:541058 rows_affected: 0 rows_affected_avg: 0 tmp_tables: 0 tmp_disk_tables: 0 rows_sorted: 0 sort_merge_passes: 0 digest: 4086bc3dc6510a1d9c8f2fe1f59f0943 first_seen: 2016-04-14 15:19:19 last_seen: 2016-04-14 16:13:14 Temporary Tables and Other Job 49 ©2021 Percona
  • 100.
    How to AffectQuery Plans
  • 101.
    • Index statistics •Histogram statistics • Optimizer switches • Bugs in optimizer What has an Effect on Query Optimizer Plans? 51 ©2021 Percona
  • 102.
    • Collected bystorage engine Index Statistics 52 ©2021 Percona
  • 103.
    • Collected bystorage engine • Used by Optimizer Index Statistics 52 ©2021 Percona
  • 104.
    • Can beexamined by SHOW INDEX mysql show index from sbtest1; +---------+----------+-------------+-------------+ | Table | Key_name | Column_name | Cardinality | +---------+----------+-------------+-------------+ | sbtest1 | k_1 | k | 49142 | +---------+----------+-------------+-------------+ mysql select count(distinct id), - count(distinct k) from sbtest1; +--------------------+-------------------+ | count(distinct id) | count(distinct k) | +--------------------+-------------------+ | 100000 | 17598 | +--------------------+-------------------+ Index Statistics 52 ©2021 Percona
  • 105.
    • Can beupdated • ANALYZE TABLE • If does not help: rebuild the table OPTIMIZE TABLE ALTER TABLE ENGINE=INNODB; ANALYZE TABLE Index Statistics 52 ©2021 Percona
  • 106.
    • Since version8.0 Histogram Statistics 53 ©2021 Percona
  • 107.
    • Since version8.0 • Collected and used by the Optimizer Histogram Statistics 53 ©2021 Percona
  • 108.
    • Since version8.0 • Collected and used by the Optimizer • Visible in Information Schema mysql select HISTOGRAM from information_schema.column_statistics - where table_name=’example’G *************************** 1. row *************************** HISTOGRAM: {buckets: [[1, 0.6], [2, 0.8], [3, 1.0]], data-type: int, null-values: 0.0, collation-id: 8, last-updated: 2018-11-07 09:07:19.791470, sampling-rate: 1.0, histogram-type: singleton, number-of-buckets-specified: 3} 1 row in set (0.00 sec) Histogram Statistics 53 ©2021 Percona
  • 109.
    • Since version8.0 • Collected and used by the Optimizer • Visible in Information Schema More details Histogram Statistics 53 ©2021 Percona
  • 110.
    mysql select @@optimizer_switchG ***************************1. row *************************** @@optimizer_switch: index_merge=on,index_merge_union=on, index_merge_sort_union=on,index_merge_intersection=on, engine_condition_pushdown=on,index_condition_pushdown=on, mrr=on,mrr_cost_based=on, block_nested_loop=on,batched_key_access=off, materialization=on,semijoin=on,loosescan=on,firstmatch=on, duplicateweedout=on,subquery_materialization_cost_based=on, use_index_extensions=on,condition_fanout_filter=on, derived_merge=on 1 row in set (0,00 sec) Optimizer Switches 54 ©2021 Percona
  • 111.
    • Turn ONand OFF particular optimization Optimizer Switches 54 ©2021 Percona
  • 112.
    • Turn ONand OFF particular optimization • Can be unhelpful • Especially for queries, tuned for previous versions Optimizer Switches 54 ©2021 Percona
  • 113.
    • Turn ONand OFF particular optimization • Can be unhelpful • Work with them as with any other option • Turn OFF and try SET optimizer_switch = ’use_index_extensions=off’; SELECT ... EXPLAIN SELECT ... Optimizer Switches 54 ©2021 Percona
  • 114.
    • Turn ONand OFF particular optimization • Can be unhelpful • Work with them as with any other option • If helps implement in queries SELECT /*+ SEMIJOIN(FIRSTMATCH, LOOSESCAN) */ * FROM t1 ...; SELECT /*+ BKA(t1) NO_BKA(t2) */ * FROM t1 INNER JOIN t2 WHERE ...; Optimizer Switches 54 ©2021 Percona
  • 115.
    • Optimizer choseswrong plan for no reason Bugs in Optimizer 55 ©2021 Percona
  • 116.
    • Optimizer choseswrong plan for no reason • Statistics are up to date • Histograms are not usable Bugs in Optimizer 55 ©2021 Percona
  • 117.
    • Optimizer choseswrong plan for no reason • Statistics are up to date • Histograms are not usable • Solution • Use index hints FORCE INDEX IGNORE INDEX Bugs in Optimizer 55 ©2021 Percona
  • 118.
    • Optimizer choseswrong plan for no reason • Statistics are up to date • Histograms are not usable • Solution • On every upgrade • Remove index hints • Test if the query improved • You must do it even for minor version upgrades! Bugs in Optimizer 55 ©2021 Percona
  • 119.
    • EXPLAIN isessential for query tuning • Real job is done by storage engine • Index statistics affect query execution plan • All index hints, optimizer hints and other workarounds must be validated on each upgrade Summary 56 ©2021 Percona
  • 120.
    EXPLAIN Syntax EXPLAIN FORMAT=JSONis Cool! Troubleshooting add-ons Optimizer Statistics aka Histograms Optimizer Hints Tracing the Optimizer More Information 57 ©2021 Percona
  • 121.