奇怪的索引行为mysql

时间:2016-01-12 06:24:43

标签: mysql indexing

我通常以成为数据库专家为荣,但我无法真正理解这种行为。我希望有人能解释这是如何运作的。

我有两个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;

现在您可以看到我已在订单表中为名为billing_address_id的{​​{1}}创建了一个索引,该索引应与地址address_id匹配。

这是我正在尝试运行的查询:

id

如果我在没有任何索引规范的情况下运行查询,它将拾取并使用address_id索引,我希望这是匹配这两个表的最快方法。

奇怪的是,' address_id'索引查询在2秒内运行。 如果我使用正常的主要原因'对订单ID起作用的索引需要0.000秒。

这让我烦恼。我以为我应该创建索引来加快表之间的连接过程。

如果我对两个查询运行EXPLAIN:

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

使用索引:

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  

感谢您抽出时间回答这个问题。

2 个答案:

答案 0 :(得分:0)

我很惊讶两个查询甚至编译 - ORDER BY id都不明确,因为每个表都有不同的 id

执行JOIN时,总是限定所有列。

同时,删除USE INDEX

答案 1 :(得分:0)

对于ORDER BY ... LIMIT查询,使用避免排序的查询执行计划通常是有益的。这不一定是因为排序很昂贵,但是因为一旦找到所请求的行数(这里是50)就可以停止查询执行。

在您的情况下,如果以表a开头,则必须在选择“顶部”50行之前生成完整的连接结果。如果从使用PRIMARY索引扫描表o开始,则联接结果将在o.id上排序,并且一旦找到50行,连接执行就会停止。

自MySQL 5.6以来,用于在两种方法之间进行选择的成本模型得到了改进。我建议你试试MySQL 5.7,看看MySQL优化器现在是否能够选择最优的计划。