0、關鍵字
- mysql
- explain
- optimizer_trace
- 成本
- 全表掃描
SELECT count(*) FROM table;
SELECT count(1) FROM table;
1、sql執行成本
sql查詢是否選擇索引還是全表,執行引擎會通過成本覈算出最佳的方式,如果全表掃描成本最低,則使用全表掃描。mysql執行成本組成如下:
- IO成本:即從磁盤把數據加載到內存的成本,默認情況下,讀取數據頁的IO成本是1,mysql是以頁的形式讀取數據。即當用到某個數據時,並非僅讀取該數據,會把這個數據相鄰的數據一起讀到內存中,這就是有名的程序局部性原理,所以mysql每次會讀取一整頁數據(一頁大小16kb),一頁成本即1。
- CPU成本:數據讀入內存後,還要檢測數據是否滿足條件和排序等CPU操作成本,顯然與行數有關,默認情況下,檢查成本0.2。
成本覈算公式如下:
cost = rows*0.2 + data_length/(1024*16)
在瞭解mysql查詢成本覈算公式後,計算下table表的全表掃描成本,通過以下命令查詢table表屬性信息。
show table status like 'table'
Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
table | InnoDB | 10 | Dynamic | 8139 | 324 | 2637824 | 0 | 0 | 4194304 | 200000000000008199 | 2020-06-03 16:56:50 | 2020-06-03 11:24:01 | utf8_general_ci | 樣例信息表 |
依據查詢結果rows、data_length可以計算出全表掃描成本,如下:
2、sql執行過程跟蹤
sql優化工具使用,開啓optimizer_trace
set optimizer_trace = "enabled=on";
explain select count(*) from table;
select * from information_schema.`OPTIMIZER_TRACE`;
set optimizer_trace = "enabled=off";
通過對執行計劃過程跟蹤,查看其執行成本。
{
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select count(0) AS `count(*)` from `table` limit 0,200"
}
]
}
},
{
"join_optimization": {
"select#": 1,
"steps": [
{
"table_dependencies": [
{
"table": "`table`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": [
]
}
]
},
{
"rows_estimation": [
{
"table": "`table`",
"table_scan": {
"rows": 8139,
"cost": 161
}
}
]
},
{
"considered_execution_plans": [
{
"plan_prefix": [
],
"table": "`table`",
"best_access_path": {
"considered_access_paths": [
{
"rows_to_scan": 8139,
"access_type": "scan",
"resulting_rows": 8139,
"cost": 1788.8,
"chosen": true
}
]
},
"condition_filtering_pct": 100,
"rows_for_plan": 8139,
"cost_for_plan": 1788.8,
"chosen": true
}
]
},
{
"attaching_conditions_to_tables": {
"original_condition": null,
"attached_conditions_computation": [
],
"attached_conditions_summary": [
{
"table": "`table`",
"attached": null
}
]
}
},
{
"refine_plan": [
{
"table": "`table`"
}
]
}
]
}
},
{
"join_execution": {
"select#": 1,
"steps": [
]
}
}
]
}
3、具體場景分析
序號 | SQL | 執行計劃 |
---|---|---|
1 | select created_date from table where enabled_flag = ‘Y’ order by created_date desc; | 全表掃描 |
2 | select created_date from table force index(created_date_idx) where enabled_flag = ‘Y’ order by created_date desc; | 使用索引 |
備註:table表created_date字段已創建索引。
3.1、select created_date from table where enabled_flag = ‘Y’ order by created_date desc;
explain select created_date from table where enabled_flag = ‘Y’ order by created_date desc;
查看執行計劃,該sql爲全表(All)掃描。成本爲1786.2。
{
"considered_execution_plans": [
{
"plan_prefix": [
],
"table": "`do_task_ver`",
"best_access_path": {
"considered_access_paths": [
{
"rows_to_scan": 8126,
"access_type": "scan",
"resulting_rows": 812.6,
"cost": 1786.2,
"chosen": true,
"use_tmp_table": true
}
]
},
"condition_filtering_pct": 100,
"rows_for_plan": 812.6,
"cost_for_plan": 1786.2,
"sort_cost": 812.6,
"new_cost_for_plan": 2598.8,
"chosen": true
}
]
}
3.2、select created_date from table force index(created_date_idx) where enabled_flag = ‘Y’ order by created_date desc;
explain select created_date from table force index(created_date_idx) where enabled_flag = ‘Y’ order by created_date desc;
查看執行計劃,該sql爲使用了索引index(created_date_idx)。成本爲9752.2。
{
"considered_execution_plans": [
{
"plan_prefix": [
],
"table": "`do_task_ver` FORCE INDEX (`created_date_idx`)",
"best_access_path": {
"considered_access_paths": [
{
"rows_to_scan": 8126,
"access_type": "scan",
"resulting_rows": 812.6,
"cost": 9752.2,
"chosen": true,
"use_tmp_table": true
}
]
},
"condition_filtering_pct": 100,
"rows_for_plan": 812.6,
"cost_for_plan": 9752.2,
"sort_cost": 812.6,
"new_cost_for_plan": 10565,
"chosen": true
}
]
}
通過對比以上兩sql,發現使用索引的成本大於全表掃描成本,因此默認情況select created_date from table where enabled_flag = ‘Y’ order by created_date desc;語句不會使用索引。
mysql選擇的執行計劃未必是最佳的,原因大多爲行數統計不準確,mysql認爲的最優是成本最低,我們認爲最優是時間最短。
本篇文章僅一個淺顯的記錄,若想了解更深入,需要參考更多資料。