尝试在超过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
以下是解释:
答案 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 BY
和WHERE
的执行成本大大降低。第二行允许索引“覆盖”查询,这意味着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万行在数据库中,全表扫描可能会更快。