加速包含300k +记录的MySQL查询

时间:2009-12-17 23:45:23

标签: sql mysql join performance

我需要查询我的所有产品(sku's)他们最新的库存数量 我有一个表(称为“股票”),其中包含315k +记录,包含此信息(对于大多数sku来说,每天都会添加一批新数据)。参考数据在另一个表中(称为“stockfile”)。

这是查询:

SELECT s1 . * , f1 . *
FROM stock s1
JOIN stockfile f1 ON ( s1.stockfileid = f1.stockfileid )
LEFT OUTER JOIN ( stock s2
JOIN stockfile f2 ON ( s2.stockfileid = f2.stockfileid )
) ON ( s1.sku = s2.sku
AND ( f1.date < f2.date
OR f1.date = f2.date
AND f1.stockfileid < f2.stockfileid) )
WHERE s2.sku IS NULL

这些是表格定义

SHOW CREATE TABLE股票:

CREATE TABLE `stock` (
 `stockid` bigint(20) NOT NULL AUTO_INCREMENT,
 `sku` char(25) NOT NULL,
 `quantity` int(5) NOT NULL,
 `creationdate` datetime NOT NULL,
 `stockfileid` smallint(5) unsigned NOT NULL,
 `touchdate` datetime NOT NULL,
 PRIMARY KEY (`stockid`),
 KEY `stock_sku` (`sku`),
 KEY `stock_stockfileid` (`stockfileid`)
) ENGINE=MyISAM AUTO_INCREMENT=316039 DEFAULT CHARSET=latin1

SHOW CREATE TABLE stockfile:

CREATE TABLE `stockfile` (
 `stockfileid` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
 `filename` varchar(25) NOT NULL,
 `creationdate` datetime DEFAULT NULL,
 `touchdate` datetime DEFAULT NULL,
 `date` datetime DEFAULT NULL,
 `begindate` datetime DEFAULT NULL,
 `enddate` datetime DEFAULT NULL,
 PRIMARY KEY (`stockfileid`),
 KEY `stockfile_date` (`date`)
) ENGINE=MyISAM AUTO_INCREMENT=266 DEFAULT CHARSET=latin1

没有任何额外的索引需要......永远。我添加了这些并加速了大约250秒:

CREATE INDEX stock_sku ON stock(sku);
CREATE INDEX stock_stockfileid ON stock(stockfileid);
CREATE INDEX stockfile_date ON stockfile(date);

这是原始查询的EXPLAIN,带有这些索引。

id  select_type  table   type    possible_keys               key        key_len  ref                     rows   Extra
1   SIMPLE       s1      ALL    stock_stockfileid           NULL       NULL     NULL                    316038
1   SIMPLE       f1      eq_ref  PRIMARY                     PRIMARY    2        kompare.s1.stockfileid  1     
1   SIMPLE       s2      ref    stock_sku,stock_stockfileid stock_sku  25       kompare.s1.sku          12     Using where
1   SIMPLE       f2      eq_ref  PRIMARY,stockfile_date      PRIMARY    2        kompare.s2.stockfileid  1

还有另一种方法可以加快速度吗?

  • 感谢Bill Karwin解决原始查询!

4 个答案:

答案 0 :(得分:4)

我不确定我的查询是否正确,但如果可以安全地假设最大日期也有一个最大的stockfileid(就像你的OR条件一半建议的话)也许像这样的查询会有所帮助:

SELECT s1.*, f1.*
 FROM
  stock s1 JOIN stockfile f1 USING (stockfileid)
  JOIN (
   SELECT sku, max(date) AS maxdate, max(stockfileid) AS maxfileid
   FROM stock JOIN stockfile USING (stockfileid)
   GROUP BY sku
  ) AS dfi ON (s1.sku,f1.date,f1.stockfileid)=(dfi.sku,maxdate,maxfileid);

不确定这是否是您想要的以及它是否更快,但它应该是。另一方面,如果fileid拥有全部内容,则根本不需要考虑日期。无论如何,我认为这种预过滤可能有助于作为起点。

答案 1 :(得分:3)

my.cnf中的默认值通常是根据今天的标准为具有非常小内存的系统设置的。如果您使用这些默认值,那么这可能是寻找性能提升的最佳位置。确保您将所有可以节省的内存分配给MySQL。

mysqltuner可以为在可以使用它的MySQL的各个部分之间分配内存提供良好的启动建议。

如果在添加大部分数据之前创建了索引,则可以通过在表上执行ANALYZE TABLE来获得巨大的改进。我只看到一个查询从24秒下降到1秒。

您的EXPLAIN表明MySQL在缩小搜索范围之前正在进行表扫描以满足WHERE s2.sku IS NULL。这非常昂贵。

f1.date < f2.date
OR f1.date = f2.date 

应该可以重写为

f1.date <= f2.date

虽然我怀疑这对优化器很重要。

您能用简单的英语解释您要对查询做什么吗?这可能有助于阐明如何简化它。

答案 2 :(得分:2)

数据的规范化将大大加快查询速度,此外,如果您在慢速计算机上运行,​​这将对结果返回的速度产生不利影响。向我展示一个对该表的示例查询,然后我可以更好地了解您在该角度上的尝试。

答案 3 :(得分:2)

我不确定这是否是您可以对您的应用进行的操作,但不是每次运行查询时计算每个sku的数量,将sku和数量存储在单独的内容会更有效表,然后只要收到新的stockfile就更新数据。这样你就会产生每个得分文件计算一次的成本,而不是每个查询计算一次。计算这个是一个前期成本,但它可以为您节省很多。