我有一张桌子,每天至少增加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
任何智慧的话语???
答案 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。
顺便说一句,这适用于所有查询,而不仅仅是分区表上的查询。您永远不应该将函数应用于列值,它每次都会执行全表扫描。