防止Web应用程序中的并发交易 [英] Prevent simultaneous transactions in a web application

查看:66
本文介绍了防止Web应用程序中的并发交易的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个Web应用程序(它是一个游戏),其中包含许多形式和元素,它们充当按钮并触发服务器上的某些动作.问题是,如果用户单击按钮的速度过快或在两个选项卡中打开网站,然后同时执行某些操作,则有时会混淆我们的应用程序.我们有一些基本的保护措施-MySQL事务,某些双击阻止Java脚本,但无论如何有时还是会漏掉一些东西.当然,最好的方法是重新设计所有SQL事务和支持功能,以免造成系统混乱.这种混淆的一个示例是同时发布两个更新-一个Web请求更改了数据库中的某些内容,但是第二个请求仍然使用旧数据运行,因此SQL更新返回受影响的行数为零",因为第一个事务已经更改数据库中的数据. 显而易见的解决方案是在UPDATE之前再次读取数据以查看是否仍需要更新,但这意味着到处都放置更多的double SELECT查询,这不是一个好的解决方案-为什么要两次从db读取相同的数据? 我们还考虑过使用一些隐藏的令牌在服务器上对每个更新请求进行比较,并拒绝具有相同令牌ID的操作,但这也意味着触摸了很多地方的代码,并可能在系统中引入了新的bug,除了可以正常工作之外,这个问题.

We have a web application (it is a game) with lots of various forms and elements which act as buttons and trigger some actions on server. The problem is that users can sometimes confuse our application if he clicks on buttons too fast or opens the website in two tabs and then issues some actions simultaneously. We have some basic protection - MySQL transactions, some double-click preventing Javascripts, but anyway sometimes something just skips out. Of course, the best way would be to redesign all the SQL transactions and supporting functions in the way that does not allow to confuse the system. One example of such confusion is issuing two updates simultaneously - one web request changes something in the db, but the second request operates still with the old data and so SQL update returns "number of affected rows was zero" because the first transaction has already changed data in the db. The obvious solution is to read data once again right before UPDATE to see if it still needs updating, but that means putting many more double SELECT queries everywhere, not a good solution - why to read the same data from db twice? Also we have considered using some hidden token to compare on the server on each updating request and deny operations which have the same token id, but this also means touching really many places of code and possibly introducing new bugs into the system which works just fine except this one problem.

操作流程的逻辑如下:如果用户同时发出两个请求,则第二个请求应等到第一个请求完成.但是我们还必须考虑到我们的应用程序中有很多重定向(例如在POST之后,以避免用户刷新页面时出现两次POST),因此该解决方案不应为用户创建死锁.

The logic of the action flow would be the following: if the user issued two requests simultaneously, the second request should wait until the first completes. But we must also consider that there are many redirects in our application (for example after POSTs to avoid double POSTing when user refreshes the page), so the solution should not create deadlocks for a user.

所以问题是: 什么是最简单的全局修补程序,它可以使所有用户操作按顺序进行?有什么好的通用解决方案吗?

So the question is: what would be the most easy possible global fix which could just somehow make all the user operations sequential? Is there any good universal solution?

我们正在使用MySQL和PHP.

We are using MySQL and PHP.

推荐答案

我很高兴您意识到这只是一个不好的临时解决方案,您应该优化代码. 忘记令牌和东西了. 最简单且仍然有效的方法可能是每个操作对共享文件都具有排他文件锁定.您可以想象这就像一块木头,只有一个人可以持有身份证,只有持有它的人才可以说话或做某事.

I'm glad you realise that its only a bad and temporary solution and you should optimize your code. Forget your tokens and stuff. The easiest and still efficient way is probably to have an exclusive file lock on a shared file per operation. You can imagine this like a piece of wood and only one can hold id and only who holds it is allowed to talk or do something.

<?php
    $fp = fopen("/tmp/only-one-bed-available.txt", "w+");

    if (flock($fp, LOCK_EX)) { // do an exclusive lock

         // do some very important critical stuff here which must not be interrupted:
         // sleeping.
         sleep(60);
         echo "I now slept 60 seconds";
        flock($fp, LOCK_UN); // release the lock
    } else {
        echo "Couldn't get the lock!";
    }

    fclose($fp);
?>

如果您并行执行此脚本10次,则将需要10分钟才能完成(因为只有一张床可用!),您将每60秒(大约)看到回声我现在睡着了……".

If you execute this script 10 times parallel, it will take 10 minutes to finish (because there is only one bed available!) and you will see the echo "I now slept..." every 60 seconds (approximately).

这将在全球范围内序列化此代码的所有执行. 这可能不是您想要的(您希望按每个用户使用,不是吗?) 我确定您有类似User-ID的名称,否则请使用外部IP地址,并且每个用户都有一个唯一的文件名:

This serializes ALL executions of this code GLOBALLY. This is probably not what you want (you want it on a per user basis, don't you?) I am sure you have something like User-IDs, otherwise use the foreign IP-Adress, and have a unique filename for each user:

<?php $fp = fopen("/tmp/lock-".$_SERVER["REMOTE_ADDR"].".txt", "w+"); ?>

您还可以为一组操作定义一个锁定文件名. Maby用户可以并行执行一些操作,但是仅此操作会引起问题?给它一个标签,并将其包含在锁定文件名中!

You can also define a lock file name for a group of operations. Maby the user can do some stuff parallel but only this operation makes problems? Give it a tag and include it int he lock filename!

这种做法确实还不错,可以使用!只有很少的方法可以更快地做到这一点(mysql内部,共享内存段,更高级别的序列化(例如负载均衡器)...

This practice really isn't that bad and can be used! There are only little ways to do it faster (mysql internals, shared memory segments, serialisation on a higher layer like load balancer...)

使用上面发布的解决方案,您可以在您的应用程序中完成此操作,这可能很好. 您也可以在Mysql中使用相同的方案: http://dev.mysql.com/doc /refman/5.0/en/miscellaneous-functions.html#function_get-lock

With the solution posted above you can do it in your application which is probably good. You can use the same scheme in Mysql as well: http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock

但是对于您的用例而言,PHP实现可能更容易,更好.

But the PHP implementation is probably easier and better for your use case.

如果您真的想在这里踢屁股,甚至可以找到更优雅的解决方案, 只需查看此功能 http://www.php.net/manual/zh/function.sem-get.php

If you really wanna kick ass there an even more elegant solution, just check out this function http://www.php.net/manual/en/function.sem-get.php

这篇关于防止Web应用程序中的并发交易的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆