首先,我要感谢大家参加我的11月9日,2021网络研讨会”<一个href="https://learn.percona.com/introduction-into-mysql-query-tuning-for-devops">介绍MySQL查询调优为Dev (Op)一个>”。可在我们的记录和幻灯片<一个href="https://learn.percona.com/introduction-into-mysql-query-tuning-for-devops">在线研讨会页面一个>。
这里有答案的提问参与者,我无法提供网络研讨会期间由于技术问题我们有经验。
问:如果一个大型/复杂的从里面选择运行一个存储过程,它将使用/不使用相同的优化,因为它将运行直接查询?有什么要记住编写存储过程时,需要运行大型或复杂的选择在他们吗?
只是想澄清,使用存储过程不使用任何相应的表上的索引?
答:任何查询在一个存储过程将优化一样,如果它被称为外的例行公事。我们不能运行
例如,让我们来一个标准的测试数据库<一个href="https://github.com/datacharmer/test_db">员工一个>并执行一个完全无效的查询:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
mysql
- - - - - -
- - - - - -
- - - - - -
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
|
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
|
|
|
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
3
|
然后让我们创建一个存储程序使用这个查询:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
mysql
mysql
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
查询
|
这个例程调用需要相同的时间在我的笔记本电脑如果我单独运行的查询:
|
1
2
3
4
5
6
7
8
9
10
|
mysql
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
|
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
|
|
|
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
3
查询
|
如果我使语句仪表性能模式和再次运行查询我以下输出:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29日
30.
31日
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61年
62年
63年
64年
65年
66年
67年
68年
69年
70年
71年
72年
73年
74年
75年
76年
77年
78年
79年
80年
81年
82年
83年
|
mysql
阅读
你
数据库
改变了
mysql
查询
行
匹配:
mysql
查询
行
匹配:
mysql
数据库
改变了
mysql
查询
mysql
- - - - - -
- - - - - -
- - - - - -
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
|
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
|
|
|
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
3
mysql
- - - - - -
- - - - - -
* * * * * * * * * * * * * * * * * * * * * * * * * * *
THREAD_ID:
EVENT_ID:
END_EVENT_ID:
EVENT_NAME:
来源:
TIMER_START:
TIMER_END:
TIMER_WAIT:
LOCK_TIME:
SQL_TEXT:
加入
工资
摘要:
DIGEST_TEXT:
加入
“工资”
从
“工资”
CURRENT_SCHEMA:
OBJECT_TYPE:
OBJECT_SCHEMA:
OBJECT_NAME:
OBJECT_INSTANCE_BEGIN:
MYSQL_ERRNO:
RETURNED_SQLSTATE:
MESSAGE_TEXT:
错误:
警告:
ROWS_AFFECTED:
ROWS_SENT:
ROWS_EXAMINED:
CREATED_TMP_DISK_TABLES:
CREATED_TMP_TABLES:
SELECT_FULL_JOIN:
SELECT_FULL_RANGE_JOIN:
SELECT_RANGE:
SELECT_RANGE_CHECK:
SELECT_SCAN:
SORT_MERGE_PASSES:
SORT_RANGE:
SORT_ROWS:
SORT_SCAN:
NO_INDEX_USED:
NO_GOOD_INDEX_USED:
NESTING_EVENT_ID:
NESTING_EVENT_TYPE:
NESTING_EVENT_LEVEL:
STATEMENT_ID:
1
|
有趣的部分输出性能与非零值的字段。也就是说,
|
1
2
3
4
|
ROWS_SENT:
ROWS_EXAMINED:
SELECT_SCAN:
NO_INDEX_USED:
|
现在让我们来调用存储过程和比较这些值。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29日
30.
31日
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61年
62年
63年
64年
65年
66年
67年
68年
69年
70年
71年
72年
73年
74年
75年
76年
77年
78年
79年
80年
81年
82年
83年
84年
85年
86年
87年
88年
89年
90年
91年
92年
93年
94年
95年
96年
97年
98年
99年
One hundred.
101年
102年
103年
104年
105年
|
mysql
查询
mysql
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
|
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
|
|
|
+ - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
3
查询
mysql
- - - - - -
- - - - - -
* * * * * * * * * * * * * * * * * * * * * * * * * * *
THREAD_ID:
EVENT_ID:
END_EVENT_ID:
EVENT_NAME:
来源:
TIMER_START:
TIMER_END:
TIMER_WAIT:
LOCK_TIME:
SQL_TEXT:
加入
工资
摘要:
DIGEST_TEXT:
CURRENT_SCHEMA:
OBJECT_TYPE:
OBJECT_SCHEMA:
OBJECT_NAME:
OBJECT_INSTANCE_BEGIN:
MYSQL_ERRNO:
RETURNED_SQLSTATE:
MESSAGE_TEXT:
错误:
警告:
ROWS_AFFECTED:
ROWS_SENT:
ROWS_EXAMINED:
CREATED_TMP_DISK_TABLES:
CREATED_TMP_TABLES:
SELECT_FULL_JOIN:
SELECT_FULL_RANGE_JOIN:
SELECT_RANGE:
SELECT_RANGE_CHECK:
SELECT_SCAN:
SORT_MERGE_PASSES:
SORT_RANGE:
SORT_ROWS:
SORT_SCAN:
NO_INDEX_USED:
NO_GOOD_INDEX_USED:
NESTING_EVENT_ID:
NESTING_EVENT_TYPE:
NESTING_EVENT_LEVEL:
STATEMENT_ID:
* * * * * * * * * * * * * * * * * * * * * * * * * * *
THREAD_ID:
EVENT_ID:
END_EVENT_ID:
EVENT_NAME:
来源:
TIMER_START:
TIMER_END:
TIMER_WAIT:
LOCK_TIME:
SQL_TEXT:
摘要:
DIGEST_TEXT:
CURRENT_SCHEMA:
OBJECT_TYPE:
OBJECT_SCHEMA:
OBJECT_NAME:
OBJECT_INSTANCE_BEGIN:
MYSQL_ERRNO:
RETURNED_SQLSTATE:
MESSAGE_TEXT:
错误:
警告:
ROWS_AFFECTED:
ROWS_SENT:
ROWS_EXAMINED:
CREATED_TMP_DISK_TABLES:
CREATED_TMP_TABLES:
SELECT_FULL_JOIN:
SELECT_FULL_RANGE_JOIN:
SELECT_RANGE:
SELECT_RANGE_CHECK:
SELECT_SCAN:
SORT_MERGE_PASSES:
SORT_RANGE:
SORT_ROWS:
SORT_SCAN:
NO_INDEX_USED:
NO_GOOD_INDEX_USED:
NESTING_EVENT_ID:
NESTING_EVENT_TYPE:
NESTING_EVENT_LEVEL:
STATEMENT_ID:
2
|
这一次,从performance_schema选择。events_statements_history返回两行:一个用于
|
1
2
3
4
|
ROWS_SENT:
ROWS_EXAMINED:
SELECT_SCAN:
NO_INDEX_USED:
|
所以性能指标是完全一样的原始查询。
好吧,这是缓慢的,不是有效的查询。但是使用索引呢?
让我们工艺的另一个例子。考虑一个查询:
|
1
|
选择数(*),
|
和存储过程:
|
1
2
3
4
5
6
|
mysql
- - - - - -
- - - - - -
- - - - - -
- - - - - -
查询
|
现在让我们调用查询并检查数据的性能模式:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29日
30.
31日
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61年
62年
63年
64年
|
mysql
查询
mysql
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
|
|
|
|
|
|
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
7
mysql
- - - - - -
- - - - - -
* * * * * * * * * * * * * * * * * * * * * * * * * * *
THREAD_ID:
EVENT_ID:
END_EVENT_ID:
EVENT_NAME:
来源:
TIMER_START:
TIMER_END:
TIMER_WAIT:
LOCK_TIME:
SQL_TEXT:
摘要:
DIGEST_TEXT:
CURRENT_SCHEMA:
OBJECT_TYPE:
OBJECT_SCHEMA:
OBJECT_NAME:
OBJECT_INSTANCE_BEGIN:
MYSQL_ERRNO:
RETURNED_SQLSTATE:
MESSAGE_TEXT:
错误:
警告:
ROWS_AFFECTED:
ROWS_SENT:
ROWS_EXAMINED:
CREATED_TMP_DISK_TABLES:
CREATED_TMP_TABLES:
SELECT_FULL_JOIN:
SELECT_FULL_RANGE_JOIN:
SELECT_RANGE:
SELECT_RANGE_CHECK:
SELECT_SCAN:
SORT_MERGE_PASSES:
SORT_RANGE:
SORT_ROWS:
SORT_SCAN:
NO_INDEX_USED:
NO_GOOD_INDEX_USED:
NESTING_EVENT_ID:
NESTING_EVENT_TYPE:
NESTING_EVENT_LEVEL:
STATEMENT_ID:
1
|
在这种情况下,图片是不同的:
|
1
2
3
4
5
6
|
ROWS_SENT:
ROWS_EXAMINED:
CREATED_TMP_TABLES:
SELECT_SCAN:
NO_INDEX_USED:
NO_GOOD_INDEX_USED:
|
这仍然不是一个非常有效的查询:它使用索引扫描但它确实使用索引。
让我们检查数据存储过程:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29日
30.
31日
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61年
62年
63年
64年
65年
66年
67年
68年
69年
70年
|
mysql
查询
mysql
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
|
|
|
|
|
|
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
7
查询
mysql
- - - - - -
- - - - - -
* * * * * * * * * * * * * * * * * * * * * * * * * * *
THREAD_ID:
EVENT_ID:
END_EVENT_ID:
EVENT_NAME:
来源:
TIMER_START:
TIMER_END:
TIMER_WAIT:
LOCK_TIME:
SQL_TEXT:
摘要:
DIGEST_TEXT:
CURRENT_SCHEMA:
OBJECT_TYPE:
OBJECT_SCHEMA:
OBJECT_NAME:
OBJECT_INSTANCE_BEGIN:
MYSQL_ERRNO:
RETURNED_SQLSTATE:
MESSAGE_TEXT:
错误:
警告:
ROWS_AFFECTED:
ROWS_SENT:
ROWS_EXAMINED:
CREATED_TMP_DISK_TABLES:
CREATED_TMP_TABLES:
SELECT_FULL_JOIN:
SELECT_FULL_RANGE_JOIN:
SELECT_RANGE:
SELECT_RANGE_CHECK:
SELECT_SCAN:
SORT_MERGE_PASSES:
SORT_RANGE:
SORT_ROWS:
SORT_SCAN:
NO_INDEX_USED:
NO_GOOD_INDEX_USED:
NESTING_EVENT_ID:
NESTING_EVENT_TYPE:
NESTING_EVENT_LEVEL:
STATEMENT_ID:
* * * * * * * * * * * * * * * * * * * * * * * * * * *
THREAD_ID:
EVENT_ID:
END_EVENT_ID:
EVENT_NAME:
<我
|
计数器又有完全相同的值作为独立的语句:
|
1
2
3
4
5
6
|
ROWS_SENT:
ROWS_EXAMINED:
CREATED_TMP_TABLES:
SELECT_SCAN:
NO_INDEX_USED:
NO_GOOD_INDEX_USED:
|
我们有证据证明优化器创建相同的查询计划无论查询被称为内部存储过程。
问:几次的时候建立一个与多个索引,查询一个表解释命令向我展示了一个索引,显然不是最好的选择,我不得不使用武力指数查询中。我永远不会明白为什么有时会发生这种情况,可能有时引擎让这个错误?
答:发动机可以犯错误。如果你想了解更多关于这样的错误我推荐你去尝试
问:我有一个问题关于状态。关于你提到的Handler_read。正在执行查询的时候一直在增加。如果服务器的正常运行时间超过14天,有1000每秒,处理程序将在数百万的范围。一旦我们做一个查询调优车间,我想重置处理程序计数器。如何执行?(看看:同样的时间范围,是否Handler_read减少)
答:为会话使用重置计数器
这是伪代码,如何做到这一点:
|
1
2
3
|
冲洗
状态;
<运行
显示
状态
就像
”
|
问:你能说一下解释扩展吗?我们如何用它来进一步帮助我们调优查询?它显示完整的查询服务器/优化器将执行正确的格式吗?
答:解释扩展包含在标准输出的吗
首先,这是一
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
mysql
* * * * * * * * * * * * * * * * * * * * * * * * * * *
id:
select_type:
表:
分区:
类型:
possible_keys:
关键:
key_len:
裁判:
行:
过滤:
额外的:
1
|
预计,这款发动机将读2838426行,但只有11.11%的人会被用来得到最终结果。这通常表明查询并不是有效的。
相反,查询:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
mysql
* * * * * * * * * * * * * * * * * * * * * * * * * * *
id:
select_type:
表:
分区:
类型:
possible_keys:
关键:
key_len:
裁判:
行:
过滤:
额外的:
1
|
将使用所有442189检索行创建最终的结果集(过滤:100.00)。
的另一个特点
例如,让我们来查询:
|
1
2
|
选择
列出
在哪里
列出
|
然后运行
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29日
30.
31日
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61年
62年
63年
64年
65年
|
mysql
- - - - - -
* * * * * * * * * * * * * * * * * * * * * * * * * * *
id:
select_type:
表:
分区:
类型:
possible_keys:
关键:
key_len:
裁判:
行:
过滤:
额外的:
* * * * * * * * * * * * * * * * * * * * * * * * * * *
id:
select_type:
表:
分区:
类型:
possible_keys:
关键:
key_len:
裁判:
行:
过滤:
额外的:
* * * * * * * * * * * * * * * * * * * * * * * * * * *
id:
select_type:
表:
分区:
类型:
possible_keys:
关键:
key_len:
裁判:
行:
过滤:
额外的:
* * * * * * * * * * * * * * * * * * * * * * * * * * *
id:
select_type:
表:
分区:
类型:
possible_keys:
关键:
key_len:
裁判:
行:
过滤:
额外的:
4
mysql
* * * * * * * * * * * * * * * * * * * * * * * * * * *
水平:
代码:
信息:
“员工”。“员工“last_name”。
在哪里
(('
和
(‘员工’。‘工资’。‘工资’
1
|
在
|
1
|
选择
列出
|
如果我们关掉semi-join优化我们将看到一个不同的警告:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20.
21
22
23
24
25
26
27
28
29日
30.
31日
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
mysql
查询
mysql
* * * * * * * * * * * * * * * * * * * * * * * * * * *
id:
select_type:
表:
分区:
类型:
possible_keys:
关键:
key_len:
裁判:
行:
过滤:
额外的:
* * * * * * * * * * * * * * * * * * * * * * * * * * *
id:
select_type:
表:
分区:
类型:
possible_keys:
关键:
key_len:
裁判:
行:
过滤:
额外的:
* * * * * * * * * * * * * * * * * * * * * * * * * * *
id:
select_type:
表:
分区:
类型:
possible_keys:
关键:
key_len:
裁判:
行:
过滤:
额外的:
3
mysql
* * * * * * * * * * * * * * * * * * * * * * * * * * *
水平:
代码:
信息:
“员工”。“员工“last_name”。
在哪里
<in_optimizer
在
(
在哪里
(‘员工’。‘工资’。‘工资’
<primary_index_lookup
在哪里
(“雇员”。“员工列出的。
1
|
这个特性可以帮助理解为什么一个或使用特定的优化。
问:你有没有使用mysqltuner perl脚本,如果是这样的话,你会认为这是一个短期的选择吗?
答:你的意思是<一个href="https://github.com/major/MySQLTuner-perl">https://github.com/major/MySQLTuner-perl一个>吗?
我只是我的笔记本电脑上运行它,这是我的建议:
推荐- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
一般建议:
减少或消除打开连接和网络问题
配置您的帐户和ip子网,然后用skip-name-resolve = 1更新您的配置
这可能是好的,除非你想从外部提供您的MySQL服务器。
我们将建议提高join_buffer_size直到连接不使用索引。
参见https://dev.mysql.com/doc/internals/en/join-buffer-size.html
(特殊页面的底部的结论)。
我不明白为什么提高
改变innodb_log_file_size之前和/或innodb_log_files_in_group读:<一个href="https://bit.ly/2TcGgtU">https://bit.ly/2TcGgtU一个>
它总是好的阅读用户手册之前改变选项。我建议你每次你跟随性能调优建议。甚至我的。
变量来调整:
join_buffer_size (> 256.0 k,或总是使用索引和连接)
这不能表示没有检查查询。
如果可能的话,通过innodb_buffer_pool_size (> = 713.2)。
这个结论是根据我的数据大小,这是理智的我的笔记本电脑,32 g RAM。但如果数据量大于RAM的机器上的这个建议不会帮助你识别的理想InnoDB缓冲池大小。在这种情况下,我建议你从这开始<一个href="//m.doggingzone.com/blog/2007/11/03/choosing-innodb_buffer_pool_size/">博客一个>最后按照链接。
innodb_log_file_size应该(= 16米)如果可能,所以InnoDB总日志文件大小等于25%的缓冲池大小。
InnoDB重做日志文件大小应该保存尽可能多的数据,所以InnoDB可以刷新数据从缓冲池和重用日志空间,没有执行咄咄逼人<一个href="https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool-flushing.html">冲洗一个>。
如果你有一个小活动数据集但是你写很多你可能InnoDB总日志文件大小大于25%的缓冲池大小。或者,如果你有一个大的数据集,但你的工作量几乎是阅读,你可能非常小的重做日志文件。这个建议本身没有任何意义。
总之,我可以说,MySQL调谐器是一个产品,你的MySQL或MariaDB实例执行静态分析和建议,根据其作者认为的最佳实践是什么。
不幸的是,它不可能优化MySQL相同的方式为所有用例。InnoDB重做日志文件大小超过只是一个例子。还有其他选项,应调整根据不同的工作负载。
我建议你研究产品的使用和理解,为什么你改变。最好从一个问题开始:“我需要解决的问题?”而不是修改随机选择。
对于通用服务器,运行在专用的机器上,您可以使用的选项<一个href="https://dev.mysql.com/doc/refman/8.0/en/innodb-dedicated-server.html">-innodb-dedicated-server一个>。不过,在我看来,它也远非理想。
问:基于混合吗?
答:这是幻灯片的问题“异步副本”,解释了二进制日志格式如何影响复制性能?Mixed-based二进制日志格式指示MySQL日志中的一切




