SELECT FOR UPDATE如何实际工作?

时间:2014-03-31 18:41:55

标签: mysql sql concurrency locking queue

我正在尝试使用MYSQL实现队列,并希望确保我正确理解SELECT FOR UPDATE

我的表:

Table jobs
Fields: id (INT), state (VARCHAR), queued_time (TIMESTAMP)

当我插入作业时,状态为QUEUED。当我锁定作业时,状态变为PROCESSING

我有多台机器,每台机器都使用相同的数据库连接。当机器准备好从队列中取出某些东西时,它会调用

SELECT FROM jobs WHERE state = "QUEUED" ORDER BY queued_time ASC LIMIT 1 FOR UPDATE; UPDATE jobs SET state = "PROCESSING" WHERE state = "QUEUED" ORDER BY queued_time ASC LIMIT 1;

在查询之后,我检查UPDATE是否成功,如果是,我允许机器处理SELECT FOR UPDATE返回的作业。

假设机器1和2准备从队列中取出一些东西。队列如下所示:

id        state        queued_time
1         QUEUED       2014-03-30 20:04:43
2         QUEUED       2014-03-30 22:04:43

计算机1将在SELECT FOR UPDATE时执行t1,在时间UPDATE执行t2。机器2在SELECT FOR UPDATEUPDATE之间执行t1t2时会发生什么?以下是哪一个发生的?

- Machine 1 ends up with job 1, and machine 2's `UPDATE` fails because machine 1 locked the row and never unlocked it (this is my current understanding)

- Machine 2 ends up with job 1, and machine 1's `UPDATE` fails

- Both machines end up with job 1

当机器2在SELECT FOR UPDATEt1之间t2以及UPDATE之后t2执行时会发生什么?以下是哪一个发生的?

- Machine 1 ends up with job 1, and machine 2's `UPDATE` fails

- Machine 2 ends up with job 1, and machine 1's `UPDATE` fails

- Both machines end up with job 1 because machine 2's `UPDATE` succeeds since machine 1 release the lock (this is my current understanding)

1 个答案:

答案 0 :(得分:1)

您需要在事务(autocommit=0)内执行这些操作,因为SELECT FOR UPDATE只保留锁,直到事务完成。

您的所有情况都不会发生。交易不会立即失败,但可能会超时。

如果事务2尝试锁定与事务1相同的记录(锁定发生在SELECT FOR UPDATE),则事务2将必须等到事务1完成。如果达到锁定超时期限(innodb_lock_wait_timeout),则事务将超时("失败")。

确保您想要选择记录并遵守您使用的任何当前锁定SELECT FOR UPDATE。没有SELECT的正常FOR UPDATE不会等待任何锁定。

显然,您应该尽快完成任何交易。

你的方法应该可以正常工作。您应该考虑在SELECT FOR UPDATE语句中检索主键值,然后根据UPDATE语句中的主键值引用记录。