MySQL性能:在普通外键上连接两个表:仍然"使用where"

时间:2015-11-24 11:37:35

标签: mysql database query-optimization database-performance database-optimization

我有以下表格:test_usertest_tagproductproductinfoproductinfoproduct的外键。

此外,我还有一些表格可以模拟以前表格之间的某些关系:test_usertagging(FK到test_tagtest_user),productinfo_tags(FK到{{ 1}}和test_tag)。

(下面我附上一个更全面的描述,但这是主要的想法)。

现在我有一个产品ID和用户ID列表,我希望找到它们之间的共同标记,以及来自test_productinfo的其他extra_info列:

test_usertagging

格式化查询:

mysql> explain SELECT `test_usertagging`.`user_id`, `test_usertagging`.`tag_id`, `test_usertagging`.`extra_info`
       FROM `productinfo`
       INNER JOIN `productinfo_tags` ON ( `productinfo_tags`.`productinfo_id` = `productinfo`.`id` )
       INNER JOIN `test_tag` ON ( `test_tag`.`id` = `productinfo_tags`.`tag_id` )
       INNER JOIN `test_usertagging` ON ( `test_usertagging`.`tag_id` = `test_tag`.`id` )
    WHERE
       (`test_usertagging`.`user_id` IN (1,2,3,4 ... ) AND
        `productinfo`.`product_id` IN ('abc', 'def', '000', '111', ...));

我得到的是:

SELECT test_usertagging.user_id,
       test_usertagging.tag_id,
       test_usertagging.extra_info
  FROM productinfo
  JOIN productinfo_tags ON productinfo_tags.productinfo_id = productinfo.id
  JOIN test_tag ON test_tag.id = productinfo_tags.tag_id
  JOIN test_usertagging ON test_usertagging.tag_id = test_tag.id
 WHERE test_usertagging.user_id IN (1,2,3,4 ... )
   AND productinfo.product_id IN ('abc', 'def', '000', '111', ...)

此查询执行得很糟糕。困扰我的是“使用何处”。 (没有索引)最后一行(test_usertagging) - 正确的索引列在+----+-------------+------------------+--------+------------------------------------------------------------------+------------------------------+---------+--------------------------------------+------+--- -----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------------+--------+------------------------------------------------------------------+------------------------------+---------+--------------------------------------+------+--- -----------------------+ | 1 | SIMPLE | productinfo | range | PRIMARY,productinfo_218f3960 | productinfo_218f3960 | 62 | NULL | 55 | Using where; Using index | | 1 | SIMPLE | productinfo_tags | ref | productinfo_id,productinfo_tags_4b5946a2,productinfo_tags_5659cca2 | productinfo_id | 4 | tookyo_prod.productinfo.id | 1 | Using index | | 1 | SIMPLE | test_tag | eq_ref | PRIMARY | PRIMARY | 4 | tookyo_prod.productinfo_tags.tag_id | 1 | Using index | | 1 | SIMPLE | test_usertagging | ref | test_usertagging_5659cca2 | test_usertagging_5659cca2 | 4 | tookyo_prod.productinfo_tags.tag_id | 217 | Using where | +----+-------------+---------------------+--------+------------------------------------------------------------------+------------------------------+---------+--------------------------------------+------+--------------------------+ 下,但它仍然说''使用where'。

我尝试添加key,但这并没有改善问题(因为无论如何都列出了正确的索引)。

使用FORCE INDEX只会更改最后两行之间的顺序。 (请注意,STRAIGHT JOIN表本身对于此查询来说是多余的;将其删除并不会改变任何内容。

有关如何从test_tag进行查询的任何想法都使用其中一个或两个相关的可用索引(test_usertagginguser_id)?

这里是连接表的tag_id输出:

SHOW CREATE TABLE

1 个答案:

答案 0 :(得分:0)

在典型的多对多映射表中,例如test_usertaggingproductinfo_tags,通常不需要id AUTO_INCREMENT PRIMARY KEY。删除列,并将复合UNIQUE键提升为PRIMARY KEY

这种改变可能会加快一个方向,因为通过PK进行查找的速度大约是通过辅助密钥进行查找的两倍。

你的标题谈到FOREIGN KEY,实际上,重要的是索引。 (如果索引尚不存在,FK将创建索引。)