4

ボタンとして機能し、サーバー上でいくつかのアクションをトリガーするさまざまなフォームと要素を備えたWebアプリケーション(ゲーム)があります。問題は、ユーザーがボタンのクリックが速すぎたり、2つのタブでWebサイトを開いてから、いくつかのアクションを同時に発行したりすると、アプリケーションを混乱させることがあるということです。MySQLトランザクション、Javascriptを防ぐダブルクリックなど、いくつかの基本的な保護がありますが、とにかく何かがスキップされることがあります。もちろん、最善の方法は、システムを混乱させない方法で、すべてのSQLトランザクションとサポート機能を再設計することです。このような混乱の1つの例は、2つの更新を同時に発行することです。1つのWeb要求はデータベース内の何かを変更しますが、2番目の要求は古いデータで引き続き動作するため、SQL更新は「影響を受ける行の数はゼロでした」を返します。最初のトランザクションでデータベース内のデータがすでに変更されているためです。明らかな解決策は、UPDATEの直前にもう一度データを読み取って、それでも更新が必要かどうかを確認することですが、それは、より多くのdouble SELECTクエリをどこにでも配置することを意味し、良い解決策ではありません-なぜdbから同じデータを2回読み取るのですか?また、隠しトークンを使用して、更新要求ごとにサーバー上で比較し、同じトークンIDを持つ操作を拒否することも検討しましたが、これは、コードの非常に多くの場所に触れ、システムに新しいバグを導入することを意味します。この1つの問題。良い解決策ではありません-なぜdbから同じデータを2回読み取るのですか?また、隠しトークンを使用して、更新要求ごとにサーバー上で比較し、同じトークンIDを持つ操作を拒否することも検討しましたが、これは、コードの非常に多くの場所に触れ、システムに新しいバグを導入することを意味します。この1つの問題。良い解決策ではありません-なぜdbから同じデータを2回読み取るのですか?また、隠しトークンを使用して、更新要求ごとにサーバー上で比較し、同じトークンIDを持つ操作を拒否することも検討しましたが、これは、コードの非常に多くの場所に触れ、システムに新しいバグを導入することを意味します。この1つの問題。

アクションフローのロジックは次のようになります。ユーザーが2つのリクエストを同時に発行した場合、2番目のリクエストは最初のリクエストが完了するまで待機する必要があります。ただし、アプリケーションには多くのリダイレクトがあることも考慮する必要があります(たとえば、ユーザーがページを更新するときに二重のPOSTを回避するために、POSTの後で)。したがって、ソリューションはユーザーのデッドロックを作成しないようにする必要があります。

したがって、問題は、すべてのユーザー操作をシーケンシャルにすることができる最も簡単なグローバル修正は何でしょうか?良い普遍的な解決策はありますか?

MySQLとPHPを使用しています。

4

3 に答える 3

2

これは悪い一時的な解決策にすぎず、コードを最適化する必要があることを認識していただければ幸いです。トークンなどは忘れてください。最も簡単で効率的な方法は、操作ごとに共有ファイルを排他的にロックすることです。これは木片のようなもので、ID を持つことができるのは 1 人だけで、ID を持っている人だけが話したり何かをしたりできると想像できます。

<?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 分かかります (利用できるベッドが 1 つしかないためです!)。60 秒ごとに (およそ) 「I now sleep...」というエコーが表示されます。

これにより、このコードのすべての実行が GLOBALLY でシリアル化されます。これはおそらくあなたが望むものではありません(ユーザーごとに必要ですよね?)ユーザーIDのようなものを持っていると確信しています。それ以外の場合は、外部のIPアドレスを使用し、ユーザーごとに一意のファイル名を持っています:

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

操作グループのロック ファイル名を定義することもできます。ユーザーはいくつかのことを並行して行うことができますが、この操作だけが問題になりますか? タグを付けて、ロック ファイル名に含めてください。

この練習は実際にはそれほど悪くなく、使用できます。高速化する方法はほとんどありません (mysql 内部、共有メモリ セグメント、ロード バランサーのような上位層でのシリアライゼーションなど)。

上記のソリューションを使用すると、アプリケーションでそれを行うことができます。これはおそらく良いことです。Mysql でも同じスキームを使用できます: http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock

しかし、PHP の実装はおそらくより簡単で、ユース ケースに適しています。

あなたが本当にもっとエレガントな解決策を探しているなら、この機能をチェックしてください http://www.php.net/manual/en/function.sem-get.php

于 2010-09-23T18:41:09.127 に答える
1

mysqlのget_lock関数を使用するのはどうですか?また、mysqlのトランザクションを確認する必要があるかもしれません。

于 2010-09-23T18:56:26.187 に答える
1

2 つのタブと同時アクション ? ユーザーの速度は?

とにかく : 1 つのタブのみを許可することは、すでに良い解決策です。

たった 1 つのタブで、JavaScript でアクションをスタックし、最後の呼び出しから戻り値を取得したら、新しいアクションを送信できます。

于 2010-09-23T16:29:32.517 に答える