2011-10-31 51 views
2

为什么此查询(以及许多类似的变体)不会在'标记'表上使用ASIN的索引?即使A仅包含几行,它仍坚持全表扫描。由于制作中的“标签”表包含近一百万个条目,因此它会严重影响查询。内部连接不会使用索引

SELECT C.tag, count(C.tag) AS total 
FROM 
(
    SELECT B.* 
    FROM 
    (
     SELECT ASIN FROM requests WHERE user_id=9 
    ) A 
    INNER JOIN tags B USING(ASIN) 
) C 
GROUP BY C.tag ORDER BY total DESC 

EXPLAIN显示正在使用索引(在测试数据库运行,因此在 '标签' 行低,但仍然是一个全表扫描):

| 1 | PRIMARY  | <derived2>   | system | NULL   | NULL  | NULL | NULL | 0 | const row not found   | 
| 2 | DERIVED  | <derived3>   | ALL | NULL   | NULL  | NULL | NULL | 28 |        | 
| 2 | DERIVED  | B     | ALL | NULL   | NULL  | NULL | NULL | 2593 | Using where; Using join buffer | 
| 3 | DERIVED  | borrowing_requests | ref | idx_user_id | idx_user_id | 5  |  | 27 | Using where   

指标:

| book_tags |   1 | asin   |   1 | ASIN  | A   |   432 |  NULL | NULL |  | BTREE  |   | 
| book_tags |   1 | idx_tag  |   1 | tag   | A   |  1296 |  NULL | NULL |  | BTREE  |   | 
| book_tags |   1 | idx_updated_on |   1 | updated_on | A   |   518 |  NULL | NULL |  | BTREE 

查询被改写自一个INNER JOIN,它具有相同的问题:

SELECT tag, count(tag) AS total 
FROM tags 
INNER JOIN requests ON requests.ASIN=tags.ASIN 
WHERE user_id=9 
GROUP BY tag 
ORDER BY total DESC 

说明:

| 1 | SIMPLE  | tags   | ALL | NULL     | NULL  | NULL | NULL | 2593 | Using temporary; Using filesort | 
| 1 | SIMPLE  | requests | ref | idx_ASIN,idx_user_id | idx_ASIN | 33  | func | 3 | Using where 

我的想法,这是一个真正的基本观点我失踪,但它大约4个小时的工作已经让我无处。任何建议是受欢迎的。

编辑:

我可以看到使用子查询的第一查询不会使用索引由于一些答复,但是这是被用作它跑了两次只内底部的查询一样快加入。

举例来说,请求中有70k行(全部带有索引ASIN),标签中有700k行,标签中有95K个不同的ASIN,每个标签中有不少于10个不同的标签记录。

如果用户有10个请求,我只希望这10个ASIN中的标签被列出和计数。在我看来,这应该使用tags.idx_ASIN,并且应该从标签表中查找至多100行(10个ASIN,每个最多包含10个标签)。

我错过了什么......我只是看不到什么。

编辑:

请求CREATE TABLE:

CREATE TABLE IF NOT EXISTS `requests` (
    `bid` int(40) NOT NULL AUTO_INCREMENT, 
    `user_id` int(20) DEFAULT NULL, 
    `ASIN` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `status` enum('active','inactive','pending','deleted','completed') COLLATE  utf8_unicode_ci NOT NULL, 
    `added_on` datetime NOT NULL, 
    `status_changed_on` datetime NOT NULL, 
    `last_emailed` datetime DEFAULT '0000-00-00 00:00:00', 
    PRIMARY KEY (`bid`), 
    KEY `idx_ASIN` (`ASIN`), 
    KEY `idx_status` (`status`), 
    KEY `idx_added_on` (`added_on`), 
    KEY `idx_user_id` (`user_id`), 
    KEY `idx_status_changed_on` (`status_changed_on`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=149380 ; 

标签CREATE TABLE

CREATE TABLE IF NOT EXISTS `tags` (
    `ASIN` varchar(10) NOT NULL, 
    `tag` varchar(50) NOT NULL, 
    `updated_on` datetime NOT NULL, 
    KEY `idx_tag` (`tag`), 
    KEY `idx_updated_on` (`updated_on`), 
    KEY `idx_asin` (`ASIN`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1; 

没有在标签上没有主键。我通常没有没有主键的表,但没有看到这个需要。这可能是一个问题吗?

AHA!不同的字符集和归类。我将纠正并重试!

后来:

这得到了它。查询从10秒下降到0.006秒。感谢大家让我以不同的方式看待这个问题。

+0

可能是因为内部选择 - 索引不适用于子查询 – galchen

+0

底部的原始查询没有子查询,但仍执行全表扫描。 – markdwhite

+0

这可能是因为MySQL不考虑重新排序的花费,而是使用索引所节省的成本。尝试删除'order by'和/或'group by'来查看这是否会导致MySQL使用您的索引。 – jswolf19

回答

0

AHA!不同的字符集和归类。我将纠正并重试!

后来:

这得到了它。查询从10秒下降到0.006秒。感谢大家让我以不同的方式看待这个问题。

1

MySQL不索引子查询。如果您希望索引提高查询的性能,请重写它们以不使用子查询。

+0

谢谢 - 我不知道子查询没有被索引,但仍然没有得到为什么第二个查询仍然执行全表扫描。 – markdwhite

0

尝试扭转你的原始查询的表的顺序:

SELECT tag, count(tag) AS total 
FROM requests 
INNER JOIN tags ON requests.ASIN=tags.ASIN 
WHERE user_id=9 
GROUP BY tag 
ORDER BY total DESC 
+0

mysql优化器以更好的方式改变顺序。所以实际上,无论您在原始查询中指定什么内容都无关紧要。 – zerkms

+0

我也阅读过文档,但我也看到这种方法造成了巨大的差异,所以从我的经验来看,mysql并不总是按照我在文档 – Bohemian

+0

中声称的那样做,我之前尝试过,并且它确实如此似乎mySQL优化两个查询是相同的,所以这也不使用索引。这就是为什么我尝试使用子查询的版本 - 试图强制'先查找用户请求,然后从mySQL查找标签'的方法。 – markdwhite