如何处理Web应用程序中的并发更改?

时间:2011-08-28 20:51:26

标签: database web-applications concurrency race-condition

以下是我希望在Web应用程序中执行的两个潜在工作流程。

变异1

  • 用户发送请求
  • 服务器读取数据
  • 服务器修改数据
  • 服务器保存修改后的数据

变化2:

  • 用户发送请求
  • 服务器读取数据
  • 服务器向用户发送数据
  • 用户发送带有修改的请求
  • 服务器保存修改后的数据

在每种情况下,我都想知道:确保对此服务的并发访问能够产生合理结果的标准方法是什么? (即,没有人的编辑被破坏,值对应于编辑的某些顺序等)。

这种情况是假设的,但这里有一些细节,说明我在实践中可能需要处理的问题:

  • 网络应用程序,但语言未指定
  • 可能,使用网络框架
  • 数据存储是SQL关系数据库
  • 所涉及的逻辑太复杂,无法在查询中很好地表达,例如value = value + 1

我觉得我宁愿不尝试在这里重新发明轮子。当然,这些是众所周知的解决方案中众所周知的问题。请指教。

感谢。

3 个答案:

答案 0 :(得分:6)

据我所知,这个问题没有一般解决方案。

问题的根源在于用户可能会在进行更新和保存之前检索数据并在屏幕上长时间盯着它。

我知道三种基本方法:

  1. 当用户读取数据库时,请锁定记录,并且在用户保存任何更新之前不要释放。在实践中,这是非常不切实际的。如果用户打开屏幕,然后在不保存的情况下去吃午餐怎么办?或者回家过一天?或者是如此沮丧地试图更新这个愚蠢的记录,他退出并且永远不会回来?

  2. 将您的更新表达为增量而非目的地。举一个典型的例子,假设您有一个记录库存中库存的系统。每次进行销售时,您必须从库存盘点中减去1(或更多)。

  3. 所以说现有的数量是10.用户A创造销售。当前数量= 10.用户B创建销售。他还获得当前数量= 10.用户A输入两个单位出售。新数量= 10 - 2 = 8.保存。用户B输入一个售出的单位。新数量= 10(他加载的值) - 1 = 9.保存。显然,出了点问题。

    解决方案:写入“更新库存集数量=数量-1,其中itemid = 12345”,而不是写“更新库存集数量= 9,其中itemid = 12345”。然后让数据库对更新进行排队。这与策略#1非常不同,因为数据库只需要锁定记录足够长的时间来读取它,进行更新并写入它。当有人盯着屏幕时,它不必等待。

    当然,这仅适用于可以表示为delta的更改。如果你是,比如说,更新客户的电话号码,那就不行了。 (比如,旧数字是555-1234。用户A表示将其更改为555-1235。这是+1的变化。用户B表示将其更改为555-1243。这是+9的变化。所以总变化是+10,客户的新号码是555-1244。:-))但在这种情况下,“最后一位用户点击输入键获胜”可能是你能做的最好的。

    1. 在更新时,检查数据库中的相关字段是否与“from”值匹配。例如,假设您为一家律师事务所工作,为您的客户谈判合同。您有一个屏幕,用户可以在其中输入有关谈判的说明。用户A提出合同记录。用户B提出相同的合同记录。用户A输入他刚刚通过电话与另一方通话,他们同意建议的条款。用户B也试图打电话给另一方,他说他们没有回应电话,他怀疑他们正在阻止他们。用户A单击保存。我们是否希望用户B的评论覆盖用户A?可能不是。相反,我们会显示一条消息,指示自从他读取记录后笔记已被更改,并允许他在决定是继续保存,中止还是输入不同内容之前查看新值。
    2. [注意:论坛会自动重新编号我的编号列表。我不知道如何覆盖它。]

答案 1 :(得分:0)

应用层中的事情很简单 - 每个请求都由不同的线程(或进程)提供服务,所以除非你的处理类(服务)中有状态,否则一切都是安全的。

当你到达数据库时,事情变得更加复杂 - 即状态被保持。在那里你需要transactions以确保一切正常。

事务具有一组属性 - ACID,“保证可靠地处理数据库事务”。

答案 2 :(得分:0)

如果mysql中没有事务,可以使用update命令确保数据没有损坏。

UPDATE tableA  SET status=2  WHERE status = 1

如果status为1,那么只有一个进程可以获得记录更新的结果。在下面的代码中,如果未执行更新,则返回-1(如果没有要更新的行)。

PreparedStatement query;
query = connection.prepareStatement(s);
int rows = -1;
try
{
    rows = query.executeUpdate();
    query.close();
}
catch (Exception e)
{
   e.printStackTrace();
}
return rows;