2012-07-26 56 views
3

我正在用mysql查询一个有12百万个寄存器的表,这些寄存器是上述数据的一年。 查询必须选择某种数据(硬币,企业,类型等),然后为该数据的某些字段提供每日平均值,因此我们可以在之后对其进行绘制。 梦想能够实时做到这一点,所以响应时间不到10秒,但目前看起来并不明显,因为它需要4到6分钟。 例如,其中一个querys提供了150k个寄存器,每天分割约500个,然后使用AVG()和GroupBy对三个字段(不在where子句中)进行平均。大表上的MySQL查询优化

现在,原始数据,查询

SELECT 
`Valorizacion`.`fecha`, AVG(tir) AS `tir`, AVG(tirBase) AS `tirBase`, AVG(precioPorcentajeValorPar) AS `precioPorcentajeValorPar` 
FROM `Valorizacion` USE INDEX (ix_mercado2) 
WHERE 
(Valorizacion.fecha >= '2011-07-17') AND 
(Valorizacion.fecha <= '2012-07-18') AND 
(Valorizacion.plazoResidual >= 365) AND 
(Valorizacion.plazoResidual <= 3650000) AND 
(Valorizacion.idMoneda_cache IN ('UF')) AND 
(Valorizacion.idEmisorFusionado_cache IN ('ABN AMRO','WATTS', ...)) AND 
(Valorizacion.idTipoRA_cache IN ('BB', 'BE', 'BS', 'BU')) 
GROUP BY `Valorizacion`.`fecha` ORDER BY `Valorizacion`.`fecha` asc; 

248 rows in set (4 min 28.82 sec) 

该指数的顺序

(fecha, idTipoRA_cache, idMoneda_cache, idEmisorFusionado_cache, plazoResidual) 

选择“其中”寄存器,而无需使用取得了所有的where子句领域group by或AVG

149670 rows in set (58.77 sec) 

并选择寄存器,分组和仅做一个计数(*)istead平均花费

248 rows in set (35.15 sec) 

这可能是因为它它并不需要去磁盘搜索数据,但其直接从索引中查询获得。

因此,只要它告诉我的老板“我很抱歉,但不能完成”的想法,但在此之前,我来找你们问,如果你认为有什么我可以做,以改善这一点。我想我可以通过索引时间来改进搜索,将最大基数的索引移动到前面等等,但即使在此之后,每个记录访问磁盘所需的时间和AVG似乎都太多了。

任何想法?

- 编辑,表结构

CREATE TABLE `Valorizacion` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `idInstrumento` int(11) NOT NULL, 
    `fecha` date NOT NULL, 
    `tir` decimal(10,4) DEFAULT NULL, 
    `tirBase` decimal(10,4) DEFAULT NULL, 
    `plazoResidual` double NOT NULL, 
    `duracionMacaulay` double DEFAULT NULL, 
    `duracionModACT365` double DEFAULT NULL, 
    `precioPorcentajeValorPar` decimal(20,15) DEFAULT NULL, 
    `valorPar` decimal(20,15) DEFAULT NULL, 
    `convexidad` decimal(20,15) DEFAULT NULL, 
    `volatilidad` decimal(20,15) DEFAULT NULL, 
    `montoCLP` double DEFAULT NULL, 
    `tirACT365` decimal(10,4) DEFAULT NULL, 
    `tipoVal` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `idEmisorFusionado_cache` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `idMoneda_cache` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `idClasificacionRA_cache` int(11) DEFAULT NULL, 
    `idTipoRA_cache` varchar(20) COLLATE utf8_unicode_ci NOT NULL, 
    `fechaPrepagable_cache` date DEFAULT NULL, 
    `tasaEmision_cache` decimal(10,4) DEFAULT NULL, 
    PRIMARY KEY (`id`,`fecha`), 
    KEY `ix_FechaNemo` (`fecha`,`idInstrumento`) USING BTREE, 
    KEY `ix_mercado_stackover` (`idMoneda_cache`,`idTipoRA_cache`,`idEmisorFusionado_cache`,`plazoResidual`) 
) ENGINE=InnoDB AUTO_INCREMENT=12933194 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 
+0

如果添加一个索引是一个选项(它会锁定表格一段时间)尝试添加这个'idMoneda_cache,idTipoRA_cache,idEmisorFusionado_cache,plazoResidual'(不知道“plazoResidual”或“fecha”应该是最后一个)。然后运行它(或“解释”它)没有'USE INDEX'。原因是MySQL只会使用索引到有范围条件的字段(在您的情况下,您只使用索引中的“fecha”列)。 – Vatev 2012-07-26 21:54:11

+0

这很有道理,现在是下降到1分2秒。 行:193763.额外:使用where;使用临时;使用filesort。 但是,放在网页上仍然太慢= | – Jimmy 2012-07-26 22:12:35

+1

仍然不是很好用...你可以发布'SHOW CREATE TABLE ...'(可能没有任何不相关的列)和完整的'EXPLAIN'输出 – Vatev 2012-07-26 22:17:21

回答

1

选择150K记录了12M的记录,并执行它们聚合函数不会那么快不管你做什么。

由于您的示例查询是针对一年的数据,因此您可能主要处理的是历史数据。更好的方法可能是预先计算每日平均值并将它们放入单独的表格中。然后,您可以查询这些表格以获取报告,图表等。您需要决定何时以及如何运行此类计算,以便您不必在相同的数据上重新运行它们。

当您的要求是对数百万条历史记录进行分析和报告时,您需要考虑数据仓库方法http://en.wikipedia.org/wiki/Data_warehouse而不是简单的数据库方法。

+0

你完全正确,谢谢:) – Jimmy 2012-07-28 00:52:04