如何提高这个 MySQL 查询的速度?

Posted

技术标签:

【中文标题】如何提高这个 MySQL 查询的速度?【英文标题】:How can I improve the speed of this MySQL query? 【发布时间】:2013-10-07 15:34:58 【问题描述】:

这是查询:

SELECT 
u.uid as UID,
fuo.uid as FUO_UID,
fo.prid as FO_NAME
FROM 
users u
LEFT OUTER JOIN firstpoint_users_organisations fuo ON (u.uid=fuo.uid)
LEFT OUTER JOIN firstpoint_organisations fo ON (fo.nid=fuo.nid)
WHERE 
u.status=1 AND u.uid>1 
ORDER BY u.uid 
LIMIT 3;

还有桌子:

users
+------------------+------------------+------+-----+---------+----------------+
| Field            | Type             | Null | Key | Default | Extra          |
+------------------+------------------+------+-----+---------+----------------+
| uid              | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name             | varchar(60)      | NO   | UNI |         |                |
| status           | tinyint(4)       | NO   |     | 0       |                |
+-----------------------------------------------------------------------------+

firstpoint_users_organisations
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| nid   | int(10) unsigned | NO   | PRI | 0       |       |
| uid   | int(10) unsigned | NO   | PRI | 0       |       |
+-------+------------------+------+-----+---------+-------+

firstpoint_organisations
+----------+------------------+------+-----+---------+-------+
| Field    | Type             | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+-------+
| nid      | int(10) unsigned | NO   | PRI | 0       |       |
| prid     | varchar(32)      | NO   |     |         |       |
+------------------------------------------------------------+

我希望为users 中的每一行显示users.uidfirstpoint_organisations.prid,即使有些用户没有prid,在这种情况下我显示NULL(因此左外连接)。连接应该如下:

users
uid -      firstpoint_users_organisations 
     \---->uid
           nid -          firstpoint_organisations
                \-------->nid
                          prid

所以每个用户(users)都有一个用户id(uid),他们关联的组织(firstpoint_users_organisation)有一个节点id(nid)并存储这个关联。然后将组织的详细信息存储在 firstpoint_organisations 中。

所以每个用户都会有一个prid,但如果他们没有,则显示 NULL。

现在,如果我在 firstpoint_users_organisationsfirstpoint_organisations 上执行 INNER JOIN,我将获得良好的查询速度(上述查询在 0.02 秒内运行)。但是,当我将两者都切换到 LEFT OUTER JOIN 时,我可以获得所有用户,prid 或没有 prid,上述查询需要大约 90 秒才能运行。

有什么办法可以加快查询速度吗?大约有。 users 表中有 70,000 行,但即使使用 LIMIT 3,使 INNER JOIN 成为 LEFT OUTER JOIN 也需要很长时间。有趣的是,查询在 LIMIT 30 下运行所需的时间相同,所以我认为我的查询存在根本问题。

按要求解释:

+----+-------------+-------+--------+---------------+---------+---------+-----------------------+-------+----------------------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                   | rows  | Extra                                        |
+----+-------------+-------+--------+---------------+---------+---------+-----------------------+-------+----------------------------------------------+
|  1 | SIMPLE      | u     | range  | PRIMARY       | PRIMARY | 4       | NULL                  | 13152 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | fuo   | index  | NULL          | PRIMARY | 8       | NULL                  |  3745 | Using index                                  |
|  1 | SIMPLE      | fo    | eq_ref | PRIMARY       | PRIMARY | 4       | dbdb-dbdb_uat.fuo.nid |     1 |                                              |
+----+-------------+-------+--------+---------------+---------+---------+-----------------------+-------+----------------------------------------------+
3 rows in set (0.00 sec)

【问题讨论】:

请张贴此声明的EXPLAIN 对 firstpoint_users_organisations 表的一个问题是可以有 2 个 PRIMARY 键吗?这听起来对我来说实际上有点奇怪。 您在表中忘记了 u.status! @njp:一个表不能有两个主键。但是,与任何其他索引一样,主键可以包含多个列 所以问题出在第一个 EXPLAIN 行,但我不知道为什么 mysql 在这里使用文件排序,因为“order by”应该使用主 u.uid,还是不? 【参考方案1】:

您的查询毫无意义(因为uid > 1 将包括除一个用户之外的所有用户)使用 uid 上的索引,因此对该索引使用 IGNORE INDEX 提示:

SELECT 
  u.uid as UID,
  fuo.uid as FUO_UID,
  fo.prid as FO_NAME
FROM users u IGNORE INDEX (uid)
LEFT JOIN firstpoint_users_organisations fuo ON u.uid=fuo.uid
LEFT JOIN firstpoint_organisations fo ON fo.nid=fuo.nid
WHERE u.status=1
AND u.uid > 1 
ORDER BY u.uid 
LIMIT 3

您应该在users(status) 上放置一个索引,如果有足够多的行状态为 != 1,这可能会给您带来一些好处

预计更改 LIMIT 不会产生任何效果,因为必须对 70000 行进行排序应用限制以了解 哪些行是要排序的第一行return - 限制影响不大,只是返回给客户端的行数更少(逗号 IO 更少)


我坚信“代码越少越好”,因此从严格的风格角度来看,我已从您的查询中删除了非必要代码:

删除了OUTER,因为没有其他类型的左连接 删除了连接条件周围的括号,因为您不需要它们

【讨论】:

【参考方案2】:

我会在 u.status,u.uid 上使用唯一索引,因为 mysql 必须进行全扫描才能查看,我认为哪些条目的状态 = 1。

我希望之后它会更快;)

【讨论】:

以上是关于如何提高这个 MySQL 查询的速度?的主要内容,如果未能解决你的问题,请参考以下文章

如何提高mysql查询速度

mysql提高查询速度

如何提高ElasticSearch 索引速度

Oracle新手入门:如何提高索引创建速度?

如何让mysql速度更快的响应?如何提高读取和查询速度

怎么提高数据库查询效率