大表的 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 查询优化的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 对于千万级的大表要怎么优化

了解MySQL联表查询中的驱动表,优化查询,以小表驱动大表

如何优化大表的 Postgresql ARRAY_AGG 查询?

在 3 个大表上使用内连接优化 SQL 查询

Mysql语句优化

针对MySQL大表优化方案