【问题标题】:DATE_SUB and INTERVAL calculation may be wrongDATE_SUB 和 INTERVAL 计算可能有误
【发布时间】:2014-05-01 20:18:47
【问题描述】:

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

背景:我们有一个带有两个独立 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

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

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

【问题讨论】:

  • 99% 的情况下,当您使用完善的工具时,问题不在于工具,而在于用户。您确定record_timeDATETIME 值而不仅仅是时间(以小时为单位,而不是小时和天、月、年......)?会不会随时更新?
  • @Floris 是的,record_time 始终是包含正确日期和时间值的 datetime,并按原样输入。我仔细检查以验证。检测这样的问题相当容易。不,在这种情况下,record_time 值一旦输入就永远不会改变。任何一个守护进程中都没有其他更新语句。唯一被操纵的列是set 列。
  • 我想确保在进行选择之前不存在具有随机值的条目。为什么不总是使用相同的值?首先,将所有1的值设置为0;然后将您需要的记录设置为1
  • @Floris 是的。你和斯宾塞是对的;这确实是导致问题的随机数。应该意识到的。我们最初确实有一个步骤,其中“旧”set 值被重置为不同的值,但我们在没有意识到后果的情况下将其删除,以尝试调试一些死锁问题。我们将不得不重新实现类似的东西来解决它。感谢您的帮助!

标签: java mysql sql join sql-update


【解决方案1】:

我们将假设id 是表的主键,或者它是唯一键。

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

我推荐一个生成 unique 值而不是 random 值的数字生成器,因为相同的 random 值必然会再次出现在某个时候。

如果发生这种情况,这组 48 行将使用“随机”值进行更新,但是基于该“随机”值检索这些行的后续查询也将获取先前更新为的行相同的“随机”值。


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


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

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

 NOW() - INTERVAL 1 HOUR

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

【讨论】:

  • 是的,id 确实是 PRIMARY 和 UNIQUE。抱歉应该提到这一点。这绝对解决了问题 - 你是绝对正确的,这是罪魁祸首的“随机”数字。最初,set 值在推送后被更新为正等值,因此使用伪随机负数进行选择从来都不是问题。但是我们在尝试解决运行多个守护程序时的一些死锁问题时删除了该功能。鉴于数据的巨大增长率,应该意识到这一切。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2019-09-15
  • 1970-01-01
  • 2011-11-13
  • 2013-03-16
  • 1970-01-01
  • 2018-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多