MySQL性能:在公共外键上连接两个表:still;使用where";
我有以下表格:MySQL性能:在公共外键上连接两个表:still;使用where";,mysql,database,query-optimization,database-performance,database-optimization,Mysql,Database,Query Optimization,Database Performance,Database Optimization,我有以下表格:测试用户,测试标签,产品,产品信息(产品信息有一个外键指向产品) 此外,我还有一些表,它们模拟了前面的表之间的一些关系:test\u-usertaging(FK到test\u-tag和test\u-user)、productinfo-tags(FK到test\u-tag和test\u-productinfo) (下面我附上一个更完整的描述,但这是主要的想法) 现在我有了一个产品ID和用户ID的列表,我想找到它们之间的共同标签,以及来自test\u usertagging的附加ext
测试用户
,测试标签
,产品
,产品信息
(产品信息
有一个外键指向产品
)
此外,我还有一些表,它们模拟了前面的表之间的一些关系:test\u-usertaging
(FK到test\u-tag
和test\u-user
)、productinfo-tags
(FK到test\u-tag
和test\u-productinfo
)
(下面我附上一个更完整的描述,但这是主要的想法)
现在我有了一个产品ID和用户ID的列表,我想找到它们之间的共同标签,以及来自test\u usertagging
的附加extra\u info
列:
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', ...)
我得到的是:
+----+-------------+------------------+--------+------------------------------------------------------------------+------------------------------+---------+--------------------------------------+------+--- -----------------------+
| 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”(不带索引)(test_usertagging)——正确的索引列在键下,但仍然显示“使用where”
我曾尝试添加强制索引
,但这并没有改善问题(因为正确的索引已经列出)
使用直联
只需更改最后两行之间的顺序。(请注意,test_标记
表本身对此查询是多余的;完全删除它不会改变任何内容)
关于如何使用相关可用索引(user\u id
或tag\u id
)中的一个或两个,从test\u usertagging
进行查询,您知道吗
以下是连接表的SHOW CREATE TABLE
输出:
CREATE TABLE `test_usertagging` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tag_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `usertagging_user_per_tag_is_unique` (`user_id`,`tag_id`),
KEY `test_usertagging_5659cca2` (`tag_id`),
CONSTRAINT `test_usertagging_ibfk_3` FOREIGN KEY (`tag_id`) REFERENCES `test_tag` (`id`),
CONSTRAINT `test_usertagging_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `test_user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6456921 DEFAULT CHARSET=utf8
CREATE TABLE `productinfo` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` varchar(20) NOT NULL,
`text` varchar(256) NOT NULL,
`inftype` varchar(64) NOT NULL,
`extra_info` varchar(256) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `productinfo_218f3960` (`product_id`),
CONSTRAINT `product_id_refs_rpk_d9184c73` FOREIGN KEY (`product_id`) REFERENCES `product` (`rpk`)
) ENGINE=InnoDB AUTO_INCREMENT=44088 DEFAULT CHARSET=utf8
CREATE TABLE `productinfo_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`productinfo_id` int(11) NOT NULL,
`tag_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `productinfo_id` (`productinfo_id`,`tag_id`),
KEY `productinfo_tags_4b5946a2` (`productinfo_id`),
KEY `productinfo_tags_5659cca2` (`tag_id`),
CONSTRAINT `productinfo_id_refs_id_1c393f74` FOREIGN KEY (`productinfo_id`) REFERENCES `productinfo` (`id`),
CONSTRAINT `tag_id_refs_id_0afa07dc` FOREIGN KEY (`tag_id`) REFERENCES `test_tag` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=62637 DEFAULT CHARSET=utf8
在典型的多对多映射表中,例如test\u usertaging
和productinfo\u tags
,通常不需要id自动递增主键。去掉该列,并将复合UNIQUE
键提升为主键
这种改变可能会加快一个方向,因为通过PK进行查找的速度大约是通过辅助键进行查找的速度的两倍
您的标题谈论的是外键
,而实际上,索引才是最重要的。(如果FK不存在,它将创建一个索引。)您在“显示创建表”中又错过了一个表。由于(1,2,3,4…)中的test\u usertagging
user\u id
,您将获得“using where”。MySQL加入表,然后检查此条件以满足以下要求:如果您将(1,2,3,4…)中的WHERE test\u usertagging.user\id重铸为WHERE test\u usertagging.user\id介于1和19之间的或类似内容,您将使该搜索词可搜索。此外,EXPLAIN输出中的行数也不是很大。每个表中有多少行?你有什么表演?您需要什么性能?@SamIvichuk但它不能使用用户id上的索引来检查较少的行吗?@OllieJones不幸的是,实际上这些是任意数字(用户id),不是连续的。这些表并不小,它们有10万到数百万行。这里给出的行数是MySQL对匹配行的估计。查询需要几分钟(例如3分钟)。我希望正确的实现可以在几秒钟内完成,我想20秒是可以接受的