当where语句中的日期发生变化时,MySQL EXPLAIN'type'会从'range'变为'ref'吗?

Cri*_*eak 5 mysql sql query-optimization

我一直在测试不同的想法,以优化我们在工作中的系统中的一些表.今天我遇到了一张桌子,它跟踪我们系统中每辆车的每个视图.在下面创建表格.

SHOW CREATE TABLE vehicle_view_tracking;

CREATE TABLE `vehicle_view_tracking` (
  `vehicle_view_tracking_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `public_key` varchar(45) NOT NULL,
  `vehicle_id` int(10) unsigned NOT NULL,
  `landing_url` longtext NOT NULL,
  `landing_port` int(11) NOT NULL,
  `http_referrer` longtext,
  `created_on` datetime NOT NULL,
  `created_on_date` date NOT NULL,
  `server_host` longtext,
  `server_uri` longtext,
  `referrer_host` longtext,
  `referrer_uri` longtext,
  PRIMARY KEY (`vehicle_view_tracking_id`),
  KEY `vehicleViewTrackingKeyCreatedIndex` (`public_key`,`created_on_date`),
  KEY `vehicleViewTrackingKeyIndex` (`public_key`)
) ENGINE=InnoDB AUTO_INCREMENT=363439 DEFAULT CHARSET=latin1;
Run Code Online (Sandbox Code Playgroud)

我正在玩多列和单列索引.我运行了以下查询:

EXPLAIN EXTENDED SELECT dealership_vehicles.vehicle_make, dealership_vehicles.vehicle_model, vehicle_view_tracking.referrer_host, count(*) AS count
FROM vehicle_view_tracking
LEFT JOIN dealership_vehicles
ON dealership_vehicles.dealership_vehicle_id = vehicle_view_tracking.vehicle_id
WHERE vehicle_view_tracking.created_on_date >= '2011-09-07' AND vehicle_view_tracking.public_key IN ('ab12c3')
GROUP BY (dealership_vehicles.vehicle_make) ASC , dealership_vehicles.vehicle_model, referrer_host

+----+-------------+-----------------------+--------+----------------------------------------------------------------+------------------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+
| id | select_type | table                 | type   | possible_keys                                                  | key                                | key_len | ref                                          | rows  | filtered | Extra                                        |
+----+-------------+-----------------------+--------+----------------------------------------------------------------+------------------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+
|  1 | SIMPLE      | vehicle_view_tracking | range  | vehicleViewTrackingKeyCreatedIndex,vehicleViewTrackingKeyIndex | vehicleViewTrackingKeyCreatedIndex | 50      | NULL                                         | 23086 |   100.00 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | dealership_vehicles   | eq_ref | PRIMARY                                                        | PRIMARY                            | 8       | vehicle_view_tracking.vehicle_id |     1 |   100.00 |                                              |
+----+-------------+-----------------------+--------+----------------------------------------------------------------+------------------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

(实际选择查询的执行时间为.309秒)

然后我将where子句中的日期从"2011-09-07"更改为"2011-07-07",并得到以下解释结果

EXPLAIN EXTENDED SELECT dealership_vehicles.vehicle_make, dealership_vehicles.vehicle_model, vehicle_view_tracking.referrer_host, count(*) AS count
FROM vehicle_view_tracking
LEFT JOIN dealership_vehicles
ON dealership_vehicles.dealership_vehicle_id = vehicle_view_tracking.vehicle_id
WHERE vehicle_view_tracking.created_on_date >= '2011-07-07' AND vehicle_view_tracking.public_key IN ('ab12c3')
GROUP BY (dealership_vehicles.vehicle_make) ASC , dealership_vehicles.vehicle_model, referrer_host


+----+-------------+-----------------------+--------+----------------------------------------------------------------+-----------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+
| id | select_type | table                 | type   | possible_keys                                                  | key                         | key_len | ref                                          | rows  | filtered | Extra                                        |
+----+-------------+-----------------------+--------+----------------------------------------------------------------+-----------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+
|  1 | SIMPLE      | vehicle_view_tracking | ref    | vehicleViewTrackingKeyCreatedIndex,vehicleViewTrackingKeyIndex | vehicleViewTrackingKeyIndex | 47      | const                                        | 53676 |   100.00 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | dealership_vehicles   | eq_ref | PRIMARY                                                        | PRIMARY                     | 8       | vehicle_view_tracking.vehicle_id |     1 |   100.00 |                                              |
+----+-------------+-----------------------+--------+----------------------------------------------------------------+-----------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

(实际选择查询的执行时间为.670秒)

我看到4个主要变化:

  1. 类型从范围更改为ref
  2. 键从vehicleViewTrackingKeyCreatedIndex更改为vehicleViewTrackingKeyIndex
  3. key_len从50变为47(由密钥更改引起)
  4. 行从23086更改为53676(由键更改引起)

此时,慢速查询的执行时间仅为0.6秒,但我们的数据库中只有大约10%的车辆.

它已经很晚了,我可能忽略了mysql文档中的一些内容,但我似乎无法找到为什么在where子句中更改日期时键(以及类型和行)正在发生变化.

非常感谢帮助.我搜索了具有相同/类似问题的人,导致此更改的日期并且无法找到任何内容.如果我错过了以前的帖子,请链接我:-)

der*_*ert 7

不同的搜索策略对不同的数据有意义.特别是,索引扫描(例如范围)通常必须寻求实际读取行.在某些时候,做所有这些搜索比完全不使用索引要慢.

举一个简单的例子,一个包含三列的表:id(主键),name(索引),birthday.说它有很多数据.如果你要求MySQL寻找Bob的生日,它可以相当快地做到这一点:首先,它在名称索引中找到Bob(这需要一些搜索,log(n),其中n是行数),然后另外一个寻求读取数据文件中的实际行并从中读取生日.这比扫描整个桌子快得多,速度也快得多.

接下来,考虑做一个name like 'Z%'.这可能只是表格的一小部分.因此,更快地找到Zs在名称索引中的起始位置,然后为每一个寻找数据文件来读取行.(这是范围扫描).

最后,考虑询问所有以MZ开头的名字.这可能是数据的一半左右.它可以进行范围扫描,然后进行大量搜索,但是在数据文件中随机搜索,最终目标是读取一半行不是最佳的:只需对数据文件进行大量顺序读取就会更快.因此,在这种情况下,索引将被忽略.

这就是你所看到的 - 除了在你的情况下,还有另一个可以依赖的关键.(它也可能实际上使用日期索引,如果没有另一个,它应该选择哪个索引最快.请注意MySQL的优化器经常会出错.)

所以,简而言之,这是预期的.查询没有说明如何检索数据,而是说要检索哪些数据.数据库的优化器应该找到最快的方法来检索它.

您可以在两个列中找到索引,在这两种情况下都是首选(public_key,created_on_date),并加快查询速度.这是因为MySQL每个表只能使用一个索引(每个查询).此外,日期结束,因为范围扫描只能在索引的最后一列上有效地完成.

[InnoDB实际上有另一层间接,我相信,但它只是混淆了这一点.它对解释没有任何影响.]