【问题标题】:How does NOW() interact with transactions?NOW() 如何与事务交互?
【发布时间】:2015-12-02 11:39:16
【问题描述】:

我正在运行对一个表执行多次更新的事务(可能需要几秒钟),然后对第二个表执行另一次更新。最终更新的时间戳为 NOW():UPDATE KeyTimestamps SET timestamp=NOW() WHERE key=$key

我担心更新以一种顺序发生,但时间戳看起来像是以另一种顺序发生的。这样我们就有了如下的表状态序列:

key | timestamp
----+--------------------
 A  | 1970-01-01 00:00:00
 B  | 1970-01-01 00:00:00

key | timestamp
----+--------------------
 A  | 2015-12-02 12:00:00
 B  | 1970-01-01 00:00:00

key | timestamp
----+--------------------
 A  | 2015-12-02 12:00:00
 B  | 2015-12-02 11:59:59

这会发生吗?手册says

NOW() 返回一个常数时间,指示语句开始执行的时间。

但是一个查询可以先于另一个开始执行,然后再完成执行吗?交易可以保护我免受这种情况的影响吗?

【问题讨论】:

  • 一般来说是可以的。您需要更具体地了解哪个线程运行什么,以获得更具体的答案。
  • 有什么替代方案?您可以在 MySQL 之外生成该值并将其作为常量参数传递给 UPDATE 语句 - 在锁争用的情况下,这将导致基本相同的问题。唯一真正的替代方案是消除争用的可能性,因此语句顺序执行,而不同时运行其他语句。
  • 如果我不需要它作为时间戳,还有其他选择吗?例如。带有 auto_increment col 的表 Monotone 怎么样。在一个事务中:许多更新; INSERT INTO Monotone () VALUES (); UPDATE KeyMonotones SET key='A', monotone=LAST_INSERT_ID()。 (或者在 Monotone 中只有一行,UPDATE Monotone SET val=LAST_INSERT_ID(val+1)。)这会安全吗/有更简单的方法吗?

标签: mysql transactions timestamp


【解决方案1】:

正如手册所说,MySQL 在每个单独语句的开头绑定NOW() 的值。因此,如果在一个语句中多次使用该值,则它是相同的。

如果您需要在多语句序列(事务)的开头绑定值,请执行以下操作:

SET @start := NOW();
SELECT something;
UPDATE something else;
...
UPDATE KeyTimestamps SET timestamp=@start WHERE key=$key

每个不同的客户端连接都将保留其自己的变量值,例如@start,因此当您有多个客户端同时执行事务时,这将起作用。

当心:您在几乎同时从不同客户端启动的操作将以形式上不可预测的顺序运行。它们不会总是按照你的直觉告诉你的顺序运行。所以尽量避免在你的系统设计中依赖于精确的操作顺序。自动递增 id 也是如此。如果您确实使您的系统依赖于这些东西,那么您正在创造race conditions 的可能性。这些都是血腥的谋杀。

【讨论】:

  • 您和 Gordon Linoff 似乎不同意自动增量列是否始终按插入顺序编号。您是否碰巧引用了它不安全的引文?
  • 我不认为我们不同意。我确信 MySQL 永远不会分配重复的自动递增 ID 号。而且,它们将始终按插入顺序编号。我的观点是关于使用异步网络和操作系统进行的近实时操作的微妙之处。大多数情况下,您的插入按照您的直觉告诉您它们会发生的顺序发生,但有时它们不会。那是因为“同时”是一个模糊的概念。因此,如果您有多个操作在彼此之间的几毫秒内开始,那么首先发生哪个操作的问题很棘手。
  • 哦,明白了。所以你说它永远不会(没有明确的干预)显示自动递增的 id 1 和 3,然后是 1、2 和 3;但如果我尝试按顺序插入 A、B 和 C,它可能会给我 1-A、2-C、3-B?
  • 正是如此。订单可能很滑。
  • 我来自未来告诉你以上是错误的,至少对于带有 InnoDB 的 MySQL 5.7。两个单独的连接都启动一个事务。一个执行 INSERT,两个执行 INSERT。两个提交事务。如果您现在选择,您将看到一条记录,id = 2。然后提交其事务。现在如果你选择,你会看到两条记录,id 1 和 2。从这个意义上说,id 是乱序的。在事务中,INSERT 将获取一个 id 并递增,甚至在事务提交之前。 (同样如果事务被回滚,这个数字也不会被填满)
【解决方案2】:

评论有点长。

如果您想知道插入到表中的顺序,请使用自动递增列。这保证永远不会有重复,并且应该按插入顺序分配。然后,您可以将此排序与使用时间戳列的排序进行比较。

请注意,同时插入的两行的时间戳列可能相同。

【讨论】:

    猜你喜欢
    • 2011-02-10
    • 1970-01-01
    • 2016-05-10
    • 1970-01-01
    • 2011-08-25
    • 2011-03-21
    • 2011-10-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多