我编写了一个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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
如何创建表(取自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;
Run Code Online (Sandbox Code Playgroud)
最后一个是最快的,因为您的比较非常适合索引.其他人,不是那么多.
在测试之前,当您使用列的值调用函数(或执行其他任何操作)时,您会看到几乎无法使用索引快速查找匹配的行.引擎必须基本上遍历整个表,抓取日期,用它做一些数学运算,然后检查条件是否为真.
同时,如果你只是说BETWEEN this_value AND that_value,MySQL根本不需要做太多 - 它可以参考索引并找到范围的两个端点,这要快得多.
调用对DATE_ADD('$date', INTERVAL $interval MINUTE)运行时没有太大影响,因为MySQL通常足够聪明,可以缓存它知道不会改变的值,因此每次都不需要再次计算它们.
至于前两者之间差异的原因,我无法告诉你.也许TIMESTAMPDIFF就是这么慢.也许转换和数学在时间戳方面要简单得多,特别是考虑到UNIX_TIMESTAMP('$date')每次都不需要重新计算.但所有这些只是在猜测.