我正在尝试使用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 UPDATE
和UPDATE
之间执行t1
和t2
时会发生什么?以下是哪一个发生的?
- 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 UPDATE
和t1
之间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)
答案 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
语句中的主键值引用记录。