【问题标题】:Range access on multiple columns using an index使用索引对多列进行范围访问
【发布时间】:2021-12-28 03:54:04
【问题描述】:

根据我对 MySQL 文档的理解:

只要比较运算符为=<=>IS NULL,优化器就会尝试使用其他关键部分来确定区间。如果运算符为><>=<=!=<>BETWEENLIKE,则优化器使用它,但不再考虑关键部分。

https://dev.mysql.com/doc/refman/5.6/en/range-optimization.html#range-access-multi-part

我试图优化以下查询:

SELECT col3 FROM tbl2 WHERE col3 = 'fHI' AND col1 > 20 AND col4 > 0.5;

其中tbl2 定义为:

CREATE TABLE `tbl2` (
  `col1` int NOT NULL,
  `col2` varchar(512) NOT NULL DEFAULT '',
  `col3` varchar(512) DEFAULT NULL,
  `col4` double DEFAULT NULL,
  PRIMARY KEY (`col2`),
  KEY `cd1` (`col3`,`col1`,`col4`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

我的印象是使用的列将是col3col1,因为col1 上的谓词使用> 运算符。但是,优化器计划说它按照计划使用所有三个索引键列。

{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "92.10"
    },
    "table": {
      "table_name": "tbl2",
      "access_type": "index",
      "possible_keys": [
        "cd1"
      ],
      "key": "cd1",
      "used_key_parts": [
        "col3",
        "col1",
        "col4"
      ],
      "key_length": "528",
      "rows_examined_per_scan": 901,
      "rows_produced_per_join": 881,
      "filtered": "97.78",
      "using_index": true,
      "cost_info": {
        "read_cost": "4.00",
        "eval_cost": "88.10",
        "prefix_cost": "92.10",
        "data_read_per_join": "901K"
      },
      "used_columns": [
        "col1",
        "col3",
        "col4"
      ],
      "attached_condition": "((`test`.`tbl2`.`col3` = 'fHI') and (`test`.`tbl2`.`col1` > 20) and (`test`.`tbl2`.`col4` > 0.5))"
    }
  }
}

是文档错误还是col4 以不同的方式使用?

【问题讨论】:

  • @ysth - 这是 MySQL 8.0
  • 这给了你什么? EXPLAIN FORMAT=JSON SELECT col3, col2 FROM tbl2 WHERE col3 = 'fHI' AND col1 > 20 AND col4 > 0.5;(我添加了col2,因此索引将不再“覆盖”。)
  • @RickJames - 它给了我这个:ctxt.io/2/AABgHhIzFg 除了used_columns 有一个额外的col2 条目之外,它完全一样
  • @RickJames 那是因为col2 是PK。我在其中添加了另一个整数列 col5 并改为使用 EXPLAIN FORMAT=JSON SELECT col3, col5 FROM tbl2 WHERE col3 = 'fHI' AND col1 > 20 AND col4 > 0.5;。计划恢复为全表扫描。
  • 糟糕——这就是我要找的。 (我错过了 col2 是 PK。)无论如何,“覆盖”在大多数情况下对性能是有益的。

标签: mysql indexing database-administration


【解决方案1】:
mysql> explain SELECT col3 FROM tbl2 WHERE col3 = 'fHI' AND col1 > 20 AND col4 > 0.5;
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | tbl2  | NULL       | index | cd1           | cd1  | 528     | NULL |    1 |   100.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+--------------------------+

我认为答案是EXPLAIN 在很多方面都很草率。

我无法解释“528”的来源。

无论如何,请注意优化器跟踪仅提及

                        "ranges": [
                          "fHI <= col3 <= fHI AND 20 < col1"
                        ],

优化器跟踪:

{
  "steps": [
    {
      "join_preparation": {
        "select#": 1,
        "steps": [
          {
            "expanded_query": "/* select#1 */ select `tbl2`.`col3` AS `col3` from `tbl2` where ((`tbl2`.`col3` = 'fHI') and (`tbl2`.`col1` > 20) and (`tbl2`.`col4` > 0.5)) limit 33"
          }
        ]
      }
    },
    {
      "join_optimization": {
        "select#": 1,
        "steps": [
          {
            "condition_processing": {
              "condition": "WHERE",
              "original_condition": "((`tbl2`.`col3` = 'fHI') and (`tbl2`.`col1` > 20) and (`tbl2`.`col4` > 0.5))",
              "steps": [
                {
                  "transformation": "equality_propagation",
                  "resulting_condition": "((`tbl2`.`col1` > 20) and (`tbl2`.`col4` > 0.5) and multiple equal('fHI', `tbl2`.`col3`))"
                },
                {
                  "transformation": "constant_propagation",
                  "resulting_condition": "((`tbl2`.`col1` > 20) and (`tbl2`.`col4` > 0.5) and multiple equal('fHI', `tbl2`.`col3`))"
                },
                {
                  "transformation": "trivial_condition_removal",
                  "resulting_condition": "((`tbl2`.`col1` > 20) and (`tbl2`.`col4` > 0.5) and multiple equal('fHI', `tbl2`.`col3`))"
                }
              ]
            }
          },
          {
            "substitute_generated_columns": {
            }
          },
          {
            "table_dependencies": [
              {
                "table": "`tbl2`",
                "row_may_be_null": false,
                "map_bit": 0,
                "depends_on_map_bits": [
                ]
              }
            ]
          },
          {
            "ref_optimizer_key_uses": [
              {
                "table": "`tbl2`",
                "field": "col3",
                "equals": "'fHI'",
                "null_rejecting": true
              }
            ]
          },
          {
            "rows_estimation": [
              {
                "table": "`tbl2`",
                "range_analysis": {
                  "table_scan": {
                    "rows": 1,
                    "cost": 2.45
                  },
                  "potential_range_indexes": [
                    {
                      "index": "PRIMARY",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
                      "index": "cd1",
                      "usable": true,
                      "key_parts": [
                        "col3",
                        "col1",
                        "col4",
                        "col2"
                      ]
                    }
                  ],
                  "best_covering_index_scan": {
                    "index": "cd1",
                    "cost": 0.35,
                    "chosen": true
                  },
                  "setup_range_conditions": [
                  ],
                  "group_index_range": {
                    "chosen": false,
                    "cause": "not_group_by_or_distinct"
                  },
                  "skip_scan_range": {
                    "potential_skip_scan_indexes": [
                      {
                        "index": "cd1",
                        "usable": false,
                        "cause": "prefix_not_const_equality"
                      }
                    ]
                  },
                  "analyzing_range_alternatives": {
                    "range_scan_alternatives": [
                      {
                        "index": "cd1",
                        "ranges": [
                          "fHI <= col3 <= fHI AND 20 < col1"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": false,
                        "using_mrr": false,
                        "index_only": true,
                        "rows": 1,
                        "cost": 0.36,
                        "chosen": false,
                        "cause": "cost"
                      }
                    ],
                    "analyzing_roworder_intersect": {
                      "usable": false,
                      "cause": "too_few_roworder_scans"
                    }
                  }
                }
              }
            ]
          },
          {
            "considered_execution_plans": [
              {
                "plan_prefix": [
                ],
                "table": "`tbl2`",
                "best_access_path": {
                  "considered_access_paths": [
                    {
                      "access_type": "ref",
                      "index": "cd1",
                      "chosen": false,
                      "cause": "range_uses_more_keyparts"
                    },
                    {
                      "rows_to_scan": 1,
                      "access_type": "scan",
                      "resulting_rows": 1,
                      "cost": 0.35,
                      "chosen": true
                    }
                  ]
                },
                "condition_filtering_pct": 100,
                "rows_for_plan": 1,
                "cost_for_plan": 0.35,
                "chosen": true
              }
            ]
          },
          {
            "attaching_conditions_to_tables": {
              "original_condition": "((`tbl2`.`col3` = 'fHI') and (`tbl2`.`col1` > 20) and (`tbl2`.`col4` > 0.5))",
              "attached_conditions_computation": [
              ],
              "attached_conditions_summary": [
                {
                  "table": "`tbl2`",
                  "attached": "((`tbl2`.`col3` = 'fHI') and (`tbl2`.`col1` > 20) and (`tbl2`.`col4` > 0.5))"
                }
              ]
            }
          },
          {
            "finalizing_table_conditions": [
              {
                "table": "`tbl2`",
                "original_table_condition": "((`tbl2`.`col3` = 'fHI') and (`tbl2`.`col1` > 20) and (`tbl2`.`col4` > 0.5))",
                "final_table_condition   ": "((`tbl2`.`col3` = 'fHI') and (`tbl2`.`col1` > 20) and (`tbl2`.`col4` > 0.5))"
              }
            ]
          },
          {
            "refine_plan": [
              {
                "table": "`tbl2`"
              }
            ]
          }
        ]
      }
    },
    {
      "join_execution": {
        "select#": 1,
        "steps": [
        ]
      }
    }
  ]
}

【讨论】:

    猜你喜欢
    • 2018-06-06
    • 1970-01-01
    • 1970-01-01
    • 2017-08-18
    • 1970-01-01
    • 2014-12-17
    • 1970-01-01
    • 2018-12-05
    • 1970-01-01
    相关资源
    最近更新 更多