回答这个问题并不容易。像很多问题一样,最好的回答是“这要看情况...”! 但是,让我们试着给你所有必要的信息来提供最准确的答案。另外,仅仅修复一个SQL可能不够,查找一个有问题的SQL可能会找到多个有问题的SQL。
01
—
最慢的SQL
第一个要优化的候选项是执行时间最长的SQL。为了找到它,我们将使用sys模式与来自performance_schema的视图events_statements_summary_by_digest进行连接(参见:https://lefred.be/content/mysql-8-0-digest-query-samples-in-performance_schema/)。 让我们看看sys模式为我们提供了哪些与我们这项任务相关的内容:
> show tables like 'statements_with%';+---------------------------------------------+| Tables_in_sys (statements_with%) |+---------------------------------------------+| statements_with_errors_or_warnings || statements_with_full_table_scans || statements_with_runtimes_in_95th_percentile || statements_with_sorting || statements_with_temp_tables |+---------------------------------------------+
然后,我们将使用statements_with_runtimes_in_95th_percentile来查找最慢的SQL语句。然而,我们将使用带有原始数据的视图版本(不是人类可读的格式),以便能够按照我们想要的方式对结果进行排序。sys模式视图的原始数据版本以x$开头:
SELECT schema_name, sys.format_time(total_latency) tot_lat, exec_count, sys.format_time(total_latency/exec_count) latency_per_call, query_sample_text FROM sys.x$statements_with_runtimes_in_95th_percentile AS t1 JOIN performance_schema.events_statements_summary_by_digest AS t2 ON t2.digest=t1.digest WHERE schema_name NOT in ('performance_schema', 'sys') ORDER BY (total_latency/exec_count) desc LIMIT 1\G*************************** 1. row *************************** schema_name: library tot_lat: 857.29 ms exec_count: 1 latency_per_call: 857.29 msquery_sample_text: INSERT INTO `books` (`doc`) VALUES ('{\"_id\": \"00005d44289d000000000000007d\", \"title\": \"lucky luke, tome 27 : l alibi\", \"isbn10\": \"2884710086\", \"isbn13\": \"978-2884710084\", \"langue\": \"français\", \"relié\": \"48 pages\", \"authors\": [\"Guylouis (Auteur)\", \"Morris (Illustrations)\"], \"editeur\": \"lucky comics (21 décembre 1999)\", \"collection\": \"lucky luke\", \"couverture\": \" data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDABQODxIPDRQSEBIXFRQYHjIhHhwcHj0sLiQySUBMS0dARkVQWnNiUFVtVkVGZIhlbXd7gYKBTmCNl4x9lnN+gXz/2wBDARUXFx4aHjshITt8U0ZTfHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHz/wAARCAEfANwDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBA...1 row in set (0.2838 sec)
优化这个语句很困难,因为它是一个简单的插入,并且只运行了一次。由于磁盘响应时间的原因,Insert可能会慢一些(写磁盘的方式是完全持久)。拥有太多的索引也会增加响应时间,这就是我建议您查看这两个sys模式视图的原因:
- schema_redundant_indexes
- schema_unused_indexes
由于有query_sample_text字段,我们可以使用explain来生成这个SQL的执行计划。
02
—
全表扫描的SQL
另外一类我建议需要优化的SQL是执行全表扫描的SQL:
SELECT schema_name, sum_rows_examined, (sum_rows_examined/exec_count) avg_rows_call, sys.format_time(total_latency) tot_lat, exec_count, sys.format_time(total_latency/exec_count) AS latency_per_call, query_sample_text FROM sys.x$statements_with_full_table_scans AS t1 JOIN performance_schema.events_statements_summary_by_digest AS t2 ON t2.digest=t1.digest WHERE schema_name NOT in ('performance_schema', 'sys') ORDER BY (total_latency/exec_count) desc LIMIT 1\G*************************** 1. row *************************** schema_name: wp_lefred sum_rows_examined: 268075 avg_rows_call: 3277.0419 tot_lat: 31.31 s exec_count: 124 latency_per_call: 252.47 ms query_sample_text: SELECT count(*) as mytotal FROM wp_posts WHERE (post_content LIKE '%youtube.com/%' OR post_content LIKE '%youtu.be/%') AND post_status = 'publish' 1 row in set (0.0264 sec)
我们可以看到这个查询执行了124次,总执行时间为31.31秒,也就是说每次调用执行252.47毫秒。我们还可以看到,该查询检查了超过268k行,这意味着平均而言,这些全表扫描每个查询检查3277条记录。 这是一个非常值得优化的SQL。
03
—
使用临时表的SQL
创建临时表对于数据库应用来说也不是最优的,如果您有一些很慢的SQL,您应该已经在前面的查询中识别出它们。但是,如果您想专门寻找它们,sys 模式可以帮助您捕捉它们:
SELECT schema_name, sys.format_time(total_latency) tot_lat, exec_count, sys.format_time(total_latency/exec_count) latency_per_call, query_sample_text FROM sys.x$statements_with_temp_tables AS t1 JOIN performance_schema.events_statements_summary_by_digest AS t2 ON t2.digest=t1.digest WHERE schema_name NOT in ('performance_schema', 'sys') AND disk_tmp_tables=1ORDER BY 2 desc,(total_latency/exec_count) desc LIMIT 1\G
幸运的是,我的系统里没有。
04
—
结束语
查询优化并不是DBA工作中最令人兴奋的部分……但却是必须要做的;-)。你现在已经有了一个简单的方法来找到从哪里开始,祝你好运!别忘了,如果你需要任何帮助,你可以随时加入MySQL社区Slack频道!(https://lefred.be/mysql-community-on-slack/)