ruby-on-rails – 在同一个数据库上工作的多个工作线程 – 如何
我有一个数据库,其中包含需要操作的行列表.它看起来像这样:
id remaining delivered locked ============================================ 1 10 24 f 2 6 0 f 3 0 14 f 我在Ruby中使用DataMapper,但实际上我认为这是一个通用的编程问题,并不是我所使用的具体实现所特有的… 我正在创建一堆工作线程,它们执行类似这样的操作(伪ruby代码): while true do t = any_row_in_database_where_remaining_greater_than_zero_and_unlocked t.lock # update database to set locked = true t.do_some_stuff t.delivered += 1 t.remaining -= 1 t.unlock end 当然,问题是,这些线程相互竞争,整个事情并不是真正的线程安全. while循环中的第一行可以很容易地在多个线程中拉出相同的行,然后才有机会被锁定. 我需要确保一个线程同时只在一行上工作. 做这个的最好方式是什么? 解决方法
关键步骤是从数据库中选择未锁定的行并将其标记为已锁定.如果你能安全地做到这一点,那么其他一切都会好的.
我所知道的两种方式可以使这种安全是悲观和乐观的锁定.在并发性方面,它们都依赖于您的数据库作为最终保证. 悲观锁定 悲观锁定意味着当您选择要使用的行时,先获取锁定,以便其他任何人都无法读取它们. SELECT * from some_table WHERE ... FOR UPDATE 与mysql和postgres(以及可能的其他人)一起工作,并将阻止与数据库的任何其他连接读取返回给你的行(锁的粒度取决于所使用的引擎,索引等 – 检查数据库的文档).它被称为悲观,因为您假设将发生并发问题并预防性地获取锁.它确实意味着即使在没有必要的情况下您也要承担锁定的成本,并且可能会降低您的并发性,具体取决于您拥有的锁的粒度. 乐观锁定 乐观锁定是指一种技术,您不需要悲观锁定的负担,因为大多数时候不会有并发更新(如果您在读取行时更新行设置锁定标志为true,窗口相对较小). AFAIK只适用于一次更新一行 首先在表中添加一个整数列lock_version.每当您更新表时,将lock_version与您正在进行的其他更新一起递增1.假设当前的lock_version为3.更新时,将更新查询更改为 update some_table set ... where id=12345 and lock_version = 3 并检查更新的行数(db驱动程序返回此值).如果这更新1行,那么你知道一切都好.如果此更新为0行,则删除所需的行或其锁定版本已更改,因此您将返回到流程中的步骤1并搜索要处理的新行. 我不是datamapper用户,所以我不知道它/ plugins是否为这些方法提供支持. Active Record支持两者,因此如果数据映射器不支持,您可以在那里寻找灵感. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |