使用分区修剪的查询优化

时间:2014-03-12 21:14:22

标签: mysql sql query-optimization

我有一张桌子,每天至少增加200万条记录,我必须每天运行统计数据。由于我的统计查询可能需要超过三个小时才能运行:O我正在尝试优化表格。我以为我会利用分区,以便查询优化器可以利用分区修剪,但是当我运行查询时,仍然会查看所有分区。

我创建了一个测试表,也可以在mysql小提琴上找到

CREATE TABLE `log_tests` (
  `_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `name` varchar(25) DEFAULT NULL,
  PRIMARY KEY (`_id`,`timestamp`),
  KEY `log_tests__timestamp` (`timestamp`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
/*!50100 PARTITION BY RANGE (unix_timestamp(`timestamp`))
(PARTITION p201401 VALUES LESS THAN (unix_timestamp('2014-02-01 00:00:00')) ENGINE = MyISAM,
 PARTITION pNew VALUES LESS THAN MAXVALUE ENGINE = MyISAM) */
;

INSERT INTO `log_tests` (`timestamp`, `name`) VALUES ('2014-01-10 01:01:01', '1');
INSERT INTO `log_tests` (`timestamp`, `name`) VALUES ('2014-01-11 01:01:01', '2');
INSERT INTO `log_tests` (`name`) VALUES ('3');
INSERT INTO `log_tests` (`name`) VALUES ('4');
INSERT INTO `log_tests` (`name`) VALUES ('5');

现在......当我在1月30日之前运行一个带有时间轴where的select语句时,会查看两个分区而不仅仅是p201401分区。例如,执行以下内容:

explain partitions select * from log_tests
where unix_timestamp(`timestamp`) < unix_timestamp('2014-01-31 00:00:00')

返回:

id | select_type | table     | partitions   | type | possible_keys | key  | key_len | ref  | rows | Extra
---------------------------------------------------------------------------------------------------------------
1  | SIMPLE      | log_tests | p201401,pNew | ALL  | NULL          | NULL | NULL    | NULL | 5    | Using where

任何智慧的话语???

1 个答案:

答案 0 :(得分:3)

问题出在你如何进行查询,分区工作。

当你这样做时

explain partitions select * from log_tests
where unix_timestamp(`timestamp`) < unix_timestamp('2014-01-31 00:00:00')

您正在将函数应用于列值。总是在将函数应用于列时,MySQL被强制执行全表扫描,因为所有行都需要应用该函数才能计算表达式。如果你想到函数rand()可能会更容易理解它,那么显然必须对每一行进行评估。

如果您将查询更改为

explain partitions select * from log_tests
where timestamp < '2014-01-31 00:00:00';

它只正确使用一个分区。见fiddle

顺便说一句,这适用于所有查询,而不仅仅是分区表上的查询。您永远不应该将函数应用于列值,它每次都会执行全表扫描。