我编写了一个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;
答案 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版本差得多。