在大表上查询慢的mysql

时间:2013-02-01 09:51:29

标签: mysql symfony doctrine-orm innodb

我有一个包含约600k记录的统计表,我在其上执行以下(原始sql)查询以获取图表的统计数据:

SELECT 
(UNIX_TIMESTAMP(s.date)*1000+3600000) as time,
ROUND((s.loadtime / s.loadtimeMeasurements), 3) as loadtime 
FROM mw_statistics s 
WHERE s.type = 0 
    AND s.date >= '2013-02-01 07:52:06' 
    AND s.date <= '2013-02-01 11:52:06' 
    AND s.product_id IN (1,8,9,10,11) 
GROUP BY s.date

此查询大约需要1秒钟才能完成。我希望它只需要几百毫秒。知道如何改进这个查询吗?我正在使用Symfony2 / Doctrine和mysql数据库以及innodb引擎。

问候,贾斯珀

这是表的结构转储:

CREATE TABLE IF NOT EXISTS `mw_statistics` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`contentErrors` smallint(6) DEFAULT NULL,
`contentMeasurements` smallint(6) DEFAULT NULL,
`thirdpartyErrors` smallint(6) DEFAULT NULL,
`thirdpartyMeasurements` smallint(6) DEFAULT NULL,
`applicationErrors` smallint(6) DEFAULT NULL,
`applicationMeasurements` smallint(6) DEFAULT NULL,
`loadtime` double NOT NULL,
`loadtimeMeasurements` smallint(6) NOT NULL,
`unavailable` smallint(6) DEFAULT NULL,
`unavailableMeasurements` smallint(6) DEFAULT NULL,
`type` smallint(6) NOT NULL,
`step` smallint(6) DEFAULT NULL,
`date` datetime NOT NULL,
`status` smallint(6) DEFAULT NULL,
`url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`product_id` int(11) DEFAULT NULL,
`script_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `IDX_FC665E6F4584665A` (`product_id`),
KEY `IDX_FC665E6FA1C01850` (`script_id`),
KEY `date` (`date`) 
) ENGINE=InnoDB DEFAULT
  CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=2105417 ;

请注意,组合是唯一的:(type = 0,product_id,date)或(type = 1,script_id,step,date)

3 个答案:

答案 0 :(得分:0)

为日期和时间创建索引ID。在条件AND p.id IN (1,8,9,10,11) s.type = 0之后的情况下,我希望它能使您的查询比以前更快。

答案 1 :(得分:0)

为了完全确定原因,我需要执行计划(使用EXPLAIN获得)。

在一个紧要关头,我猜测由于索引不正确/缺失而涉及一个或多个全表扫描。

您希望此订单基于mw_statistics type, date, product_id上的INDEX:

 CREATE INDEX mw_ndx ON mw_statistics ( type, date, product_id )

您还可以尝试将p.id上的条件移至s

WHERE s.type = 0
    AND s.date >= '2013-02-01 06:12:32' AND s.date <= '2013-02-01 10:12:30'
    AND s.product_id IN (1,8,9,10,11)

...在这种情况下,您的索引可能会表现得更好:

 CREATE INDEX mw_ndx ON mw_statistics ( type, product_id, date )

仔细看看

您有一个名为date的列,但是您使用datetime和其中的组,没有任何聚合函数。可能是您总是想要查询一天,而GROUP BY则是多余的。如果列保持datetime,那么您将拥有非常精细(可能无用)的极少数项目组,大多数情况下只有一个。

然后,您加载的所有数据都来自s表。通过在product_id上实施约束来确保统计数据确实具有产品而后者确实具有品牌,可能会更好地为您提供服务。

您还可以事先检查product_ids在这方面是否合法。完成后,您的查询将归结为

SELECT 
    (UNIX_TIMESTAMP(date)*1000+3600000) as time,
    ROUND((loadtime / loadtimeMeasurements), 3) as loadtime
FROM mw_statistics
WHERE type = 0
    AND product_id IN (1,8,9,10,11)
    AND date BETWEEN '2013-02-01 06:12:32' AND '2013-02-01 10:12:30'
;
typeproduct_iddate上建立索引的

应该在 ten 毫秒内运行。

特定尝试

CREATE INDEX mw_ndx ON mw_statistics (
          type, product_id, date, loadtime, loadtimeMeasurements
     );

SELECT
    (UNIX_TIMESTAMP(date)*1000+3600000) as time,
    ROUND((loadtime / loadtimeMeasurements), 3) as loadtime
FROM mw_statistics
WHERE type = 0
  AND product_id IN (1,8,9,10,11)
  AND date BETWEEN '2013-02-01 06:12:32' AND '2013-02-01 10:12:30'
;

这样,通过在type上精确选择并在product_id上设置选择,可以快速缩小必要的记录。 date选择也应该表现良好;在另一种情况下,你可能想要考虑分区或分片,但是只有不到几百万条记录,它就闻起来不值得。每个索引条目都用两个smallint进行加权,但是通过接受这个小开销,你实际上从不访问主表

查询运行时将取决于列基数;但是在样本上,均匀(实际上随机)填充了一百万行的样本表,我的往返时间在8到90毫秒之间,具体取决于缓存性能和实际检索的行数。

为了进行更精确的调整,我需要输出EXPLAIN SELECT (UNIX_TIMESTAMP...

答案 2 :(得分:0)

你真的需要加入mw_brands吗?你没有使用它的任何数据,所以现在唯一的用途就是确保mw_statistics与mw_brands相关(通过mw_products)?

如果您不需要它,请删除两个连接并更改(1,8,9,10,11)中的p.id,以获取(1,8,9,10,11)中的s.product_id。 / p>