2016-01-12 48 views
1

我通常爲自己成爲一名數據庫專家而感到自豪,但我無法真正地將自己的頭圍繞在這種行爲上。我希望有人能解釋這是如何工作的。奇怪的索引行爲mysql

我有兩個MySQL表訂單:

CREATE TABLE `orders` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `status` tinyint(4) NOT NULL, 
    `total` decimal(7,2) NOT NULL, 
    `date_created` datetime NOT NULL, 
    `date_updated` datetime NOT NULL, 
    `voucher_code` varchar(127) DEFAULT NULL, 
    `voucher_id` int(11) unsigned DEFAULT NULL, 
    `user_id` int(11) unsigned DEFAULT NULL, 
    `billing_address_id` int(11) unsigned NOT NULL, 
    `shipping_address_id` int(11) unsigned NOT NULL, 
    `reference_id` varchar(45) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `reference_id` (`reference_id`), 
    KEY `address_id` (`billing_address_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=168067 DEFAULT CHARSET=latin1; 

和地址:

CREATE TABLE `addresses` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `title` tinyint(4) DEFAULT NULL, 
    `first_name` varchar(255) NOT NULL, 
    `last_name` varchar(255) NOT NULL, 
    `street` varchar(255) NOT NULL, 
    `street2` varchar(255) DEFAULT NULL, 
    `company_name` varchar(255) DEFAULT NULL, 
    `city` varchar(45) NOT NULL, 
    `postcode` varchar(45) DEFAULT NULL, 
    `region` varchar(45) DEFAULT NULL, 
    `country` varchar(45) NOT NULL, 
    `phone` varchar(45) DEFAULT NULL, 
    `user_id` int(11) unsigned DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `fk_addresses_users1_idx` (`user_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=95277 DEFAULT CHARSET=latin1; 

現在你可以看到我已經創建了訂單表中的索引稱爲address_id應該匹配地址爲id

這是我試圖運行查詢:

SELECT 
    o.id, a.first_name, a.last_name, o.total, o.date_created 
FROM 
    orders o USE INDEX FOR JOIN (PRIMARY) JOIN 
    addresses a ON a.id = o.billing_address_id 
ORDER BY id DESC 
LIMIT 0, 50 

如果我運行的查詢,而無需任何索引規範它將皮卡和使用的我希望是匹配兩個最快的方式ADDRESS_ID指數表。

奇怪的是,'address_id'索引查詢在2秒內運行。 如果我使用普通的'PRIMARY'索引,它在訂單ID上工作需要0.000秒。

這使我煩惱。我以爲我應該創建索引來加快表間的連接過程。

如果我運行EXPLAIN兩個查詢我得到:

EXPLAIN EXTENDED 
SELECT o.id, a.first_name, a.last_name, o.total, o.date_created 
    FROM orders o 
    JOIN addresses a ON a.id = o.billing_address_id 
    ORDER BY id DESC 
    LIMIT 0, 50 

1 SIMPLE a ALL PRIMARY    95234 100.00 Using temporary; Using filesort 
1 SIMPLE o ref address_id address_id 4 my_basket.a.id 1 100.00 

隨着指數:

EXPLAIN EXTENDED 
SELECT o.id, a.first_name, a.last_name, o.total, o.date_created 
    FROM orders o USE INDEX FOR 
    JOIN (PRIMARY) 
    JOIN addresses a ON a.id = o.billing_address_id 
    ORDER BY id DESC 
    LIMIT 0, 50 

1 SIMPLE o index  PRIMARY 4  50 332632.00 
1 SIMPLE a eq_ref PRIMARY PRIMARY 4 my_basket.o.billing_address_id 1 100.00 

謝謝你找到時間來回答這個問題。

+0

你運行的是哪個版本的MySQL? –

+0

MySQL版本是5.6。17 –

回答

0

對於ORDER BY ... LIMIT查詢,使用避免排序的查詢執行計劃通常是有益的。這不一定是因爲排序很昂貴,而是因爲一旦找到請求的行數(這裏是50),就可以停止查詢執行。

對於你的情況,如果一個以表a開頭,在選擇「top」50行之前必須生成完整的聯接結果。如果您從使用PRIMARY索引掃描表o開始,則連接結果將按o.id排序,並且一旦找到50行就可停止連接執行。

自從MySQL 5.6以來,用於在兩種方法之間進行選擇的成本模型得到了改進。我建議你試用MySQL 5.7來查看MySQL優化器是否能夠選擇最優化的計劃。

0

我很驚訝,這兩個查詢甚至編譯 - ORDER BY id是不明確的,因爲每個表有一個不同id

當做JOIN總是資格所有列。

同時,刪除USE INDEX

+0

確定它應該是o.id,但它符合(可能由於選擇字段)。然而問題在於INDEX本身。如果我刪除USE INDEX,它會變得更慢。我不明白的是,如何通過'order's id index'運行得更快,而不是使用更加合理的'addresses id index',這被連接使用。 **我想真正的解釋是,這裏最昂貴的操作是按順序而不是連接。** –