TIPS
This article is based on MySQL 8.0 To write , theoretical support MySQL 5.6 And higher .
OPTIMIZER_TRACE yes MySQL 5.6 A tracking feature introduced , It can track various decisions made by the optimizer ( For example, the method of accessing the table 、 Various overhead calculations 、 Various conversions, etc ), And record the tracking results to INFORMATION_SCHEMA.OPTIMIZER_TRACE
In the table . This function is off by default , After opening , The following statements can be analyzed :
- SELECT
- INSERT
- REPLACE
- UPDATE
- DELETE
- EXPLAIN
- SET
- DECLARE
- CASE
- IF
- RETURN
- CALL
OPTIMIZER_TRACE Related parameters
TIPS
Reference resources https://dev.mysql.com/doc/internals/en/system-variables-controlling-trace.html
optimizer_trace
- optimizer_trace Main switch , The default value is :
enabled=off,one_line=off
- enabled: Open or not optimizer_trace;on Open for indication ,off Means closing .
- one_line: Whether to open single line storage .on Open for indication ;off Means closing , Will use standard JSON Format storage . Set to on There will be good format , Set to off Can save some space .
- optimizer_trace Main switch , The default value is :
optimizer_trace_features
- control optimizer_trace What to track , The default value is :
greedy_search=on,range_optimizer=on,dynamic_range=on,repeated_subselect=on
, Indicates that all tracking items are turned on .
- control optimizer_trace What to track , The default value is :
greedy_search: Whether to track greedy search , The greedy algorithm can be found in https://blog.csdn.net/weixin_42813521/article/details/105563103
- range_optimizer: Whether to track the scope optimizer
dynamic_range: Whether to track dynamic range optimization
- repeated_subselect: Whether to track subqueries , If I set it to off, Just follow the first one Item_subselect Implementation
See https://dev.mysql.com/doc/internals/en/optimizer-features-to-trace.html
optimizer_trace_limit: control optimizer_trace How many results to show , Default 1
optimizer_trace_max_mem_size:optimizer_trace Maximum memory allowed for stack information , Default 1048576
optimizer_trace_offset: The first thing to show optimizer trace The offset , Default -1.
end_markers_in_json: If JSON Large structure , It's hard to pair the right bracket with the left bracket . To help readers read , It can be set to on, This will add a comment around the right parenthesis , Default off.
Reference resources : https://dev.mysql.com/doc/internals/en/end-markers-in-json-system-variable.html
TIPS
The above parameters are available SET Statement operation , for example , Use the following command to open OPTIMIZER TRACE
1
2 > SET OPTIMIZER_TRACE="enabled=on",END_MARKERS_IN_JSON=on;
>
Also available SET GLOBAL Global on . But even if it turns on globally OPTIMIZER_TRACE, Every Session It can only track the statements it executes itself :
1
2 > SET GLOBAL OPTIMIZER_TRACE="enabled=on",END_MARKERS_IN_JSON=on;
>
optimizer_trace_limit and optimizer_trace_offset These two parameters are often used together , for example :
1
2 > SET optimizer_trace_offset=<OFFSET>, optimizer_trace_limit=<LIMIT>
>
These two parameters are used together , It's kind of similar MySQL Inside limit sentence .
By default , because optimizer_trace_offset=-1,optimizer_trace_limit=1, Record the last one SQL sentence , Display time , Every show 1 Data ;
If change to
SET optimizer_trace_offset=-2, optimizer_trace_limit=1
, The next to last entry will be recorded SQL sentence ;of optimizer_trace_offset 、optimizer_trace_limit For more details , May refer to https://dev.mysql.com/doc/internals/en/tuning-trace-purging.html
OPTIMIZER_TRACE Use
Turn on OPTIMIZER_TRACE function , And set the number of data entries to be displayed :
1
2SET OPTIMIZER_TRACE="enabled=on",END_MARKERS_IN_JSON=on;
SET optimizer_trace_offset=-30, optimizer_trace_limit=30;Send what you want to analyze SQL sentence , for example :
1
2
3
4select *
from salaries
where from_date = '1986-06-26'
and to_date = '1987-06-26';Use the following statements to analyze , The following results can be obtained :
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227mysql> SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE limit 30 \G;
*************************** 1. row ***************************
QUERY: select *
from salaries
where from_date = '1986-06-26'
and to_date = '1987-06-26'
TRACE: {
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select `salaries`.`emp_no` AS `emp_no`,`salaries`.`salary` AS `salary`,`salaries`.`from_date` AS `from_date`,`salaries`.`to_date` AS `to_date` from `salaries` where ((`salaries`.`from_date` = '1986-06-26') and (`salaries`.`to_date` = '1987-06-26'))"
}
] /* steps */
} /* join_preparation */
},
{
"join_optimization": {
"select#": 1,
"steps": [
{
"condition_processing": {
"condition": "WHERE",
"original_condition": "((`salaries`.`from_date` = '1986-06-26') and (`salaries`.`to_date` = '1987-06-26'))",
"steps": [
{
"transformation": "equality_propagation",
"resulting_condition": "(multiple equal('1986-06-26', `salaries`.`from_date`) and multiple equal('1987-06-26', `salaries`.`to_date`))"
},
{
"transformation": "constant_propagation",
"resulting_condition": "(multiple equal('1986-06-26', `salaries`.`from_date`) and multiple equal('1987-06-26', `salaries`.`to_date`))"
},
{
"transformation": "trivial_condition_removal",
"resulting_condition": "(multiple equal(DATE'1986-06-26', `salaries`.`from_date`) and multiple equal(DATE'1987-06-26', `salaries`.`to_date`))"
}
] /* steps */
} /* condition_processing */
},
{
"substitute_generated_columns": {
} /* substitute_generated_columns */
},
{
"table_dependencies": [
{
"table": "`salaries`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": [
] /* depends_on_map_bits */
}
] /* table_dependencies */
},
{
"ref_optimizer_key_uses": [
{
"table": "`salaries`",
"field": "from_date",
"equals": "DATE'1986-06-26'",
"null_rejecting": false
},
{
"table": "`salaries`",
"field": "to_date",
"equals": "DATE'1987-06-26'",
"null_rejecting": false
}
] /* ref_optimizer_key_uses */
},
{
"rows_estimation": [
{
"table": "`salaries`",
"range_analysis": {
"table_scan": {
"rows": 2838216,
"cost": 286799
} /* table_scan */,
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": false,
"cause": "not_applicable"
},
{
"index": "salaries_from_date_to_date_index",
"usable": true,
"key_parts": [
"from_date",
"to_date",
"emp_no"
] /* key_parts */
}
] /* potential_range_indexes */,
"setup_range_conditions": [
] /* setup_range_conditions */,
"group_index_range": {
"chosen": false,
"cause": "not_group_by_or_distinct"
} /* group_index_range */,
"skip_scan_range": {
"potential_skip_scan_indexes": [
{
"index": "salaries_from_date_to_date_index",
"usable": false,
"cause": "query_references_nonkey_column"
}
] /* potential_skip_scan_indexes */
} /* skip_scan_range */,
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "salaries_from_date_to_date_index",
"ranges": [
"0xda840f <= from_date <= 0xda840f AND 0xda860f <= to_date <= 0xda860f"
] /* ranges */,
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"rows": 86,
"cost": 50.909,
"chosen": true
}
] /* range_scan_alternatives */,
"analyzing_roworder_intersect": {
"usable": false,
"cause": "too_few_roworder_scans"
} /* analyzing_roworder_intersect */
} /* analyzing_range_alternatives */,
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "salaries_from_date_to_date_index",
"rows": 86,
"ranges": [
"0xda840f <= from_date <= 0xda840f AND 0xda860f <= to_date <= 0xda860f"
] /* ranges */
} /* range_access_plan */,
"rows_for_plan": 86,
"cost_for_plan": 50.909,
"chosen": true
} /* chosen_range_access_summary */
} /* range_analysis */
}
] /* rows_estimation */
},
{
"considered_execution_plans": [
{
"plan_prefix": [
] /* plan_prefix */,
"table": "`salaries`",
"best_access_path": {
"considered_access_paths": [
{
"access_type": "ref",
"index": "salaries_from_date_to_date_index",
"rows": 86,
"cost": 50.412,
"chosen": true
},
{
"access_type": "range",
"range_details": {
"used_index": "salaries_from_date_to_date_index"
} /* range_details */,
"chosen": false,
"cause": "heuristic_index_cheaper"
}
] /* considered_access_paths */
} /* best_access_path */,
"condition_filtering_pct": 100,
"rows_for_plan": 86,
"cost_for_plan": 50.412,
"chosen": true
}
] /* considered_execution_plans */
},
{
"attaching_conditions_to_tables": {
"original_condition": "((`salaries`.`to_date` = DATE'1987-06-26') and (`salaries`.`from_date` = DATE'1986-06-26'))",
"attached_conditions_computation": [
] /* attached_conditions_computation */,
"attached_conditions_summary": [
{
"table": "`salaries`",
"attached": "((`salaries`.`to_date` = DATE'1987-06-26') and (`salaries`.`from_date` = DATE'1986-06-26'))"
}
] /* attached_conditions_summary */
} /* attaching_conditions_to_tables */
},
{
"finalizing_table_conditions": [
{
"table": "`salaries`",
"original_table_condition": "((`salaries`.`to_date` = DATE'1987-06-26') and (`salaries`.`from_date` = DATE'1986-06-26'))",
"final_table_condition ": null
}
] /* finalizing_table_conditions */
},
{
"refine_plan": [
{
"table": "`salaries`"
}
] /* refine_plan */
}
] /* steps */
} /* join_optimization */
},
{
"join_execution": {
"select#": 1,
"steps": [
] /* steps */
} /* join_execution */
}
] /* steps */
}
MISSING_BYTES_BEYOND_MAX_MEM_SIZE: 0
INSUFFICIENT_PRIVILEGES: 0
1 row in set (0.00 sec)
Analysis completed , close OPTIMIZER_TRACE
1
SET optimizer_trace="enabled=off";
OPTIMIZER_TRACE Result analysis
From the above results we can see that ,OPTIMIZER_TRACE There are four fields :
- QUERY: Query statement
- TRACE:QUERY The tracking information of the statement corresponding to the field
- MISSING_BYTES_BEYOND_MAX_MEM_SIZE: When the tracking information is too long , The number of bytes of truncated trace information .
- INSUFFICIENT_PRIVILEGES: Whether the user executing the trace statement has permission to view the object . When you don't have permission , The column information is 1 And TRACE Field is empty , In general, when calling with SQL SECURITY DEFINER In the case of stored procedures , This problem will arise .
TIPS
Reference resources : https://dev.mysql.com/doc/refman/8.0/en/optimizer-trace-table.html
The bottom line is this TRACE The contents of the field . Let's analyze it paragraph by paragraph :
join_preparation
join_preparation The paragraph shows the execution of the preparation phase .
1 |
{ |
join_optimization
join_optimization It shows the execution process of the optimization phase , Is to analyze the OPTIMIZER TRACE Key points of . It's super long , And there are many steps , You might as well follow the steps to analyze :
condition_processing
This section is used for conditional processing , Mainly for WHERE Conditions are optimized .
1 |
"condition_processing": { |
among :
- condition: Optimize object types .WHERE A conditional sentence or HAVING Conditional sentence
- original_condition: The original statement before optimization
- steps: It mainly includes three steps , Namely quality_propagation( Equivalent conditional sentence conversion ),constant_propagation( Constant conditional sentence conversion ),trivial_condition_removal( Invalid condition removed conversion )
- transformation: Transformational sentences
- resulting_condition: The result of the transformation is output
substitute_generated_columns
substitute_generated_columns Used to replace the virtual generated column
1 |
"substitute_generated_columns": { |
table_dependencies
Analyze the dependencies between tables
1 |
{ |
among :
- table: Table names involved , If there are aliases , It will also show
- row_may_be_null: Is it possible for NULL, Here means JOIN After the operation , This may not be the data in the table NULL. If you use LEFT JOIN, On the last table row_may_be_null Will be displayed as true
- map_bit: The mapping number of the table , from 0 Began to increase
- depends_on_map_bits: Dependency mapping table . Mainly when using STRAIGHT_JOIN Forcibly control the connection order or LEFT JOIN/RIGHT JOIN When there is a difference in order , Will be in depends_on_map_bits Showing the front table in map_bit value .
ref_optimizer_key_uses
List all available ref Index of type . If multiple parts of a composite index are used ( For example, in this case , Yes index(from_date, to_date) Multi column index of ), Will be in ref_optimizer_key_uses Several elements are listed below , Each element will list ref The index used and the corresponding value .
1 |
{ |
rows_estimation
seeing the name of a thing one thinks of its function , Used to estimate the number of records that need to be scanned .
1 |
{ |
among :
- table: Table name
range_analysis:
table_scan: If it's a full table scan , How many lines need to be scanned (row,2838216), And the cost of it (cost,286799)
potential_range_indexes: List all indexes in the table and analyze whether they are available . If not available , It lists the reasons for the unavailability ; If available, lists the available fields in the index ;
setup_range_conditions: If there's a pushdown condition , Then query with conditional consideration range
group_index_range: When using the GROUP BY or DISTINCT when , Is there a suitable index available . When not used GROUP BY or DISTINCT when , Will be displayed chosen=false, cause=not_group_by_or_distinct; If used GROUP BY or DISTINCT, But when querying multiple tables , Will be displayed chosen=false,cause =not_single_table. In other cases, an attempt is made to analyze the available indexes (potential_group_range_indexes) And calculate the number of scanning lines and the cost
skip_scan_range: Have you used skip scan
TIPS
skip_scan_range yes MySQL 8.0 New features , Please refer to https://blog.csdn.net/weixin_43970890/article/details/89494915
analyzing_range_alternatives: Analyze the cost of using each index
- range_scan_alternatives:range Scanning analysis
- index: Index name
- ranges:range Range of scanning conditions
- index_dives_for_eq_ranges: Have you used index dive, The value will be set by the parameter eq_range_index_dive_limit Variable value influence .
- rowid_ordered: The range Whether the result set of the scan is based on PK Value to sort
- using_mrr: Have you used mrr
- index_only: Indicates whether the overlay index is used
- rows: Number of lines scanned
- cost: The cost of using the index
- chosen: Indicates whether the index is used
- analyzing_roworder_intersect: Analyze whether index merging is used (index merge), If not used , Will be in cause Why ; If index merging is used , The cost of index merging is shown in this section .
- range_scan_alternatives:range Scanning analysis
chosen_range_access_summary: In the previous step, we analyzed the methods and costs of using various indexes , After getting some intermediate results , stay summary The stage summarizes the intermediate results of the previous stage to confirm the final plan
- range_access_plan:range Scan the final selected execution plan .
- type: Show the execution plan type, If index merging is used , Is displayed index_roworder_intersect
- index: Index name
- rows: Number of lines scanned
- ranges:range Range of scanning conditions
- rows_for_plan: The number of scan lines for this execution plan
- cost_for_plan: The implementation cost of the implementation plan
- chosen: Whether to select the execution plan
- range_access_plan:range Scan the final selected execution plan .
considered_execution_plans
Responsible for comparing the cost of each feasible plan , And choose the relatively optimal execution plan .
1 |
{ |
among :
- plan_prefix: Front end execution plan for the current plan .
- table: Table names involved , If there are aliases , It will also show
- best_access_path: by force of contrast considered_access_paths, Choose an optimal access path
- considered_access_paths: Currently considered access path
- access_type: How to use the index , May refer to explain Medium type Field
- index: Indexes
- rows: Row number
- cost: expenses
- chosen: Whether to choose this execution path
- considered_access_paths: Currently considered access path
- condition_filtering_pct: Be similar to explain Of filtered Column , It's an estimate
- rows_for_plan: The final number of scan lines in the execution plan , from considered_access_paths.rows X condition_filtering_pct Obtained by calculation .
- cost_for_plan: The cost of implementing the plan , from considered_access_paths.cost Add to get
- chosen: Whether the execution plan has been selected
attaching_conditions_to_tables
be based on considered_execution_plans The execution plan selected in , Transform the original where Conditions , And add appropriate additional conditions to the table , In order to facilitate the filtering of single table data .
TIPS
- This part of the condition is added mainly for the convenience of ICP( Push... Under index conditions ), but ICP Whether it is opened or not does not affect the construction of this part of the content .
- ICP Reference documents :https://www.cnblogs.com/Terry-Wu/p/9273177.html
1 |
{ |
among :
- original_condition: The original conditional statement
- attached_conditions_computation: Use a heuristic algorithm to calculate the used index , If the access type of the used index is ref, It is used for calculation range Can I use more columns in the composite index , If possible , Then use range Mode substitution ref.
- attached_conditions_summary: Add up the situation after
- table: Table name
- attached: The additional condition or the condition in the original sentence that can be directly pushed down to the single table filter .
finalizing_table_conditions
The final 、 Optimized table conditions .
1 |
{ |
refine_plan
Improve the implementation plan :
1 |
{ |
among :
- table: Table name and alias
join_execution
join_execution The paragraph shows the execution process of the execution phase .
1 |
"join_execution": { |
Reference documents
- Tracing the Optimizer
- Hands teach you to know OPTIMIZER_TRACE
- MYSQL sql Some tracking analysis of the execution process ( Two .mysql Optimizer trace analysis )
- Use Trace Perform execution plan analysis