zoukankan      html  css  js  c++  java
  • mysql物理优化器代价模型分析【原创】

    1. 引言

      mysqlsql server在根据where condition检索数据的时候,一般会有多种数据检索的方法,其会根据各种数据检索方法代价的大小,选择代价最小的那个数据检索方法。

      比如说这个语句,where col1=x and col2=y and col3 >z , 同时存在inx_col1inx_col2inx_col3inx_col1_col2_col3这四个索引,sql server要解决的问题有1)选择哪个索引、 2)是索引range扫描还是ref扫描、3table scan的方式是否可行。

      mysql会根据以下几种数据检索策略选择代价最小的策略来从数据表中获取数据,1)各个索引的range scan代价 2)各个索引的ref scan代价 3table scan的代价。如何计算这些代价,是本文详细说明的重点。

      总代价cost = cpu cost + io cost 

    2 . 代价因子

       mysql的代价因子在内存中有一份副本,由Server_cost_constants SE_cost_constants两个类组成。这两个类的具体数据成员如下。 

    Mysql Server 代价因子

    Server_cost_constants {
      m_row_evaluate_cost //行记录条件谓词评估代价
      m_key_compare_cost //键值比较代价
      m_memory_temptable_create_cost //内存临时表创建代价
      m_memory_temptable_row_cost //内存临时表的行代价
      m_disk_temptable_create_cost //磁盘临时表创建代价
      m_disk_temptable_row_cost
    }

    存储引擎代价因子

    SE_cost_constants{
      m_memory_block_read_cost //从buffer pool中读取一个页面的代价
      m_io_block_read_cost //从文件系统中读取一个页面的代价,buffer miss的场景
      m_memory_block_read_cost_default
      m_io_block_read_cost_default
    }

      mysql的代价因子在系统的持久化系统表中也有一份副本,对应mysql.server_cost mysql.engine_cost两个表,这两个表中的字段与 内存中的类字段相同。DBA可以根据实际的硬件情况测试,测试出最适合的代价因子,然后update系统表中对应的字段。再然后执行flush OPTIMIZER_COSTS命令,将修改反应到内存中数据,这样新连接上来的mysql session会读取到内存中数据,然后以新的代价因子计算代价数。

      代价因子如何根据实际的硬件环境与负载压力自适应地调整,是一个重要的研究课题。 

    3 . 统计信息

      sql server需要的统计信息是由存储引擎innodb提供的,调用innodb提供的api可以获取这些统计信息,本文的后半部分会罗列这些apiinnodb的统计信息根据需要可以持久化到系统表中。mysql.innodb_table_statsmysql.innodb_index_stats存储了表的统计信息和索引的统计信息。

      mysql.innodb_table_stats表中字段说明

       database_name 库名
       table_name 表名
       n_rows 表中的数据行数
       clustered_index_size 聚集索引的页面数
       sum_of_other_index_sizes 其他非主键索引的页面数
       last_update 最后更新这张表的时间 

      mysql.innodb_index_stats 表中字段说明

      database_name 库名
       table_name 表名
      index_name 索引名
      stat_name 统计项名称
      stat_value 统计项值
      sample_size 采样的页面数
      last_update 最后更新这张表的时间
    
      其中stat_name 统计项名称包括:
          n_diff_pfxNN 为不同前缀列的cardinality,即不同前缀字段的 distinct value个数
         n_leaf_page 索引叶子节点页面数目
         size 索引页面数目

    4. 代价的计算公式

    cpu代价计算

    double row_evaluate_cost(double rows) 
     { 
        return rows * m_server_cost_constants->row_evaluate_cost(); 
     }

    table scan IO代价计算

    Cost_estimate handler::table_scan_cost()
    {
        double io_cost= scan_time() * table->cost_model()->page_read_cost(1.0);
    }

     ref and range scan IO代价计算

    聚集索引扫描IO代价计算公式

    Cost_estimate handler::read_cost(uint index, double ranges, double rows)
    {
        double io_cost= read_time(index, static_cast<uint>(ranges),
            static_cast<ha_rows>(rows)) *
                table->cost_model()->page_read_cost(1.0);
    } 

    二级索引覆盖扫描(不需要回表)IO代价计算公式

    Cost_estimate handler::index_scan_cost(uint index, double ranges, double rows)
    {
        double io_cost= index_only_read_time(index, rows) *
                table->cost_model()->page_read_cost_index(index, 1.0);
    }    

    二级索引非覆盖扫描(需要回表)IO代价计算公式

    min( table→cost_model()→page_read_cost(tmp_fanout), tab→worst_seeks )

    估算读取 pages个聚集索引页面所花费的代价, page数乘以代价因子

    double Cost_model_table::page_read_cost(double pages)

    估算读取 pages个指定 index索引页面所花费的代价数。

    double Cost_model_table::page_read_cost_index(uint index, double pages)

    5. innodb统计信息api

    全表扫描聚集索引时,聚集索引(主键)占用的所有页面数

    double ha_innobase::scan_time() 

    估算在聚集索引上,扫描 rows 条记录,需要读取的页面数

    double ha_innobase::read_time(uint index, double ranges, double rows)

    估算在指定 keynr索引进行覆盖扫描(不需要回表),扫描 records条记录,需要读取的索引页面数

    double handler::index_only_read_time(uint keynr, double records)

    估算指定 keynr索引在范围(min_key,max_key)中的记录数量

    ha_innobase::records_in_range(
      uint keynr, /*!< in: index number */
      key_range *min_key, /*!< in: start key value of the
      key_range *max_key) /*!< in: range end key val, may
    )

    估算聚集索引内存中页面数占其所有页面数的比率

    double handler::table_in_memory_estimate()

    估算二级索引内存中页面数占其所有页面数的比率

    double handler::index_in_memory_estimate(uint keyno)

    6.开启优化器跟踪

    set session optimizer_trace="enabled=on";
    
    explain your sql
    
    select * from information_schema.optimizer_trace; 

    7.优化器跟踪示例 

         "rows_estimation": [
                  {
                    "table": "`tab`",
                    "range_analysis": {
                      "table_scan": {
                        "rows": 5,
                        "cost": 4.1
                      },
                      "potential_range_indexes": [
                        {
                          "index": "PRIMARY",
                          "usable": false,
                          "cause": "not_applicable"
                        },
                        {
                          "index": "inx_clo2",
                          "usable": true,
                          "key_parts": [
                            "clo2",
                            "clo1"
                          ]
                        },
                        {
                          "index": "inx_clo3",
                          "usable": true,
                          "key_parts": [
                            "clo3",
                            "clo1"
                          ]
                        },
                        {
                          "index": "inx_clo2_clo3",
                          "usable": true,
                          "key_parts": [
                            "clo2",
                            "clo3",
                            "clo1"
                          ]
                        }
                      ],
                      "best_covering_index_scan": {
                        "index": "inx_clo2_clo3",
                        "cost": 2.0606,
                        "chosen": true
                      },
                      "setup_range_conditions": [
                      ],
                      "group_index_range": {
                        "chosen": false,
                        "cause": "not_group_by_or_distinct"
                      },
                      "analyzing_range_alternatives": {
                        "range_scan_alternatives": [
                          {
                            "index": "inx_clo2",
                            "ranges": [
                              "hu <= clo2 <= hu"
                            ],
                            "index_dives_for_eq_ranges": true,
                            "rowid_ordered": true,
                            "using_mrr": false,
                            "index_only": false,
                            "rows": 2,
                            "cost": 3.41,
                            "chosen": false,
                            "cause": "cost"
                          },
                          {
                            "index": "inx_clo3",
                            "ranges": [
                              "huan <= clo3 <= huan"
                            ],
                            "index_dives_for_eq_ranges": true,
                            "rowid_ordered": true,
                            "using_mrr": false,
                            "index_only": false,
                            "rows": 1,
                            "cost": 2.21,
                            "chosen": false,
                            "cause": "cost"
                          },
                          {
                            "index": "inx_clo2_clo3",
                            "ranges": [
                              "hu <= clo2 <= hu AND huan <= clo3 <= huan"
                            ],
                            "index_dives_for_eq_ranges": true,
                            "rowid_ordered": true,
                            "using_mrr": false,
                            "index_only": true,
                            "rows": 1,
                            "cost": 1.21,
                            "chosen": true
                          }
                        ],
                        "analyzing_roworder_intersect": {
                          "intersecting_indexes": [
                            {
                              "index": "inx_clo2_clo3",
                              "index_scan_cost": 1,
                              "cumulated_index_scan_cost": 1,
                              "disk_sweep_cost": 0,
                              "cumulated_total_cost": 1,
                              "usable": true,
                              "matching_rows_now": 1,
                              "isect_covering_with_this_index": true,
                              "chosen": true
                            }
                          ],
                          "clustered_pk": {
                            "clustered_pk_added_to_intersect": false,
                            "cause": "no_clustered_pk_index"
                          },
                          "chosen": false,
                          "cause": "too_few_indexes_to_merge"
                        }
                      },
                      "chosen_range_access_summary": {
                        "range_access_plan": {
                          "type": "range_scan",
                          "index": "inx_clo2_clo3",
                          "rows": 1,
                          "ranges": [
                            "hu <= clo2 <= hu AND huan <= clo3 <= huan"
                          ]
                        },
                        "rows_for_plan": 1,
                        "cost_for_plan": 1.21,
                        "chosen": true
                      }
                    }
                  }
                ]
              },
              {
                "considered_execution_plans": [
                  {
                    "plan_prefix": [
                    ],
                    "table": "`tab`",
                    "best_access_path": {
                      "considered_access_paths": [
                        {
                          "access_type": "ref",
                          "index": "inx_clo2",
                          "rows": 2,
                          "cost": 2.4,
                          "chosen": true
                        },
                        {
                          "access_type": "ref",
                          "index": "inx_clo3",
                          "rows": 1,
                          "cost": 1.2,
                          "chosen": true
                        },
                        {
                          "access_type": "ref",
                          "index": "inx_clo2_clo3",
                          "rows": 1,
                          "cost": 1.2,
                          "chosen": false
                        },
                        {
                          "rows_to_scan": 1,
                          "access_type": "range",
                          "range_details": {
                            "used_index": "inx_clo2_clo3"
                          },
                          "resulting_rows": 1,
                          "cost": 1.41,
                          "chosen": false
                        }
                      ]
                    },
                    "condition_filtering_pct": 40,
                    "rows_for_plan": 0.4,
                    "cost_for_plan": 1.2,
                    "chosen": true
                  }
                ]
              },
              {
                "attaching_conditions_to_tables": {
                  "original_condition": "((`tab`.`clo2` = 'hu') and (`tab`.`clo3` = 'huan'))",
                  "attached_conditions_computation": [
                  ],
                  "attached_conditions_summary": [
                    {
                      "table": "`tab`",
                      "attached": "(`tab`.`clo2` = 'hu')"
                    }
                  ]
                }
              },
              {
                "refine_plan": [
                  {
                    "table": "`tab`"
                  }
                ]
              }
            ]
    View Code
  • 相关阅读:
    OCP 062【中文】考试题库(cuug内部资料)第19题
    OCP 062【中文】考试题库(cuug内部资料)第18题
    OCP 062【中文】考试题库(cuug内部资料)第17题
    743. 网络延迟时间 力扣(中等) 最短路径SPFA,不熟练
    1337. 矩阵中战斗力最弱的 K 行 力扣(简单) 确实简单,结构体排序,二分也可
    171. Excel 表列序号 力扣(简单) 想不明白的题
    987. 二叉树的垂序遍历 力扣(困难) bfs+hash+优先队列 感觉还是简单的,就是复杂了点
    46. 全排列 力扣(中等) 容器或回溯
    1947. 最大兼容性评分和 周赛 力扣(中等) 排列next_permutation用法
    1104. 二叉树寻路 力扣(中等) 数学题,思考久了
  • 原文地址:https://www.cnblogs.com/happytech/p/13807800.html
Copyright © 2011-2022 走看看