0

我有一个包含约 60 万条记录的统计表,我在该表上执行以下(原始 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 秒才能完成。我希望它只需要几百毫秒。知道如何改进这个查询吗?我正在使用带有 mysql 数据库和 innodb 引擎的 Symfony2/Doctrine。

问候,贾斯珀

这是表的结构转储:

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)

4

3 回答 3

0

为日期和 ID 创建索引。我希望它应该使您AND p.id IN (1,8,9,10,11)s.type = 0查询比以前更快。

于 2013-02-01T09:57:48.990 回答
0

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

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

您想要一个mw_statistics基于type, date, product_id以下顺序的索引:

 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,但您使用 a 对其进行范围datetime,并对其进行分组,没有任何聚合函数。可能是您总是想查询一天GROUP BY然后 是多余的。如果该列包含 a 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_iddate

具体尝试

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'
;

这样,通过精确选择 ontype和设置选择 on可以快速减少必要的记录product_id。选择date也应该表现良好;在另一种情况下,您可能需要考虑分区或分片,但是如果记录数少于几百万,它就没有什么价值了。每个索引条目都用两个smallints 加权,但是通过接受这个小开销,您实际上根本就不会访问主表

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

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

于 2013-02-01T09:59:49.730 回答
0

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

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

于 2013-02-01T10:00:34.187 回答