2014-03-31 85 views
0

我想用MYSQL实现一个队列,并且想确保我正确理解SELECT FOR UPDATESELECT 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将在时间t2执行SELECT FOR UPDATE在时间t1UPDATE。当机器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执行t2t1t2UPDATE之间SELECT FOR UPDATE会发生什么?哪些发生?

- 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

您需要执行事务(autocommit=0),因为只有SELECT FOR UPDATE在事务完成之前持有该锁的内部这些行动。

您的场景都不会发生。交易不会立即失败,但可能会超时。

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

确保您在任何时候选择记录并服从您使用的任何当前锁定SELECT FOR UPDATE。没有FOR UPDATE的正常SELECT不会等待任何锁定。

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

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