Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Database 事务数据库表中竞争条件的处理_Database_Race Condition - Fatal编程技术网

Database 事务数据库表中竞争条件的处理

Database 事务数据库表中竞争条件的处理,database,race-condition,Database,Race Condition,让我先把情况说清楚。假设你有一个商业应用程序的数据库,它跟踪的内容之一是库存。系统显示你有5个螺丝在库存中。假设你需要全部5个。系统为-5创建库存交易记录。在您提交该交易后,因为您知道您之前有5笔,并且您提取了5笔,如果您对该螺钉的所有库存交易记录进行汇总,则总数应为0。当两个人同时尝试这样做时,就会出现问题。假设一个人想要4个,另一个人想要2个。两个客户端应用程序都会事先检查数量,并且都会被告知5。同时,一个为-4创建事务,另一个为-2创建事务。总库存量的结果为-1,这是不可能的,因为系统不允

让我先把情况说清楚。假设你有一个商业应用程序的数据库,它跟踪的内容之一是库存。系统显示你有5个螺丝在库存中。假设你需要全部5个。系统为-5创建库存交易记录。在您提交该交易后,因为您知道您之前有5笔,并且您提取了5笔,如果您对该螺钉的所有库存交易记录进行汇总,则总数应为0。当两个人同时尝试这样做时,就会出现问题。假设一个人想要4个,另一个人想要2个。两个客户端应用程序都会事先检查数量,并且都会被告知5。同时,一个为-4创建事务,另一个为-2创建事务。总库存量的结果为-1,这是不可能的,因为系统不允许负库存

如果没有服务器应用程序来帮助您,您将如何解决这个问题?我提到这一点是因为协调库存事务的服务器是我解决问题的方法,但目前我们的产品没有服务器应用程序。我们只有直接与Firebird数据库对话的客户端应用程序。我正试图找出如何只使用客户端应用程序和数据库来实现这一点。一件可能有用的事情是Firebird有一个称为生成器的东西,它基本上是一个唯一的原子数生成器,所以你可以保证,如果你让Firebird增加生成器并给你下一个数字,它不会给其他任何人同样的数字

我的脑子里一直想着用生成器创建一个临时的记录锁。我想我可以让他们都检查Item表上的“lock”字段。如果为null,则没有人拥有锁。如果它是非空的,它将被锁定,因此您需要继续检查,直到它没有被锁定。如果没有锁,请向生成器请求uniq编号,并将其存储在要锁定的项目的锁定字段中。提交该事务,然后返回并检查Item表的lock字段是否确实包含您输入的数字。如果它锁定了,那么你已经成功锁定,如果它没有锁定,那么这意味着有人同时锁定了它,而你输掉了比赛。一旦你完成了,你就清空了锁,等待的客户端将看到这个空值,自己锁定它,然后重复

但我相信这本身就有一个种族条件。Trxn1(事务1)检查锁并找到null。Trxn2检查锁并找到null。Trxn1从生成器获取新锁号。Trxn2从生成器获得新锁。Trxn1表示,如果锁仍然为空,则使用我的锁更新项目记录。Trxn1提交trxn,然后启动一个新的Trxn1,并证明锁包含他的锁id,它这样做,它知道它有权进行库存交易,并开始这样做。在Trxn1检查是否获得锁之后,Trxn2提交其更新语句,如果锁为null,则该语句存储其锁。如果Trxn2在Trxn1提交锁之前执行了update语句,则Trxn2仍会将该值视为null,并且会进行更新。如果Trxn2的锁提交发生在Trxn1提交锁之后,并且已经验证了它,那么我们就有问题了。Trxn1正在对项目事务表进行更改。Trxn2之所以提交了锁,是因为在提交时,锁在其事务世界中为null,而在提交时,Trxn2的update语句将覆盖Trxn1的锁,因为update语句中的null检查发生在提交之前,而不是在提交时。所以现在他们都认为他们有一个锁,我们将以负库存结束

有谁能想出一种方法来解决这个问题,即使用某种排队系统(FIFO)的服务器应用程序?我更愿意通过客户“与数据库对话”来协调这一点,但从技术上讲,这可能是不可能的。抱歉,如果这有点罗嗦:D

解决方案编辑: 杰塔伯恩似乎有正确的想法。我不知道Firebird实际上有行级锁定。简单的select语句(无联接、group by等)可以在语句末尾追加“with lock”,语句返回的任何行都将被锁定,直到提交或回滚事务为止。没有其他人可以获得该行的锁,也不能对其进行更改。因为我不想在向ITEM事务表中插入行时锁定整个ITEM表,所以我将创建一个只用于锁定一列(ItemID字段)的表。因为第二个事务在尝试执行自己的锁时会出错,所以我从来没有真正修改过锁表本身上的任何内容,这并不重要。找不到锁给了我所有需要的信息。我将在ITEM表的insert/delete上放置触发器,以便对于每个ITEM记录,这也是ITEMLOCK表中的一条记录。下面是我将要使用的过程

  • 启动数据库事务
  • 试图使用要更改的项的ItemID获取ITEMLOCK行的锁
  • 如果无法锁定,请继续尝试,直到解锁记录
  • 一旦锁定,去证明该物品的现有数量足以满足您的需求
  • 想要取出,因为他们可能有旧数据,这可能不是 该案例将在此处退出并向用户发送消息
  • 如果数量足够,请在库存交易表中插入库存交易记录
  • 提交事务,然后释放锁
  • 注意:Matthieu M提到了FOR UPDATE子句。文档中提到了它以及with LOCK子句。据我所知,当您用一条语句锁定多行时,可以使用它。我不是百分之百的确定,但似乎用锁来做这件事会尝试全部或全部