阻止并发mysql更新查询

时间:2013-01-14 13:25:23

标签: php mysql transactions

我有一个数据库和一个表(innoDB):table。

此表中的行为:m1和m2。

我在一个页面上有2个提交按钮。

提交1执行此操作:

Database::q('UPDATE table SET m1 = ?s, m2 = ?i WHERE id = ?i', $n, 0, $ID);

提交2执行此操作:

Database::q('UPDATE table SET m2 = ?s WHERE id = ?i', $n2, $ID);

问题:如果用户同时提交两个表单。让我们说提交1上有10万($ n),提交2上有50($ n2)。 结果将在更新后:m1上99 950和m2上10万。

我该如何防止这种情况? 我尝试使用交易,但仍然无法正常工作。

这种语法是否正确?我不明白何时使用whitch one:exec,query,execute?

try {
   Database::beginTransaction();
   Database::q('..');
   Database::commit();
} catch (Exception $e) {
   Database::rollBack();
   echo 'ERROR!';
}

这是我正在使用的数据库类: http://pastebin.com/PfsiYysX

2 个答案:

答案 0 :(得分:0)

  

如果用户同时提交两个表单

意味着您对1个文件或2个单独的php文件有2个http请求 - 这并不重要。在任何情况下都没有办法(也没有交易)来避免这个问题。

事务用于保护需要一个接一个地执行的大量查询。它们必须在执行之前为sql引擎所知。有2个请求,发送不同的单语句查询,这是不可能的。

一种出路是通过javascript停用按钮2,按下1,反之亦然。但这仅适用于性能良好的客户端(无安全措施!),并且启用了js。

答案 1 :(得分:0)

正如马丁建议的那样,一些javascript会阻止意外按下两个按钮。但是它并不适用于所有情况,当然也没有提供任何保护措施来防止两种形式的故意提交。但是,它会阻止大多数用户到达您对操作应用真实有效控件的位置。

更安全的解决方案是合并一次使用CSRF令牌(两种形式的相同令牌的副本) - 在第一次请求时检查并使令牌过期。这样做的问题是,如果用户拆分会话(打开新窗口),用户可能会发现存在故障。有一些方法可以减轻这种影响 - 但这取决于应用程序实现方式的具体细节。

更好的解决方案是将其建模为有限状态机,允许在数据处于初始状态时进行更新,但不允许后续更新。我已经提供了一个例子,但是你提供的数据是不可能的。因此,请考虑一下:用户已经填满购物篮并进入结账。在这一点上,他们有两个选择 - 购买或丢弃篮子。如果他们点击“购买”,如果是,那么您将状态设置为“购买”,其中购物篮状态为“结账时”,如果成功,则继续进行购买处理。如果他们点击“丢弃”,那么您将篮子状态更改为“丢弃”状态为“结账时”,如果成功,则继续进行清理。如果任一操作不成功,则报告错误。请注意,每个操作都应该自动提交 - 这不使用锁。