我有一个包含约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)
答案 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'
;
在type
,product_id
和date
上建立索引的应该在 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>