【问题标题】:How do I deal with concurrent changes in a web application?如何处理 Web 应用程序中的并发更改?
【发布时间】:2011-08-28 20:51:26
【问题描述】:

这是我想在 Web 应用程序中执行的两个潜在工作流程。

变化 1

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

变体 2:

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

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

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

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

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

谢谢。

【问题讨论】:

    标签: database web-applications concurrency race-condition


    【解决方案1】:

    据我所知,这个问题没有通用的解决方案。

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

    我知道三种基本方法:

    1. 当用户读取数据库时,锁定记录,在用户保存任何更新之前不要释放。在实践中,这是非常不切实际的。如果用户打开一个屏幕然后去吃午饭而不保存怎么办?还是当天回家?还是因为试图更新这个愚蠢的记录而感到沮丧,以至于他退出了,再也没有回来?

    2. 将您的更新表示为增量而不是目标。举一个经典的例子,假设您有一个记录库存库存的系统。每次有销售时,您必须从库存计数中减去 1(或更多)。

    假设当前手头的数量是 10。用户 A 创建销售。当前数量 = 10。用户 B 创建销售。他还得到当前数量 = 10。用户 A 输入已售出两个单位。新数量 = 10 - 2 = 8。保存。用户 B 输入一个已售出的单位。新数量 = 10(他加载的值)- 1 = 9。保存。显然,出了点问题。

    解决方案:不要写“update inventory set quantity=9 where itemid=12345”,而是写“update inventory set quantity=quantity-1 where itemid=12345”。然后让数据库排队更新。这与策略#1 非常不同,因为数据库只需将记录锁定足够长的时间即可读取、更新和写入。无需等待有人盯着屏幕。

    当然,这仅适用于可以表示为增量的更改。例如,如果您要更新客户的电话号码,那将无法正常工作。 (例如,旧号码是 555-1234。用户 A 说将其更改为 555-1235。这是 +1 的变化。用户 B 说将其更改为 555-1243。这是 +9 的变化。所以总变化是+10,客户的新号码是 555-1244。:-) ) 但在这种情况下,“最后一个点击回车键的用户获胜”可能是你能做的最好的事情。

    1. 更新时,检查数据库中的相关字段是否与您的“来自”值匹配。例如,假设您在一家律师事务所工作,为您的客户谈判合同。您有一个屏幕,用户可以在其中输入有关谈判的注释。用户 A 调出合同记录。用户 B 调出相同的合同记录。用户 A 输入他刚刚通过电话与对方通话并且他们同意提议的条款。用户 B 也一直试图给对方打电话,他输入说他们没有回应电话,他怀疑他们在阻挠。用户 A 点击保存。我们是否希望用户 B 的 cmets 覆盖用户 A 的?可能不是。相反,我们会显示一条消息,指出自他阅读记录以来笔记已更改,并允许他在决定是否继续保存、中止或输入其他内容之前查看新值。

    [注意:论坛正在自动重新编号我的编号列表。我不确定如何覆盖它。]

    【讨论】:

      【解决方案2】:

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

      UPDATE tableA  SET status=2  WHERE 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;
      

      【讨论】:

        【解决方案3】:

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

        当您访问数据库时,事情会变得更加复杂 - 即状态所在的位置。你需要transactions 来确保一切正常。

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

        【讨论】:

        • 谢谢。这适用于变体 1。但是,在您没有交易的情况下,通常的处理方法是什么?例如MySQL 的 MyISAM
        • 澄清一下,我想很多论坛软件都必须处理这类问题。然而,我看到他们中的许多人使用没有事务的 MyISAM。他们对此有何解决方案?
        • 您无法可靠地处理它。论坛软件以插入为主,很少并发编辑,所以问题不大
        • 变体 2 的一个可能情况是您可能要编辑的每一行的 updated_at 字段。更新行时,您将再次将该时间戳发送回服务器,以确保用户没有使用旧数据发送他的更新(数据库中的 update_at 比您请求中的更新)。然而,这实际上仍然需要交易是真正可靠的。
        猜你喜欢
        • 2013-04-06
        • 2010-10-02
        • 1970-01-01
        • 2012-09-20
        • 2021-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多