Mysql Query在大表上慢

时间:2011-01-18 17:42:57

标签: mysql sql performance

尝试在超过300万行的mysql表上运行以下查询。它非常缓慢,直到脚本超时才会挂起。以下是查询和该查询的解释,任何建议?

SELECT SQL_CALC_FOUND_ROWS
listing_track.listingid,
listing_track.commid,
listing.listingname,
listing_package.packagename,
listing.active,
community.commname,
SUM( listing_track.impression ) AS listing_impressions,
SUM( listing_track.view ) AS listing_views,
SUM( listing_track.phone ) AS listing_phones,
SUM( listing_track.forward ) AS listing_forward,
SUM( listing_track.coupon ) AS listing_coupons,
SUM( listing_track.email ) AS listing_emails
FROM listing_track
INNER JOIN listing ON listing_track.listingid = listing.id
INNER JOIN community ON listing_track.commid = community.id
INNER JOIN listing_package ON listing.packageid = listing_package.id
WHERE listing_track.commid =2
GROUP BY listing_track.commid, listing_track.listingid, listing_track.trackip
LIMIT 0 , 25

以下是解释: alt text

4 个答案:

答案 0 :(得分:0)

SELECT  listing_track.listingid,
        listing_track.commid,
        listing.listingname,
        listing_package.packagename,
        listing.active,
        community.commname
        SUM( listing_track.impression ) AS listing_impressions,
        SUM( listing_track.view ) AS listing_views,
        SUM( listing_track.phone ) AS listing_phones,
        SUM( listing_track.forward ) AS listing_forward,
        SUM( listing_track.coupon ) AS listing_coupons,
        SUM( listing_track.email ) AS listing_emails
FROM    (
        SELECT  *
        FROM    listing
        ORDER BY
                id
        LIMIT 25
        ) l
JOIN    listing_track lt
ON      lt.listingid = l.id
        AND lt.commid = 2
JOIN    community c
ON      c.id = lt.commid
JOIN    listing_package lp
ON      lp.packageid = l.packageid
GROUP BY
        l.id, lt.trackip
LIMIT 25

listing_track (listingid, commid)

上创建综合索引

此优化仅在没有SQL_CALC_FOUND_ROWS的情况下有效,因为后者需要扫描所有记录(就好像查询是在没有LIMIT子句的情况下运行的那样)。

在您的计划中,我看到另外一个表account,该表未在查询中使用。您需要发布整个查询,因为即使一个额外的表也可以更改所有内容。

答案 1 :(得分:0)

这里的问题是,在完成所有昂贵的表扫描之后,在查询结束时应用LIMIT。成本不是来自返回大量行,而是来自扫描大量行。

加快像这样的查询的最简单方法是使用covering index。这将允许您扫描所需的行,但每行需要更少的I / O字节(因为您只扫描每行的数据的一部分,而不是整行)。此外,如果索引的排序方式与查询方式相同,则可以避免排序成本,并且可以扫描的行数少得多。

您的索引应包含以下列。前三列的顺序必须与GROUP BY相同 - 这样可以使GROUP BYWHERE的执行成本大大降低。第二行允许索引“覆盖”查询,这意味着MySQL将能够仅从索引中满足查询的整个listing_track部分:

CREATE INDEX ix_listing_track_covering ON listing_track (
    commid, listingid, trackip, 
    listing_impression, listing_view, listing_phone, listing_forward, listing_coupon, listing_email);

使用此索引,您应该能够运行完全相同的查询,但看到更好的性能。

答案 2 :(得分:0)

看起来不错,适用于每个表的主键。我会添加一件事......

SELECT STRAIGHT_JOIN SQL_CALC_FOUND_ROWS ...

这告诉MySQL按照你说的顺序进行操作。我有类似的反对超过15个查找表(连接)的超过14,000万行的gov't数据。 MySQL试图使用较小的查找表作为加入的基础,因为它们较小并因此挂起了进程(我的意思是在我杀死它之前超过30个小时)。通过添加STRAIGHT_JOIN,因为您知道LISTING_TRACK是所有内容的基础,而OTHER表是次要引用,它应该更快地飞行。话虽这么说,你的表中仍有一些工作要做超过100万行,但显然受COMMID列= 2的限制。

另一个是可能删除你的“SQL_CALC_FOUND_ROWS”,因为它在其他搜索中被注意为“buggy”,但是不知道它对你在对限制被应用之前合格的总记录的重要性。

顺便说一句,添加STRAIGHT_JOIN后我的gov't查询在2小时内运行。

HTH

答案 3 :(得分:0)

在不知道更多关于数据结构的情况下,不可能说,但是在给定可用信息的情况下,系统似乎正在对查询做出合理的尝试。如果你真的想要一次选择处理数百万行,那就需要时间。但是,如果关系严格为1:N,您可以轻松地将查找删除到其他表 - 并且只计算listng表中的记录而不更改结果:

SELECT SQL_CALC_FOUND_ROWS *
FROM listing
WHERE commid=2

返回一百万行的查询的目的是什么?

可能有一些调整DBMS的范围 - 特别是连接缓冲区 - 尝试对它运行mysqltuner。

该计划似乎确实表明,您应用的唯一过滤(listing_track.commid = 2)最好通过在一百万行的区域内返回的表上的索引查找来实现 - 如果只有300万行在数据库中,全表扫描可能会更快。