大表的 MySQL 查询优化
Posted
技术标签:
【中文标题】大表的 MySQL 查询优化【英文标题】:MySQL Query Optimization for large tables 【发布时间】:2014-09-22 18:01:33 【问题描述】:我有一个需要 50 秒的查询
SELECT `security_tasks`.`itemid` AS `itemid`
FROM `security_tasks`
INNER JOIN `relations` ON (`relations`.`user_id` = `security_tasks`.`user_id` AND `relations`.`relation_type_id` = `security_tasks`.`relation_type_id` AND `relations`.`relation_with` = 3001 )
security_tasks 中的记录 = 841321 ||关系中的记录 = 234254
CREATE TABLE `security_tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`itemid` int(11) DEFAULT NULL,
`relation_type_id` int(11) DEFAULT NULL,
`Task_id` int(2) DEFAULT '0',
`job_id` int(2) DEFAULT '0',
`task_type_id` int(2) DEFAULT '0',
`name` int(2) DEFAULT '0'
PRIMARY KEY (`id`),
KEY `itemid` (`itemid`),
KEY `relation_type_id` (`relation_type_id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1822995 DEFAULT CHARSET=utf8;
CREATE TABLE `relations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`relation_with` int(11) DEFAULT NULL,
`relation_type_id` int(11) DEFAULT NULL,
`manager_level` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `relation_with` (`relation_with`),
KEY `relation_type_id` (`relation_type_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1082882 DEFAULT CHARSET=utf8;
我该怎么做才能让它快,比如快 1 或 2 秒
解释:
id select_type table type possible_keys key key_len ref rows Extra 1 简单的关系参考 user_id,relation_with,relation_type_id 关系_with 5 const 169 使用 where 1 SIMPLE security_tasks ref relation_type_id,user_id user_id 5 transparent.relations.user_id 569 使用 where
更新:
添加复合键将时间缩短到 20 秒
ALTER TABLE security_tasks ADD INDEX (user_id, relation_type_id) ; ALTER TABLE 关系 ADD INDEX (user_id, relation_type_id) ; ALTER TABLE 关系 ADD INDEX (relation_with) ;
问题是当关系表包含选定用户的大量数据时 (relations.
relation_with` = 3001)
有什么想法吗?
【问题讨论】:
请用EXPLAIN [your query]
的输出更新您的问题
1 简单的关系参考 user_id,relation_with,relation_type_id 关系_with 5 const 169 使用 where 1 简单的 security_tasks 参考 relation_type_id,user_id user_id 5 transparent.relations.user_id 569 使用 where
更新你的问题。不要在评论中发布输出。
【参考方案1】:
稍微调整你的复合索引,不要只做两个,而是三个部分
ALTER TABLE 关系添加索引(user_id、relation_type_id、relation_with)
索引不仅必须在连接列上,还应该基于连接列加上任何其他对查询条件有意义的内容(在合理范围内,需要时间来了解更多效率)。因此,在建议的情况下,您知道用户和类型的联接,但也特定于与...的关系,因此将其添加到同一索引中。
此外,您的安全任务表,您可以将 itemID 添加到索引中以使其成为覆盖索引(即:覆盖连接条件和要检索的数据元素)。这也是一种技术,不应在查询中包含所有其他元素,但由于这是一个单列,因此可能对您的方案有意义。所以,看看“覆盖索引”,但本质上,覆盖索引限定了连接,但由于它也有这个“itemid”,引擎不必回到整个安全任务表的原始数据页来得到那一栏。它是索引的一部分,因此它会抓取任何符合条件的连接并随之而来,您就完成了。
ALTER TABLE security_tasks ADD INDEX (user_id, relation_type_id, itemid) ;
为了便于阅读,尤其是长表名,最好使用别名
SELECT
st.itemid
FROM
security_tasks st
INNER JOIN relations r
ON st.user_id = r.user_id
AND st.relation_type_id = r.relation_type_id
AND r.relation_with = 3001
【讨论】:
谢谢你,真的很有帮助:) @KareemSalama,它是否解决了您 20 秒的性能问题?如果是这样,到现在为止。 它的 0.004。但不幸的是,我在security_tasks
表中有许多字段可供选择(我没有将所有字段都放在这里,因为它实际上是 85 个字段)。当然,我不能将它们全部编入索引。我现在正计划重组我的表以避免加入关系表:(以上是关于大表的 MySQL 查询优化的主要内容,如果未能解决你的问题,请参考以下文章