select count(*)使用全表掃描?閒聊下其中的成本計算...

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認爲的最優是成本最低,我們認爲最優是時間最短。

本篇文章僅一個淺顯的記錄,若想了解更深入,需要參考更多資料。

4、參考

1、8.9.5 The Optimizer Cost Model 成本模型

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章