【发布时间】:2011-05-05 19:06:59
【问题描述】:
我有一张表同时被不同的线程读取。
每个线程必须选择 100 行,在每行上执行一些任务(与数据库无关),然后他们必须从表中删除选定的行。
使用此查询选择行:
SELECT id FROM table_name FOR UPDATE;
我的问题是:如何忽略(或跳过)以前在 MySQL 中使用 select 语句锁定的行?
【问题讨论】:
标签: mysql locking race-condition
我有一张表同时被不同的线程读取。
每个线程必须选择 100 行,在每行上执行一些任务(与数据库无关),然后他们必须从表中删除选定的行。
使用此查询选择行:
SELECT id FROM table_name FOR UPDATE;
我的问题是:如何忽略(或跳过)以前在 MySQL 中使用 select 语句锁定的行?
【问题讨论】:
标签: mysql locking race-condition
我通常创建一个默认为 NULL 的 process_id 列,然后让每个线程使用唯一标识符来执行以下操作:
UPDATE table_name SET process_id = #{process.id} WHERE process_id IS NULL LIMIT 100;
SELECT id FROM table_name WHERE process_id = #{process.id} FOR UPDATE;
这确保每个线程从表中选择一组唯一的行。
希望这会有所帮助。
【讨论】:
locked_at 设置为时间,好吧,您已将记录锁定在。然后,您将修改UPDATE 语句以更新process_id 为NULL 或locked_at 早于某个合理间隔的记录。
尽管这不是最好的解决方案,因为我知道无法忽略锁定的行,但我选择了一个随机行并尝试获取锁。
START TRANSACTION;
SET @v1 =(SELECT myId FROM tests.table WHERE status is NULL LIMIT 1);
SELECT * FROM tests.table WHERE myId=@v1 FOR UPDATE; #<- lock
为事务设置一个小的超时,如果该行被锁定,事务被中止,我尝试另一个。如果我获得了锁,我会处理它。如果(运气不好)该行被锁定,它被处理并在我的超时之前释放锁定,然后我选择一个已经被“处理”的行!但是,我检查了我的流程设置的字段(例如状态):如果其他流程事务结束正常,则该字段告诉我工作已经完成并且我不再处理该行。
没有事务的所有其他可能的解决方案(例如,如果行没有状态,则设置另一个字段等等)很容易提供竞争条件和错过进程(例如,一个线程突然死亡,分配的数据仍然被标记,而交易过期;参考评论here
希望对你有帮助
【讨论】: