MySQL - 为什么在这两个查询中具有时间戳的这种不同性能?

时间:2012-03-22 21:19:28

标签: mysql performance

我编写了一个Perl脚本,它在一个超过140000行的表中进行了一些SQL查询并进行了扩展。

我想比较日期并获取一些行,但我意识到只需更改一个SQL查询,我就可以获得不同的执行速度。

查看执行100 $ sql查询的以下测试结果。 我在不同执行之间在脚本中更改的唯一行是$ sql行。

我多次运行测试并且总是得到类似的结果,所以我猜这与缓存问题无关。

my $sql = "SELECT `mem_used`, `swap_used`, `mem_total` 
FROM `$config{db}{data_table}` 
WHERE  `host_id` = $host_id 
AND date >= '$date' 
AND TIMESTAMPDIFF( MINUTE , `date`, '$date' ) <= $interval;"; # VERY SLOW

time ./data_smoothing.pl

real    1m28.818s
user    1m6.516s
sys     0m0.256s

my $sql = "SELECT `mem_used`, `swap_used`, `mem_total` 
FROM `$config{db}{data_table}` 
WHERE  `host_id` = $host_id 
AND date >= '$date' 
AND (UNIX_TIMESTAMP(`date`) - UNIX_TIMESTAMP('$date')) <= ($interval * 60);"; #SLOW

$ time ./data_smoothing.pl

real    0m10.005s
user    0m0.108s
sys     0m0.028s

my $sql = "SELECT `mem_used`, `swap_used`, `mem_total` 
FROM `$config{db}{data_table}` 
WHERE  `host_id` = $host_id 
AND (`date` BETWEEN '$date' 
AND DATE_ADD('$date', INTERVAL $interval MINUTE));"; #FAST

$ time ./data_smoothing.pl

real    0m0.190s
user    0m0.084s
sys     0m0.016s

如何创建表(取自mysqldump)

CREATE TABLE `data` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `host_id` smallint(6) NOT NULL,
  `date` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `mem_total` double(10,3) DEFAULT NULL,
  `mem_used` double(10,3) DEFAULT NULL,
  `swap_total` double(10,3) DEFAULT NULL,
  `swap_used` double(10,3) DEFAULT NULL,
  `CPU_count` smallint(6) DEFAULT NULL,
  `load_avg_1` float DEFAULT NULL,
  `load_avg_5` float DEFAULT NULL,
  `load_avg_15` float DEFAULT NULL,
  `uptime` double(10,3) DEFAULT NULL,
  `cpuIdlingTime` double(10,3) DEFAULT NULL,
  `rxBytesTotal` bigint(20) DEFAULT NULL,
  `txBytesTotal` bigint(20) DEFAULT NULL,
  `rxPacketsTotal` bigint(20) DEFAULT NULL,
  `txPacketsTotal` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`,`host_id`),
  KEY `fk_data_hosts` (`host_id`),
  KEY `date_memtot_hosts` (`date`,`mem_total`,`host_id`),
  CONSTRAINT `fk_data_hosts` FOREIGN KEY (`host_id`) REFERENCES `hosts` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=145300 DEFAULT CHARSET=utf8;

3 个答案:

答案 0 :(得分:5)

最后一个是最快的,因为你的比较非常适合索引。其他的,不是那么多。

在测试之前,当您使用列的值调用函数(或执行其他任何操作)时,您会发现几乎无法使用索引快速查找匹配的行。引擎必须基本上遍历整个表格,抓住日期,用它做一些数学运算,然后然后检查条件是否为真。

与此同时,如果您只是说BETWEEN this_value AND that_value,MySQL根本不需要做太多 - 它可以参考索引并找到范围的两个端点,这要快得多。

DATE_ADD('$date', INTERVAL $interval MINUTE)的调用对运行时间影响不大,因为MySQL通常足够聪明,可以缓存它知道不会改变的值,因此每次都不需要再次计算它们。

至于前两者之间差异的原因,我无法告诉你。也许TIMESTAMPDIFF可能很慢。也许转换和数学使用时间戳更简单,特别是考虑UNIX_TIMESTAMP('$date')每次都不需要重新计算。但所有这些只是在猜测。

答案 1 :(得分:0)

似乎是与索引相关的问题,您可以发布表创建语句,以便我们可以看到您的索引吗?

我知道&lt;或者&gt;使用索引并不好,而BETWEEN很好......可能会获得BETWEEN子句中列出的第一个日期的好处。 DATE_ADD中的任何内容都不会用于索引目的(因为函数会破坏索引)

答案 2 :(得分:-1)

对于TIMESTAMPDIFF版本,参数的顺序似乎是错误的。为了获得正结果,第二个参数应该是两个日期中的较晚者。它的编写方式,TIMESTAMPDIFF(MINUTE,date,'$ date')&lt; = $ interval将永远为真。由于将返回更多结果行,这可以解释为什么TIMESTAMPDIFF版本的性能似乎比UNIX_TIMESTAMP版本差得多。