【问题标题】:Assigning data-entry tasks to multiple concurrent web-app users将数据输入任务分配给多个并发 Web 应用用户
【发布时间】:2009-05-31 08:30:55
【问题描述】:

我试图想出一种有效的方法来让一群人完成一系列数据输入任务。以前我们只有一个人这样做,所以这不是问题。后端是 RDBMS,前端是 Web 应用程序。

目前我们做这样的事情:

分配一条记录进行编辑:

SELECT * FROM records WHERE in_edit_queue LIMIT 1;

那么,

保存对先前分配的记录的更改:

UPDATE records SET ..., in_edit_queue = false
  WHERE id = ? AND in_edit_queue = true;

这意味着可以为两个用户分配相同的记录进行编辑,我们支持第一个提交的用户,在随后的提交中静默失败,例如:

  1. 用户 A 加载记录 321 进行编辑
  2. 用户 B 加载记录 321 进行编辑
  3. 用户 B 提交更改(它们保存在数据库中)
  4. 用户 A 提交更改(未保存在数据库中)

(注意:我们可以信任所有用户提交可接受的数据,因此我们无需保留来自第二个UPDATE 的数据。)

这种方法的问题是,当用户同时开始并以大致相同的速度进行编辑时,他们通常会更新相同的记录,但只有其中一条记录被保存。换句话说,浪费了大量的工时。我可以通过选择随机行在一定程度上缓解这种情况,但我更喜欢更有保证的东西。

这就是我的想法......

有一个表叫:locked_records (record_id integer, locked_until timestamp)

-- Assign a record for editing:
-- Same as before but also make sure the
-- record is not listed in locked_records...
SELECT * FROM records
  WHERE in_edit_queue AND id NOT IN (
    SELECT record_id FROM locked_records
    WHERE locked_until > now() )
  LIMIT 1;

-- ..and effectively remove it from
-- the queue for the next 5 minutes
INSERT INTO locked_records (record_id, locked_until)
  VALUES (?, now() + 300);

然后:

UPDATE records SET ..., in_edit_queue = false
  WHERE id = ? AND in_edit_queue = true;
DELETE FROM locked_records WHERE record_id = ?;

一个典型的编辑大约需要 30 秒到 1 分钟,队列中的 5 分钟应该是一个不错的数字。我还可以让网络应用程序上的 XHR 继续更新锁,如果结果证明是有利的。

任何人都可以对此提出想法吗?听起来像是一种很好的做事方式?听起来像一个可怕的方式?以前做过这个?我很想听听一些反馈。

谢谢! J

【问题讨论】:

    标签: sql concurrency locking


    【解决方案1】:

    RDBMS 的内部锁列表怎么样?将 SELECT 语句更改为 SELECT FOR UPDATE 是一种选择吗?

    【讨论】:

    • 我不这么认为,因为 UPDATE 将通过不同的数据库连接发生,因为它是一个 Web 应用程序。或者根本没有,再说一次.. 它是一个网络应用程序 :-) 如果我有什么误解,请告诉我......
    • 附注如果您的意思是作为维护单独的locked_records 表的替代方案,那么是的,我认为这会起作用并且更优雅,但是“记录”表会不断被非编辑用户大量读取,我不想锁定出于这个原因。
    • 必须评估使用不同数据库连接的可能性;你说得对,SELECT FOR UPDATE 在那种情况下是行不通的。 SELECT FOR UPDATE 不应该阻止大多数 RDBMS 中的读者访问,你必须检查你的文档。
    【解决方案2】:

    另一个想法:这些记录有两个额外的列:assigned_to 和 completed。

    当有人想要编辑记录时,请执行以下操作

    update records set assigned_to = ? # assigning to 'me'
    where assigned_to is null
    and completed = false
    limit 1 # only assign one record at a time
    

    然后,要恢复该行:

    select ...
    from records
    where assigned_to = ? # assigned to 'me'
    and completed = false
    

    一旦你完成了,你将完成设置为“真”。

    您可以有一个额外的时间戳列,用于记录何时分配给某人,然后在上面的更新语句中的 where 子句的“assigned_to is null”部分添加一个 OR 替代项,您需要一定的审查一个有效的分配。

    【讨论】:

      猜你喜欢
      • 2014-11-29
      • 1970-01-01
      • 2019-11-30
      • 1970-01-01
      • 2021-07-05
      • 2016-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多