MySQL选择错误的索引,排序索引而不是过滤索引

时间:2018-12-31 00:07:16

标签: mysql

我的Web应用程序对DBMS Mysql 5.7.23进行了一些查询。

该表有大约80万条记录。这是具有实际索引的表的DDL:

CREATE TABLE `contactlens` 
(
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `createdBy` varchar(255) DEFAULT NULL,
  `createdDate` datetime(6) NOT NULL,
  `lastModifiedBy` varchar(255) DEFAULT NULL,
  `lastModifiedDate` datetime(6) DEFAULT NULL,
  `sid` varchar(36) NOT NULL,
  `version` bigint(20) NOT NULL,
  `brand` varchar(255) DEFAULT NULL,
  `category` varchar(255) DEFAULT NULL,
  `colorCode` varchar(255) DEFAULT NULL,
  `colorDescription` varchar(255) DEFAULT NULL,
  `description` longtext,
  `imageUrl` varchar(255) DEFAULT NULL,
  `lastPurchase` datetime(6) DEFAULT NULL,
  `lastPurchasePrice` decimal(19,2) DEFAULT NULL,
  `lastSell` datetime(6) DEFAULT NULL,
  `lastSellPrice` decimal(19,2) DEFAULT NULL,
  `line` varchar(255) DEFAULT NULL,
  `manufacturer` varchar(255) DEFAULT NULL,
  `manufacturerCode` varchar(255) DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  `preset` bit(1) NOT NULL DEFAULT b'0',
  `purchasePrice` decimal(19,2) DEFAULT NULL,
  `salesPrice` decimal(19,2) DEFAULT NULL,
  `sku` varchar(255) NOT NULL,
  `stock` bit(1) NOT NULL DEFAULT b'1',
  `thumbUrl` varchar(255) DEFAULT NULL,
  `trial` bit(1) NOT NULL DEFAULT b'0',
  `upc` varchar(255) DEFAULT NULL,
  `additionMax` decimal(10,2) DEFAULT NULL,
  `additionMin` decimal(10,2) DEFAULT NULL,
  `axisMax` int(11) DEFAULT NULL,
  `axisMin` int(11) DEFAULT NULL,
  `baseCurveMax` decimal(10,2) DEFAULT NULL,
  `baseCurveMin` decimal(10,2) DEFAULT NULL,
  `cylinderMax` decimal(10,2) NOT NULL,
  `cylinderMin` decimal(10,2) NOT NULL,
  `design` varchar(30) NOT NULL,
  `diameterMax` decimal(10,1) DEFAULT NULL,
  `diameterMin` decimal(10,1) DEFAULT NULL,
  `dominant` bit(1) DEFAULT NULL,
  `duration` int(11) NOT NULL,
  `family` varchar(30) DEFAULT NULL,
  `material` varchar(255) DEFAULT NULL,
  `pack` int(11) NOT NULL,
  `source` varchar(30) NOT NULL,
  `sphereMax` decimal(10,2) NOT NULL,
  `sphereMin` decimal(10,2) NOT NULL,
  `type` varchar(30) NOT NULL,
  `taxRate_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_sku` (`sku`),
  UNIQUE KEY `UK_elol05sqtuwi88exc8cdmqul1` (`sid`),
  UNIQUE KEY `idx_upc` (`upc`),
  KEY `idx_design` (`design`),
  KEY `FKq7sw02khmcn1nqil9pcxkgmfa` (`taxRate_id`),
  KEY `idx_manufacturer_line_duration_sph_cyl_add` (`type`,`design`,`line`,`duration`,`sphereMin`,`sphereMax`,`cylinderMin`,`cylinderMax`,`axisMin`,`axisMax`,`additionMin`,`additionMax`,`manufacturer`),
  KEY `idx_sorting` (`manufacturer`,`line`,`duration`,`sphereMin`,`cylinderMin`,`additionMin`),
  CONSTRAINT `FKq7sw02khmcn1nqil9pcxkgmfa` FOREIGN KEY (`taxRate_id`) REFERENCES `taxrate` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2572246 DEFAULT CHARSET=utf8

该网络应用程序显示了其中一些数据,并允许用户过滤作用在每一列上的数据(例如Google Spreadsheet文档中的过滤器)。

用户可以过滤以下列:

manufacturer | line | type | design | duration | pack | baseCurve (mix/max) | Sph (min/max) | Cyl (min/max) | Axis (min/max) | Addition (min/max)

所有查询的默认顺序为。最常见的查询是:

查询1

SELECT *
FROM `ContactLens` contactlen0_
WHERE 1=1 
ORDER BY contactlen0_.`manufacturer` ASC, contactlen0_.`line` ASC, contactlen0_.`duration` ASC , contactlen0_.`sphereMin` ASC, contactlen0_.`cylinderMin` ASC, contactlen0_.`additionMin` ASC 
LIMIT 10

如您所见,idx_sorting的使用符合预期。查询大约需要6毫秒,这很好。

enter image description here

查询2

这次我既进行了过滤,也进行了排序。

SELECT *
FROM `ContactLens` contactlen0_
WHERE 1=1 
AND contactlen0_.`sphereMin`<=1.25 
AND contactlen0_.`sphereMax`>=1.75 
AND contactlen0_.`additionMin`<=2.25 
AND contactlen0_.`additionMax`>=2.5
AND contactlen0_.`type`='MULTI_FOCAL'
ORDER BY contactlen0_.`manufacturer` ASC, contactlen0_.`line` ASC, contactlen0_.`duration` ASC , contactlen0_.`sphereMin` ASC, contactlen0_.`cylinderMin` ASC, contactlen0_.`additionMin` ASC 
LIMIT 10

对此进行解释,您可以看到使用了 idx_sorting ,相反,我希望它是使用idx_manufacturer_line_duration_sph_cyl_add索引的。 查询需要4秒钟:很多!我不明白:

  1. 使用了“错误索引”

  2. 为什么即使使用idx_manufacturer_line_duration_sph_cyl_add,考虑到行基数为20,查询也要花很长时间

enter image description here

这是优化跟踪:

{
  "steps": [
    {
      "join_preparation": {
        "select#": 1,
        "steps": [
          {
            "expanded_query": "/* select#1 */ select `contactlen0_`.`id` AS `id`,`contactlen0_`.`createdBy` AS `createdBy`,`contactlen0_`.`createdDate` AS `createdDate`,`contactlen0_`.`lastModifiedBy` AS `lastModifiedBy`,`contactlen0_`.`lastModifiedDate` AS `lastModifiedDate`,`contactlen0_`.`sid` AS `sid`,`contactlen0_`.`version` AS `version`,`contactlen0_`.`brand` AS `brand`,`contactlen0_`.`category` AS `category`,`contactlen0_`.`colorCode` AS `colorCode`,`contactlen0_`.`colorDescription` AS `colorDescription`,`contactlen0_`.`description` AS `description`,`contactlen0_`.`imageUrl` AS `imageUrl`,`contactlen0_`.`lastPurchase` AS `lastPurchase`,`contactlen0_`.`lastPurchasePrice` AS `lastPurchasePrice`,`contactlen0_`.`lastSell` AS `lastSell`,`contactlen0_`.`lastSellPrice` AS `lastSellPrice`,`contactlen0_`.`line` AS `line`,`contactlen0_`.`manufacturer` AS `manufacturer`,`contactlen0_`.`manufacturerCode` AS `manufacturerCode`,`contactlen0_`.`name` AS `name`,`contactlen0_`.`preset` AS `preset`,`contactlen0_`.`purchasePrice` AS `purchasePrice`,`contactlen0_`.`salesPrice` AS `salesPrice`,`contactlen0_`.`sku` AS `sku`,`contactlen0_`.`stock` AS `stock`,`contactlen0_`.`thumbUrl` AS `thumbUrl`,`contactlen0_`.`trial` AS `trial`,`contactlen0_`.`upc` AS `upc`,`contactlen0_`.`additionMax` AS `additionMax`,`contactlen0_`.`additionMin` AS `additionMin`,`contactlen0_`.`axisMax` AS `axisMax`,`contactlen0_`.`axisMin` AS `axisMin`,`contactlen0_`.`baseCurveMax` AS `baseCurveMax`,`contactlen0_`.`baseCurveMin` AS `baseCurveMin`,`contactlen0_`.`cylinderMax` AS `cylinderMax`,`contactlen0_`.`cylinderMin` AS `cylinderMin`,`contactlen0_`.`design` AS `design`,`contactlen0_`.`diameterMax` AS `diameterMax`,`contactlen0_`.`diameterMin` AS `diameterMin`,`contactlen0_`.`dominant` AS `dominant`,`contactlen0_`.`duration` AS `duration`,`contactlen0_`.`family` AS `family`,`contactlen0_`.`material` AS `material`,`contactlen0_`.`pack` AS `pack`,`contactlen0_`.`source` AS `source`,`contactlen0_`.`sphereMax` AS `sphereMax`,`contactlen0_`.`sphereMin` AS `sphereMin`,`contactlen0_`.`type` AS `type`,`contactlen0_`.`taxRate_id` AS `taxRate_id` from `contactlens` `contactlen0_` where ((1 = 1) and (`contactlen0_`.`sphereMin` <= 1.25) and (`contactlen0_`.`sphereMax` >= 1.75) and (`contactlen0_`.`additionMin` <= 2.25) and (`contactlen0_`.`additionMax` >= 2.5) and (`contactlen0_`.`type` = 'MULTI_FOCAL')) order by `contactlen0_`.`manufacturer`,`contactlen0_`.`line`,`contactlen0_`.`duration`,`contactlen0_`.`sphereMin`,`contactlen0_`.`cylinderMin`,`contactlen0_`.`additionMin` limit 10"
          }
        ]
      }
    },
    {
      "join_optimization": {
        "select#": 1,
        "steps": [
          {
            "condition_processing": {
              "condition": "WHERE",
              "original_condition": "((1 = 1) and (`contactlen0_`.`sphereMin` <= 1.25) and (`contactlen0_`.`sphereMax` >= 1.75) and (`contactlen0_`.`additionMin` <= 2.25) and (`contactlen0_`.`additionMax` >= 2.5) and (`contactlen0_`.`type` = 'MULTI_FOCAL'))",
              "steps": [
                {
                  "transformation": "equality_propagation",
                  "resulting_condition": "((1 = 1) and (`contactlen0_`.`sphereMin` <= 1.25) and (`contactlen0_`.`sphereMax` >= 1.75) and (`contactlen0_`.`additionMin` <= 2.25) and (`contactlen0_`.`additionMax` >= 2.5) and (`contactlen0_`.`type` = 'MULTI_FOCAL'))"
                },
                {
                  "transformation": "constant_propagation",
                  "resulting_condition": "((1 = 1) and (`contactlen0_`.`sphereMin` <= 1.25) and (`contactlen0_`.`sphereMax` >= 1.75) and (`contactlen0_`.`additionMin` <= 2.25) and (`contactlen0_`.`additionMax` >= 2.5) and (`contactlen0_`.`type` = 'MULTI_FOCAL'))"
                },
                {
                  "transformation": "trivial_condition_removal",
                  "resulting_condition": "((`contactlen0_`.`sphereMin` <= 1.25) and (`contactlen0_`.`sphereMax` >= 1.75) and (`contactlen0_`.`additionMin` <= 2.25) and (`contactlen0_`.`additionMax` >= 2.5) and (`contactlen0_`.`type` = 'MULTI_FOCAL'))"
                }
              ]
            }
          },
          {
            "substitute_generated_columns": {
            }
          },
          {
            "table_dependencies": [
              {
                "table": "`contactlens` `contactlen0_`",
                "row_may_be_null": false,
                "map_bit": 0,
                "depends_on_map_bits": [
                ]
              }
            ]
          },
          {
            "ref_optimizer_key_uses": [
              {
                "table": "`contactlens` `contactlen0_`",
                "field": "type",
                "equals": "'MULTI_FOCAL'",
                "null_rejecting": false
              }
            ]
          },
          {
            "rows_estimation": [
              {
                "table": "`contactlens` `contactlen0_`",
                "range_analysis": {
                  "table_scan": {
                    "rows": 728004,
                    "cost": 171586
                  },
                  "potential_range_indexes": [
                    {
                      "index": "PRIMARY",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
                      "index": "idx_sku",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
                      "index": "UK_elol05sqtuwi88exc8cdmqul1",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
                      "index": "idx_upc",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
                      "index": "FKq7sw02khmcn1nqil9pcxkgmfa",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
                      "index": "idx_manufacturer_line_duration_sph_cyl_add",
                      "usable": true,
                      "key_parts": [
                        "type",
                        "design",
                        "line",
                        "duration",
                        "sphereMin",
                        "sphereMax",
                        "cylinderMin",
                        "cylinderMax",
                        "axisMin",
                        "axisMax",
                        "additionMin",
                        "additionMax",
                        "manufacturer",
                        "id"
                      ]
                    },
                    {
                      "index": "idx_sorting",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
                      "index": "idx_design",
                      "usable": false,
                      "cause": "not_applicable"
                    }
                  ],
                  "setup_range_conditions": [
                  ],
                  "group_index_range": {
                    "chosen": false,
                    "cause": "not_group_by_or_distinct"
                  },
                  "analyzing_range_alternatives": {
                    "range_scan_alternatives": [
                      {
                        "index": "idx_manufacturer_line_duration_sph_cyl_add",
                        "ranges": [
                          "MULTI_FOCAL <= type <= MULTI_FOCAL"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": false,
                        "using_mrr": false,
                        "index_only": false,
                        "rows": 364002,
                        "cost": 436803,
                        "chosen": false,
                        "cause": "cost"
                      }
                    ],
                    "analyzing_roworder_intersect": {
                      "usable": false,
                      "cause": "too_few_roworder_scans"
                    }
                  }
                }
              }
            ]
          },
          {
            "considered_execution_plans": [
              {
                "plan_prefix": [
                ],
                "table": "`contactlens` `contactlen0_`",
                "best_access_path": {
                  "considered_access_paths": [
                    {
                      "access_type": "ref",
                      "index": "idx_manufacturer_line_duration_sph_cyl_add",
                      "rows": 364002,
                      "cost": 145601,
                      "chosen": true
                    },
                    {
                      "rows_to_scan": 728004,
                      "access_type": "scan",
                      "resulting_rows": 4492.1,
                      "cost": 171584,
                      "chosen": false
                    }
                  ]
                },
                "condition_filtering_pct": 1.2341,
                "rows_for_plan": 4492.1,
                "cost_for_plan": 145601,
                "chosen": true
              }
            ]
          },
          {
            "attaching_conditions_to_tables": {
              "original_condition": "((`contactlen0_`.`sphereMin` <= 1.25) and (`contactlen0_`.`sphereMax` >= 1.75) and (`contactlen0_`.`additionMin` <= 2.25) and (`contactlen0_`.`additionMax` >= 2.5) and (`contactlen0_`.`type` = 'MULTI_FOCAL'))",
              "attached_conditions_computation": [
              ],
              "attached_conditions_summary": [
                {
                  "table": "`contactlens` `contactlen0_`",
                  "attached": "((`contactlen0_`.`sphereMin` <= 1.25) and (`contactlen0_`.`sphereMax` >= 1.75) and (`contactlen0_`.`additionMin` <= 2.25) and (`contactlen0_`.`additionMax` >= 2.5))"
                }
              ]
            }
          },
          {
            "clause_processing": {
              "clause": "ORDER BY",
              "original_clause": "`contactlen0_`.`manufacturer`,`contactlen0_`.`line`,`contactlen0_`.`duration`,`contactlen0_`.`sphereMin`,`contactlen0_`.`cylinderMin`,`contactlen0_`.`additionMin`",
              "items": [
                {
                  "item": "`contactlen0_`.`manufacturer`"
                },
                {
                  "item": "`contactlen0_`.`line`"
                },
                {
                  "item": "`contactlen0_`.`duration`"
                },
                {
                  "item": "`contactlen0_`.`sphereMin`"
                },
                {
                  "item": "`contactlen0_`.`cylinderMin`"
                },
                {
                  "item": "`contactlen0_`.`additionMin`"
                }
              ],
              "resulting_clause_is_simple": true,
              "resulting_clause": "`contactlen0_`.`manufacturer`,`contactlen0_`.`line`,`contactlen0_`.`duration`,`contactlen0_`.`sphereMin`,`contactlen0_`.`cylinderMin`,`contactlen0_`.`additionMin`"
            }
          },
          {
            "added_back_ref_condition": "((`contactlen0_`.`type` <=> 'MULTI_FOCAL') and ((`contactlen0_`.`sphereMin` <= 1.25) and (`contactlen0_`.`sphereMax` >= 1.75) and (`contactlen0_`.`additionMin` <= 2.25) and (`contactlen0_`.`additionMax` >= 2.5)))"
          },
          {
            "reconsidering_access_paths_for_index_ordering": {
              "clause": "ORDER BY",
              "index_order_summary": {
                "table": "`contactlens` `contactlen0_`",
                "index_provides_order": true,
                "order_direction": "asc",
                "index": "idx_sorting",
                "plan_changed": true,
                "access_type": "index"
              }
            }
          },
          {
            "refine_plan": [
              {
                "table": "`contactlens` `contactlen0_`"
              }
            ]
          }
        ]
      }
    },
    {
      "join_execution": {
        "select#": 1,
        "steps": [
        ]
      }
    }
  ]
}

强制使用idx_manufacturer_line_duration_sph_cyl_add索引,解释查询:

SELECT *
FROM `ContactLens` contactlen0_
USE INDEX (idx_manufacturer_line_duration_sph_cyl_add)
WHERE 1=1 
 AND contactlen0_.`sphereMin`<=1.25 
 AND contactlen0_.`sphereMax`>=1.75 
 AND contactlen0_.`additionMin`<=2.25 
 AND contactlen0_.`additionMax`>=2.5
 AND contactlen0_.`type`='MULTI_FOCAL'
ORDER BY contactlen0_.`manufacturer` ASC, contactlen0_.`line` ASC, contactlen0_.`duration` ASC , contactlen0_.`sphereMin` ASC, contactlen0_.`cylinderMin` ASC, contactlen0_.`additionMin` ASC 
LIMIT 10

这是查询的解释:

enter image description here

因此使用“正确”索引;即使行基数为403245(因此比以前大得多),查询也需要400毫秒。

我基于this paper创建了索引。

在我的情况下,可以生成大多数查询的最佳索引是什么?

为什么在查询2中,即使对40万行有效,“正确”索引也比“错误”索引要快得多?

如何提示mysql(无需明确地执行此操作)以使用正确的索引?

========与MYSQL 8和RDS AURORA的比较======

根据@oysteing的建议,我在Mysql 8(8.0.11和8.0.13)和RDS Aurora上测试了查询2。这是我得到的结果

Mysql 8.0.11

优化程序跟踪:https://codeshare.io/2BXVeK

enter image description here

Mysql 8.0.13

优化程序跟踪:https://codeshare.io/5Ookyr

并尝试在列上使用直方图:

https://codeshare.io/2KdgO7

enter image description here

RDS Aurora

优化程序跟踪:https://codeshare.io/5X4Ngj

enter image description here

1 个答案:

答案 0 :(得分:1)

在这里查看我的答案:https://stackoverflow.com/a/52033986/3481706

在您的情况下,查询优化器显然认为它只能从idx_sorting索引中读取几行(20),以找到满足WHERE条件的前10行。这似乎有些奇怪,因为EXPLAIN表示它仅估计满足条件的0.62%。 (也许这里的LIMIT优化中存在一个错误。我可能可以告诉我是否有优化器跟踪。)最终结果是,通过使用idx_sorting,它将访问的行数远远超过估计的20行满足查询的10行。

您可以尝试升级到MySQL 8.0,然后查看是否已解决此问题。使用8.0,您还可以在希望用于改善过滤估算值的列上创建直方图。

通常,在创建复合索引时,应首先放置在相等条件下使用的列。创建一个覆盖所有类型查询的索引是很困难的,因为MySQL将只能使用索引的前缀,除最后一行外,所有列均具有相等条件。在MySQL 8.0中,新的skip-scan access method对此进行了改进。这种方法允许在索引前缀中使用一个空格。