我有一张表,由不同的线程同时读取。
每个线程必须选择 100 行,对每行执行一些任务(与数据库无关),然后必须从表中删除所选行。
使用此查询选择行:
SELECT id FROM table_name FOR UPDATE;
我的问题是:如何忽略(或跳过)之前在 MySQL 中使用 select 语句锁定的行?
我通常创建一个默认为 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;
这确保每个线程从表中选择一组唯一的行。
希望这有帮助。
尽管这不是最好的解决方案,因为我无法知道忽略锁定的行,但我选择随机一个并尝试获取锁定。
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
为事务设置一个小的超时,如果该行被锁定,则事务将中止,然后我尝试另一行。如果我获得了锁,我就会处理它。如果(运气不好)该行被锁定,它会被处理并在超时之前释放锁,然后我选择一个已经被“处理”的行!但是,我检查我的流程设置的字段(例如状态):如果其他流程事务正常结束,该字段告诉我工作已经完成,我不会再次处理该行。
没有事务的所有其他可能的解决方案(例如,如果行没有状态等,则设置另一个字段等)可以轻松提供竞争条件和错过的进程(例如,一个线程突然死亡,分配的数据仍然被标记,而事务过期;参考评论这里
希望有帮助
从 8.0.1 版本开始,Mysql 现在有 SKIP LOCKED 和 NOWAIT。顾名思义,在获取锁定时,它会忽略是否存在已锁定的行。
这个博客很好地捕捉到了这一点。