2016-01-12 52 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',这被连接使用。 **我想真正的解释是,这里最昂贵的操作是按顺序而不是连接。** –