如何使用表扫描优化查询?

Posted

技术标签:

【中文标题】如何使用表扫描优化查询?【英文标题】:How to optimize query with table scans? 【发布时间】:2012-07-07 00:48:03 【问题描述】:

这是迄今为止我的网络应用程序中最慢的查询。

SELECT prof.user_id                      AS userId,
       prof.first_name                   AS first,
       prof.last_name                    AS last,
       prof.birthdate,
       prof.class_string                 AS classes,
       prof.city,
       prof.country,
       prof.state,
       prof.images,
       prof.videos,
       u.username,
       u.avatar,
       (SELECT Count(*)
        FROM   company_member_sponsorship
        WHERE  member_id = prof.user_id
               AND status = 'sponsored') AS sponsor_count,
       (SELECT Count(*)
        FROM   member_schedules
        WHERE  user_id = prof.user_id)   AS sched_count
FROM   member_profiles prof
       LEFT JOIN users u
              ON u.id = prof.user_id
ORDER  BY ( prof.images + prof.videos * 5 + (
            CASE
              WHEN prof.expire_date > :time THEN 50
              ELSE 0
            end ) + sponsor_count * 20 + sched_count * 4
          ) DESC,
          prof.last_name ASC
LIMIT  :start, :records  

即使在所有级别上都发生了大量查询,网站上的所有其他内容也只需不到一秒钟的时间即可加载。这大约需要 3-4 秒。

显然是表扫描导致了速度变慢。我能理解为什么;第一个表有 50,000+ 行,第二个表有 160,000+ 行。

有什么办法可以优化这个查询以使其运行得更快?

如果情况变得更糟,我总是可以检查我的代码并在个人资料表中维护赞助和活动的记录,就像我对图像和视频所做的那样,尽管我想避免这样做。

编辑:我在查询中添加了 EXPLAIN 的结果。

id  select_type         table                       type    possible_keys   key         key_len ref                         rows    Extra
1   PRIMARY             prof                        ALL     NULL            NULL        NULL    NULL                        44377   Using temporary; Using filesort
1   PRIMARY             u                           eq_ref  PRIMARY         PRIMARY     3       mxsponsor.prof.user_id      1   
3   DEPENDENT SUBQUERY  member_schedules            ref     user_id         user_id     3       mxsponsor.prof.user_id      6       Using index
2   DEPENDENT SUBQUERY  company_member_sponsorship  ref     member_id       member_id   3       mxsponsor.prof.user_id      2       Using where; Using index

EDIT2:

我最终通过在会员资料中维护计数来解决这个问题。无论在何处添加/删除赞助/活动,我只需调用一个扫描赞助/活动表并更新该成员计数的函数。可能还有一种方法可以优化这样的查询,但我们很快就会发布这个网站,所以我现在使用快速而肮脏的解决方案。

【问题讨论】:

如果不是EXPLAIN...,真的需要查看带有索引的架构 发到dba.stackexchange.com/?as=1可能会得到更好的回复 【参考方案1】:

不保证有效,但尝试使用 joingroup by 而不是内部选择:

SELECT prof.user_id      AS userId,
       prof.first_name   AS first,
       prof.last_name    AS last,
       prof.birthdate,
       prof.class_string AS classes,
       prof.city,
       prof.country,
       prof.state,
       prof.images,
       prof.videos,
       u.username,
       u.avatar,
       Count(cms.id)     AS sponsor_count,
       Count(ms.id)      AS sched_count
FROM   member_profiles prof
       LEFT JOIN users u
              ON u.id = prof.user_id
       LEFT JOIN company_member_sponsorship cms
              ON cms.member_id = prof.user_id
                 AND cms.status = 'sponsored'
       LEFT JOIN member_schedules ms
              ON ms.user_id = prof.user_id
GROUP  BY u.id
ORDER  BY ( prof.images + prof.videos * 5 + (
            CASE
              WHEN prof.expire_date > :time THEN 50
              ELSE 0
            end ) + sponsor_count * 20 + sched_count * 4
          ) DESC,
          prof.last_name ASC
LIMIT  :start, :records  

如果没有更好,那么该查询的 explain 会有所帮助。

【讨论】:

感谢您的建议。我尝试了查询,它比原来的查询长了大约 5 倍……很奇怪。我也一直听说加入也比子查询好。 @Peronix 向我展示了该查询的解释,我可能会提供更多帮助。听起来像是索引问题。 -id|select_type|table|type|possible_keys|key|key_len|ref|rows|Extra- -1|SIMPLE|prof|ALL|NULL NULL|NULL|NULL|44377|使用临时;使用文件排序- -1|SIMPLE|u|eq_ref|PRIMARY|PRIMARY|3|mxsponsor.prof.user_id|1- -1|SIMPLE|cms|ref|member_id|member_id|3|mxsponsor.prof.user_id|4|Using在哪里;使用索引--1|SIMPLE|ms|ref|user_id|user_id|3|mxsponsor.prof.user_id|3|使用索引- 所以它似乎是你的order by 导致了问题,你可能需要简化它,或者非规范化表结构。

以上是关于如何使用表扫描优化查询?的主要内容,如果未能解决你的问题,请参考以下文章

优化 MySQL 查询以避免扫描大量行

Oracle数据库查询优化方案(处理上百万级记录如何提高处理查询速度)

mysql提高查询速度

有一张表里面有上百万的数据,在做查询的时候,如何优化?从数据库端,java端和查询语句上回答

如何提高sql查询速度

无法理解如何在基本的 MYSQL 解释示例中摆脱表扫描