【问题标题】:can't make select+update transaction in mariadb execute atomically无法使 mariadb 中的 select+update 事务以原子方式执行
【发布时间】:2016-04-10 23:22:58
【问题描述】:

我正在尝试通过使用 mariadb 中的“任务”表来并行化 python 脚本,其中每一行描述一个任务,并且我的脚本的每个运行实例都执行类似以下简化的 python + peewee 代码的操作:

with db.transaction() as txn: 
   result = list(Table.raw("select id from tasks where started=0 limit 1"))
   if len(result) == 0:
      sys.exit("nothing left to do")

   my_next_task = result[0]

   # mark this task as started
   Table.raw("update tasks set started=1 where id=%s" % my_next_task.id)

# process my_next_task

但是,当我同时启动脚本的 2 个实例时,它们都开始执行相同的任务。我是否误解了在这种情况下交易应该如何工作?

【问题讨论】:

    标签: mysql python-3.4 mariadb peewee


    【解决方案1】:

    试试这个。以下代码保证只有1个线程在执行任务之前获取锁

    1.您正在执行事务的事实,确保一次只有 1 个线程可以执行更新

    2.注意,我们需要检查started = 0。这确保只有1个线程可以进行更新

    with db.transaction() as txn: 
       result = list(Table.raw("select id from tasks where started=0 limit 1"))
    
       // The update query should return number of rows updated. So if a thread updates the task, it should return 1, which means it acquired a lock. If not, it means the thread didn't acquire the lock
       got_lock = Table.raw("update tasks set started=1 where id=%s and started = 0" % my_next_task.id) > 0 
    
       if len(result) == 0:
          sys.exit("nothing left to do")
    
       my_next_task = if(got_lock) result[0] else return
    

    【讨论】:

    • 谢谢!那应该可以(尽管我认为您的意思是started=0而不是started 0)
    • 那我一定是对交易有误解。将多个查询分组在一个事务中是否不能保证它们将作为 1 个原子操作一起执行(或者以其他方式回滚)?
    • 是的,我的意思是开始 = 0
    • 事务不保证读取锁,这意味着可以并行发生 2 次读取。在您的代码中,您在读取后立即处理任务。所以同一个任务会被执行两次。在您的情况下,更新也是幂等的,这意味着它们可以一个接一个地执行。所以没有什么可以回滚的,因为在同一行上不能有冲突更新开始 = 1 两次。
    • 在我的代码中,任务仅在读写事务之后处理(在#process my_next_task)。我知道读取可以并行发生,但是读取+写入一起不是幂等的。我认为将读+写包装在一个事务中可以确保它的执行就像每个“读+写”事务以某种顺序连续发生一样。
    猜你喜欢
    • 2018-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-14
    • 1970-01-01
    • 2017-06-22
    • 2021-04-17
    相关资源
    最近更新 更多