2011-10-06 140 views
5

我一直在測試出不同的想法,以優化我們系統工作中的一些表格。今天,我遇到了一張表格,可以跟蹤系統中每輛車的每個視圖。在下面創建表格。當where語句中的日期改變時,MySQL EXPLAIN'type'從'range'改變爲'ref'?

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; 

我當時玩的是多列和單列索引。我跑下面的查詢:

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 |            | 
+----+-------------+-----------------------+--------+----------------------------------------------------------------+------------------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+ 

(執行時間爲實際的選擇查詢被0.309秒)

然後我更改日期從「2011-09-07」到「where子句中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 |            | 
+----+-------------+-----------------------+--------+----------------------------------------------------------------+-----------------------------+---------+----------------------------------------------+-------+----------+----------------------------------------------+ 

(執行時間爲實際的選擇查詢是0.670秒)

我看到4個主要變化:

  1. 類型從範圍變更爲REF
  2. 鍵從vehicleViewTrackingKeyCreatedIndex變爲vehicleViewTrackingKeyIndex
  3. key_len從50改變到47(由在密鑰的變化)
  4. 行從23086改變爲53676(所造成的更改密鑰)

此時,慢速查詢的執行時間僅爲.6秒,但我們的數據庫中只有大約10%的車輛。

它遲到了,我可能忽略了mysql文檔中的某些內容,但似乎無法找到在where子句中更改日期時鍵(爲什麼鍵和行又改變)的原因。

非常感謝幫助。我搜索了一個具有相同/相似問題的人,導致此更改的日期,但無法找到任何內容。如果我錯過了以前的帖子,請鏈接我:-)

回答

7

不同的搜索策略對不同的數據有意義。特別是,索引掃描(如範圍)通常需要嘗試實際讀取該行。在某種程度上,做所有這些尋求比根本不使用索引慢。舉一個簡單的例子,一個包含三列的表:id(主鍵),name(索引),生日。說它有很多數據。如果你問MySQL尋找Bob的生日,那麼它可以很快完成:首先,它在名稱索引中找到Bob(這需要幾個搜索,log(n),其中n是行數),然後再尋找一個讀取數據文件中的實際行並從中讀取生日。這很快,而且比掃描整個表格要快得多。

接下來,考慮做一個name like 'Z%'。這可能只是表格的一小部分。因此,它更快地找到Zs在名稱索引中的起始位置,然後爲每個數據文件尋找數據文件以讀取該行。 (這是一個範圍掃描)。

最後,考慮要求所有以M-Z開頭的名字。這大概是大約一半的數據。它可以執行一次範圍掃描,然後搜索,但是隨機搜索數據文件時,讀取一半行的最終目標不是最佳選擇:只需執行一次大的順序讀取就可以更快數據文件。所以,在這種情況下,索引將被忽略。

這就是你所看到的 - 除了你的情況,還有另一個可以重新使用的鑰匙。 (它也有可能實際上使用日期索引,如果它沒有另一個,它應該選擇哪個索引最快。注意,MySQL的優化器經常會在這裏出錯。)

因此,總之,這是預期的。一個查詢不說如何檢索數據,而是說什麼數據檢索。數據庫的優化器應該找到最快的方式來檢索它。

您可能會在這兩列中找到一個索引列,按照(public_key,created_on_date)的順序在這兩種情況下都是首選,並加快查詢速度。這是因爲MySQL只能使用每個表的一個索引(每個查詢)。此外,日期還是最後,因爲範圍掃描只能在索引中的最後一列高效完成。我相信,InnoDB實際上有另一層間接性,但它只是混淆了這一點。這對解釋沒有任何影響。]

+0

所以簡而言之,mysql優化器認爲做出更改並使用其他索引更好/更快。我跑了另一個測試,並刪除了第二個索引(vehicleViewTrackingKeyIndex),查詢時間在0.01秒之內。看起來隨着結果集的增加,它意識到使用2列索引是沒有意義的。 – CriticalSpeak

+0

@CriticalSpeak:是的,簡而言之。你經常不得不使用索引(並重寫查詢),因爲它的優化器存在許多缺陷,這比你在MySQL中必須要做的要多得多。如果您對此感到厭煩,請嘗試PostgreSQL。 – derobert