0

我正在使用MySQL具有以下数据的表:

users(ID, name, email, create_added) (about 10000 rows)
points(user_id, point) (about 15000 rows)

我的查询:

SELECT u.*, SUM(p.point) point 
FROM users u 
LEFT JOIN points p ON p.user_id = u.ID 
WHERE u.id > 0 
GROUP BY u.id 
ORDER BY point DESC 
LIMIT 0, 10

我只让前 10 名用户获得最佳点数,但随后它就死了。如何提高查询的性能?

4

4 回答 4

2

就像@Grim 说的那样,您可以使用INNER JOIN而不是LEFT JOIN. 但是,如果您真的在寻找优化,我建议您在 table 中有一个额外的字段,users并带有 precalculate point。此解决方案将击败您当前数据库设计的任何查询优化。

于 2013-08-23T03:00:28.607 回答
1

交换LEFT JOINaINNER JOIN会有很大帮助。确保points.point并被points.user_id索引。我假设您可以摆脱该WHERE子句,因为u.id它将始终大于 0(尽管 MySQL 可能在查询优化阶段为您执行此操作)。

于 2013-08-23T02:57:12.943 回答
1

与您仅获得 10 行相比,这并不重要。MySQL 必须为每个用户总结点,然后才能对它们进行排序(“使用文件排序”操作。)最后应用 LIMIT。

覆盖指数ON points(user_id,point)将是获得最佳性能的最佳选择。(我真的只是在猜测,没有任何EXPLAIN输出或表定义。)

中的idusers可能是主键,或者至少是唯一索引。所以很可能你已经有一个id作为前导列的索引,或者如果它是 InnoDB,则为主键集群索引。)

我很想测试这样的查询:

 SELECT u.*
      , s.total_points
   FROM ( SELECT p.user_id
               , SUM(p.point) AS total_points
            FROM points p
           WHERE p.user_id > 0
           GROUP BY p.user_id
           ORDER BY total_points DESC
           LIMIT 10
        ) s
   JOIN user u
     ON u.id = s.user_id
  ORDER BY s.total_points DESC 

这确实有创建派生表的开销,但是在点上有一个合适的索引,有一个 user_id 的前导列,并且包括点列,MySQL 很可能可以通过使用索引来优化组,并避免一个“使用文件排序”操作(用于 GROUP BY)。

该结果集上可能会有一个“使用文件排序”操作,以获取按总点数排序的行。然后从中获取前 10 行。

有了这 10 行,我们可以连接到用户表以获取相应的行。

但是.. 这个结果有一点不同,如果其中任何一个值user_id在前 10 名中而不在用户表中,那么这个查询将返回少于 10 行。(我希望定义一个外键,所以这不会发生,但我真的只是在猜测没有表定义。)

EXPLAIN显示 MySQL 正在使用的访问计划。

于 2013-08-23T03:33:22.447 回答
0

有没有想过分区?我目前正在使用大型数据库并成功改进 sql 查询。

例如,

PARTITION BY RANGE (`ID`) (
    PARTITION p1 VALUES LESS THAN (100) ENGINE = InnoDB,
    PARTITION p2 VALUES LESS THAN (200) ENGINE = InnoDB,
    PARTITION p3 VALUES LESS THAN (300) ENGINE = InnoDB,
    ... and so on..
)

它可以让我们在扫描 mysql 表时获得更好的速度。即使表中有百万行,Mysql 也只会扫描包含 userid 1 到 99 的分区 p 1。

看看这个http://dev.mysql.com/doc/refman/5.5/en/partitioning.html

于 2013-10-08T07:14:21.633 回答