【问题标题】:Read Lock on a row of a Database table对数据库表的一行进行读取锁定
【发布时间】:2013-11-05 15:29:19
【问题描述】:

我正在尝试构建一个作业调度程序。我有一个要在 2-3 台不同机器上按时间执行的作业列表。因此,任何机器都可以选择任何工作并在其next_execution_time < current_time 时执行它。我将所有作业存储在数据库表中,并在 SQL 中使用SELECT.... FOR UPDATE 查询来选择要执行的作业。

但是这种方法的问题是,如果一台机器1选择了一个作业,因为只有写锁,其他机器也会选择相同的作业执行,但不能执行,因为它们会等待锁将被释放或锁超时。那么有什么方法可以让其他机器跳过这个作业并使用 SQL 锁执行其他作业。数据库中不应该添加其他列吗?

流程是这样的:

select a job and lock it -> execute the job -> release the lock

我正在使用 ruby​​-on-rails 来开发它。如果 rails 中没有等待或 set_lock_timeout = 0。它可能可以解决问题。如果存在...语法是什么?

【问题讨论】:

  • 您可以为正在处理的行设置一个标志,例如being_processed="Y",并通过being_processed!="Y"过滤行。
  • @fedorqui 是的,我使用了一个额外的列 "locked_by" = machine_ip 来检查作业是否被锁定/执行,但不建议这样做,因为如果机器在执行时停机一个任务,锁永远不会被释放。
  • 是的,你是对的。您也可以为此使用redis 并设置一个在一定时间后过期的标志。

标签: mysql sql ruby-on-rails cron locking


【解决方案1】:

其实你有一个简单的方法来用你在mysql中的当前表来做这件事,你需要在选择下一个任务时临时锁定表。我假设您在表中有一个列来标记已经开始/完成的任务,否则您可以使用与日期时间相同的列来启动作业来标记它已经完成/开始:

lock tables jobs write;

select * from jobs where start_time < current_time and status = 'pending' order by start_time;
-- be carefull here to check for SQL errors in your code and run unlock tables if an exception is thrown or something like that

update jobs set status = 'started' where id = the_one_you_selected_adobe;

unlock tables;

就是这样,多个并发线程/进程 cab 使用作业表来执行任务,而无需 2 个线程运行相同的任务。

【讨论】:

  • 感谢您的快速回复。我一直在使用相同的方法,但同样的问题是,如果机器在执行任务时出现故障,状态将永远不会改变,并且该作业将永远不会执行,因为状态将始终启动。所以我问在sql server级别内部是否有任何锁定技术,它将自行处理这种锁定机制。
  • 抱歉耽搁了,是的,有这样一个机制,它叫Transaction。具体实现可能因您使用的引擎而异(我假设您使用 MySQL 作为问题的标签)但 innoDB def 支持事务。想法是一样的,您需要从表中读取(选择)下一个任务,并将任务的状态更新为“已启动”或类似事务中的状态,然后 DBMS 将负责其余的工作。
猜你喜欢
  • 1970-01-01
  • 2011-05-08
  • 2020-01-31
  • 1970-01-01
  • 2013-11-24
  • 1970-01-01
  • 1970-01-01
  • 2019-12-23
  • 2011-01-19
相关资源
最近更新 更多