2017-05-08 85 views
3

MySQL版本5.7.18的問題。早期版本的MySQL的行爲應該如此。與JOIN不使用索引的MySQL

這裏有兩張表。表1:

CREATE TABLE `test_events` (
    `id` int(11) NOT NULL, 
    `event` int(11) DEFAULT '0', 
    `manager` int(11) DEFAULT '0', 
    `base_id` int(11) DEFAULT '0', 
    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `client` int(11) DEFAULT '0', 
    `event_time` datetime DEFAULT '0000-00-00 00:00:00' 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 


ALTER TABLE `test_events` 
    ADD PRIMARY KEY (`id`), 
    ADD KEY `client` (`client`), 
    ADD KEY `event_time` (`event_time`), 
    ADD KEY `manager` (`manager`), 
    ADD KEY `base_id` (`base_id`), 
    ADD KEY `create_time` (`create_time`); 

,第二個表:

CREATE TABLE `test_event_types` (
    `id` int(11) NOT NULL, 
    `name` varchar(255) DEFAULT NULL, 
    `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `base` varchar(255) DEFAULT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

ALTER TABLE `test_event_types` 
    ADD PRIMARY KEY (`id`); 

讓我們嘗試選擇從基地 「314」 最後事件:

EXPLAIN SELECT `test_events`.`create_time` 
    FROM `test_events` 
    LEFT JOIN `test_event_types` 
      ON (`test_events`.`event` = `test_event_types`.`id`) 
    WHERE base = 314 
    ORDER BY `test_events`.`create_time` DESC 
    LIMIT 1; 
 
+----+-------------+------------------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ 
| id | select_type | table   | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra            | 
+----+-------------+------------------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ 
| 1 | SIMPLE  | test_events  | NULL  | ALL | NULL   | NULL | NULL | NULL | 434928 | 100.00 | Using temporary; Using filesort     | 
| 1 | SIMPLE  | test_event_types | NULL  | ALL | PRIMARY  | NULL | NULL | NULL |  44 |  2.27 | Using where; Using join buffer (Block Nested Loop) | 
+----+-------------+------------------+------------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+ 
2 rows in set, 1 warning (0.00 sec) 

MySQL是不使用索引和讀取整個表格。 沒有WHERE語句:

EXPLAIN SELECT `test_events`.`create_time` 
    FROM `test_events` 
    LEFT JOIN `test_event_types` 
      ON (`test_events`.`event` = `test_event_types`.`id`) 
    ORDER BY `test_events`.`create_time` DESC 
    LIMIT 1; 
 
+----+-------------+------------------+------------+--------+---------------+-------------+---------+-----------------------+------+----------+-------------+ 
| id | select_type | table   | partitions | type | possible_keys | key   | key_len | ref     | rows | filtered | Extra  | 
+----+-------------+------------------+------------+--------+---------------+-------------+---------+-----------------------+------+----------+-------------+ 
| 1 | SIMPLE  | test_events  | NULL  | index | NULL   | create_time | 4  | NULL     | 1 | 100.00 | NULL  | 
| 1 | SIMPLE  | test_event_types | NULL  | eq_ref | PRIMARY  | PRIMARY  | 4  | m16.test_events.event | 1 | 100.00 | Using index | 
+----+-------------+------------------+------------+--------+---------------+-------------+---------+-----------------------+------+----------+-------------+ 
2 rows in set, 1 warning (0.00 sec) 

現在它使用索引。

MySQL 5.5.55在這兩種情況下都使用索引。爲什麼是這樣以及如何處理它?

回答

2

我不知道你在以前和當前的安裝中看到的不同,但服務器的行爲是有道理的。

SELECT test_events.create_time FROM test_events LEFT JOIN test_event_types ON ( test_events.event = test_event_types.id) ORDER BY test_events.create_time DESC LIMIT 1; 

在這個查詢中,您沒有where子句,但是隻提取一行。這是排序create_time後,恰好有一個索引。並且該索引可以用於排序。但讓我們看看第二個查詢。

SELECT test_events.create_time FROM test_events LEFT JOIN test_event_types ON ( test_events.event = test_event_types.id) WHERE base = 314 ORDER BY test_events.create_time DESC LIMIT 1 

您在基準列上沒有索引。所以沒有索引可以用於此。要查找相關記錄,mysql必須執行表掃描。確定相關行後,需要對其進行排序。但是,在這種情況下,查詢規劃已經決定,它只是不值得使用索引上create_time

我看到幾個問題你的設置,對base如已經提到的第一是沒有和索引。但爲什麼base varchar?你似乎在存儲整數。

ALTER TABLE test_events 
    ADD PRIMARY KEY (id), 
    ADD KEY client (client), 
    ADD KEY event_time (event_time), 
    ADD KEY manager (manager), 
    ADD KEY base_id (base_id), 
    ADD KEY create_time (create_time); 

而使得像這樣的多個索引在mysql中沒有多大意義。這是因爲mysql只能爲每個表使用一個索引進行查詢。用一個或兩個指標你會好得多。可能是多列索引。

我覺得你的理想指數將同時包含CREATE_TIME和事件字段

+0

這很奇怪,但它的工作原理。如果我將列「base」的類型更改爲整數並向此列添加索引,MySQL將開始在「create_time」字段中使用索引。如果我只添加索引或只將類型更改爲int - 它的行爲與以前一樣。前一段時間錯誤地將Varchar設置爲字段「base」,但在更新到最後一個版本的MySQL之前它已經正常。謝謝。 –

+0

我不希望在表格中使用不會超過5-10行的索引(主索引除外)。但我認爲這是一個錯誤的策略。但是,問題仍然存在於其他JOIN到文本字段中。在上面的示例中,我可以輕鬆地將varchar更改爲整數,但是我無法在其他表中使用該字段,其中該字段包含一些文本代碼。 –

+0

我不知道這個表有少於10行。那麼當然不需要索引。但後來mysql查詢計劃一直很奇怪 – e4c5

1

base = 314base VARCHAR...是一個性能問題。要麼將報價放在314左右,要麼將base設置爲某種整數類型。

您似乎不需要LEFT。如果沒有,那麼做一個普通的JOIN,這樣優化器可以自由地開始INDEX(base),然後丟失和需要。

至於5.5和5.6和5.7之間的差異,出現了一些優化變化;你可能遇到了迴歸。但是我不想在查詢和索引得到改進之前追逐它。

+0

我可以用引號和LEFT去掉,但結果是一樣的。 MySQL 5.7在應該使用的地方不使用索引。假設它不是314,而是「z314」 –

+0

@TarasBulgakov - 使用'z314',您將不得不使用引號。 –