DATE_SUB和INTERVAL计算可能是错误的

时间:2014-05-01 20:18:48

标签: java mysql sql join sql-update

这可能过于本地化,但希望可能有一些我根本无法找到的信息。

后台:我们有一个带有两个独立Java守护进程的系统;一个创建数据并插入数据库,另一个抓取最新的数据集(不超过1小时),然后推送到客户端。

问题:从此表中抓取的数据偶尔会超过1小时。时差可以是2小时到几个月之间的任何时间。

当前查询:获取1小时数据分为两步。首先,我们通过设置随机负数来“保留”一组记录:

UPDATE tracking_records as target
  JOIN 
    (SELECT tracking_records.id, `set`, unit_id, tracking_records.record_time 
     FROM tracking_records 
     WHERE `set` IS NULL and record_time > DATE_SUB(NOW(), INTERVAL 1 HOUR) 
     ORDER BY record_time DESC LIMIT 48) source 
  ON source.id = target.id
SET target.`set` = -1371504452;

注意:-1371504452是一个示例值;它是在Java中随机生成的。每个查询的LIMIT 48保持不变。

然后我们只需选择包含该随机set值的列。

这是tracking_records表格的结构:

+-------------+---------------------+
| Field       | Type                |
+-------------+---------------------+
| id          | bigint(20) unsigned |
| unit_id     | int(11)             |
| record_time | datetime            |
| latitude    | int(11)             |
| longitude   | int(11)             |
| created_at  | datetime            |
| updated_at  | datetime            |
| set         | int(11)             |
+-------------+---------------------+

如您所见,查询应仅匹配set列为空且记录时间最多为1小时的记录。

如上所述,我发现几个小时的时间差异,长达几个月(我迄今为止检测到的最早的时​​间是2013-09-23 11:01:08)。考虑到WHERE子句的时间限制,这对我来说没有意义。

我们使用的是mysql版本 5.5.29-0ubuntu0.12.04.2

问题:我很困惑,我想知道是否存在错误或其他一些问题导致时间计算失败如此剧烈,随机发生。要么是这样,要么查询本身存在问题,我根本就没有看到。

有没有人观察到错误的时间比较,DATE_SUB()函数或INTERVAL计算的问题,在这个版本的MySQL中可能解释我看到的异常时间?

1 个答案:

答案 0 :(得分:1)

我们假设id是表的PRIMARY KEY,或者它是一个独特的键。

最可能的怀疑是“随机”数字并不是唯一的。可能,Java正在使用伪随机数生成器,它实际上从同一个种子生成可重复的数字序列。

我建议使用生成唯一值的数字生成器,而不是随机值,因为必须重新出现相同的随机值在某些时候。

如果发生了这种情况,那么48行的集合将使用“随机”值进行更新,但是根据该“随机”值检索这些行的后续查询也将获取以前更新为相同的“随机”值。


话虽如此,使用您显示的SQL语句,如果其中两个语句在同一时间运行,则可能会有一个“碰撞”的小窗口。 (该内联视图实现了一个临时的MyISAM表,然后外部查询运行。我不确定内联视图查询是否获得跟踪记录中的行的独占或意图排他锁。但是那里发生了碰撞的症状将是一个后续查询,找不到具有指定set值的48行,因为另一个查询用它自己的值覆盖了该值。


你很可能在MySQL DATETIME处理中发现了一个错误。该代码已经运行多年,并且不太可能在那里引入错误。 (我已经使用它超过十年了(版本3.23,4.x,5.1,5.5)并且我从来没有遇到过DATETIME的错误。(我已经粗略地介绍了记录的行为。但从未遇到过真正的错误。)

请注意,DATE_SUB功能不是必需的;你可以得到相同的结果:

 NOW() - INTERVAL 1 HOUR

请注意,NOW()会在语句或块的开头进行评估;我们更喜欢将它用于其他功能,主要是因为它的复制是安全的。原始执行时计算的值保留在二进制日志中,以便可以在副本数据库上应用相同的值。每次调用函数时,都会重新评估返回当前时间的其他函数,并且不会保留该值以进行复制。