2013-02-22 23 views
22

私はいくつかの質問... 例があります。ユーザーは自分のUSDPHP/MySQLの - 2つの要求を防ぐために、どのように*更新

  1. のために何かを購入する彼のUSDバランス
  2. が彼からUSDを控除チェックアカウント
  3. 注文ください - 、>オーダーキュー
  4. ユーザーが自分のアイテムを取得し、他の一つは、彼USD

が言うことができます取得ユーザーは同じ秒で5つのリクエストを行います(非常に速い)。 5つのリクエストが実行されている可能性があります(起こります)。 彼は1つのリクエストからしか買うことができません。今度は というリクエストは非常に速く、スクリプトは残高を確認しますが、それほど速くはないので、自分のアカウントからお金を差し引くことになります。したがって、リクエストは2回に渡ります! それを解決するには?私は、プロセスを開始する前に、私は、MySQLでLOCKを使用

  1. IS_FREE_LOCK - チェックはこのユーザーのロックが存在しない場合は - > 2.
  2. はGET_LOCK -
  3. が作るロックを設定し、注文/トランザクション
  4. RELEASE_LOCK - ロックを解除

しかし、これは実際に動作しません。別の方法がありますか?

function lock($id) { 
    mysql_query("SELECT GET_LOCK('$id', 60) AS 'GetLock'"); 
} 

function is_free($id) { 
    $query = mysql_query("SELECT IS_FREE_LOCK('$id') AS 'free'"); 
    $row = mysql_fetch_assoc($query); 
    if($row['free']) { 
    return true; 
    } else { 
    return false; 
    } 
} 

function release_lock($id) { 
    mysql_query("SELECT RELEASE_LOCK('$id')"); 
} 

function account_balance($id) { 
    $stmt = $db->prepare("SELECT USD FROM bitcoin_user_n WHERE id = ?"); 
    $stmt->execute(array($id)); 
    $row = $stmt->fetch(PDO::FETCH_ASSOC); 

    return $row['USD']; 
} 

if(is_free(get_user_id())) { 
    lock(get_user_id()); 
    if(account_balance(get_user_id()) < str2num($_POST['amount'])) { 
    echo "error, not enough money"; 
    } else { 
    $stmt = $db->prepare("UPDATE user SET USD = USD - ? WHERE id = ?"); 
    $stmt->execute(array(str2num($_POST['amount']), get_user_id())); 
    $stmt = $db->prepare("INSERT INTO offer (user_id, type, price, amount) VALUES (?, ?, ?, ?)"); 
    $stmt->execute(array(get_user_id(), 2, str2num($_POST['amount']), 0)); 
} 

更新 はUPDATE

$db->beginTransaction(); 
$stmt = $db->prepare("SELECT value, id2 FROM test WHERE id = ? FOR UPDATE"); 
$stmt->execute(array(1)); 
$row = $stmt->fetch(PDO::FETCH_ASSOC); 
if($row['value'] > 1) { 
    sleep(5); 
    $stmt = $db->prepare('UPDATE test SET value = value - 5 WHERE id = 1'); 
    $stmt->execute(); 
    $stmt = $db->prepare('UPDATE test SET value = value + 5 WHERE id = 2'); 
    $stmt->execute(); 
    echo "did have enough money"; 
} else { 
    echo "no money"; 
} 
$db->commit(); 
+27

[**新しいコード**]でmysql_ *関数を使用しないでください(http://bit.ly/phpmsql)。彼らはもはや維持されておらず(正式に非難されている)(http://j.mp/XqV7Lp)。 [** red box **](http://j.mp/Te9zIL)を参照してください。代わりに[* prepared statements *](http://j.mp/T9hLWi)について学び、[PDO](http://php.net/pdo)または[MySQLi](http://php.net/ mysqli) - [この記事では、(http://j.mp/QEx8IB)はあなたがどちらを決定するのに役立ちます。 – Kermit

+3

誰かがこれが何を意味するかを知っている: "ユーザーは彼のアイテムになり、もう1人は彼のUSDになる"?彼らは「つくられる」より「つかむ」という意味ですか? – ESRogs

+9

取引についても*基本*を知らなくても、オンライン取引サイトを操作することは可能ですか? – Massimo

答えて

25

まず、トランザクションを使用する必要がありますが、それだけでは十分ではありません。取引では、SELECT FOR UPDATEを使用できます。

基本的には、"選択しているレコードを更新するつもりです"なので、UPDATEと同じロックが設定されています。しかし、これはオートコミットをオフにしてトランザクション内で発生しなければならないことを忘れないでください。

+0

が例になりました!私のために働く、ありがとう:) – DjangoSi

6

使用TRANSACTION ... FOR SELECTとトランザクション機能をテストし、それが失敗した場合は、ロールバックすることができます。

たとえば、現在の残高が$ 20であるとします。

Connection A    Connection B 
======================= =========================== 
BEGIN TRANSACTION   
          BEGIN TRANSACTION 
SELECT AccountBalance 
          SELECT AccountBalance 
--returns $20 
--sufficient balance, 
--proceed with purchase 
          --returns $20 
          --sufficient balance, 
          --proceed with purchase 

          --update acquires exclusive lock 
          UPDATE SET AccountBalance 
           = AccountBalance - 20 
--update blocked due 
UPDATE SET AccountBalance 
    = AccountBalance - 20 

          --order complete 
          COMMIT TRANSACTION 

--update proceeds 

--database triggers 
--constraint violation 
--"AccountBalance >= 0" 

ROLLBACK TRANSACTION 
+1

したがって、ユーザーが2つの要求を設定した場合、トランザクションはキューに入れられますか? 私はSTART TRANSACTIONを実行すると、コミットまたはロールバックのように、他のリクエストが長くキューに入れられます。 – DjangoSi

+0

2つの選択肢がまだ十分なUSDを返すので、これはopの問題を解決するとは思わない。 –

+0

db内のクエリは同時に実行されません。トランザクション2がなければ、選択はキューに入れられ、次に更新されます。トランザクションでは、次のキューステートメントにジャンプする前に、SQLステートメントのブロックを同時に実行する必要があります。トランザクションでは2番目の選択は失敗します。 – Oden

5

これは私が何年も前にそれを行うために使用される方法です。..

results = query("UPDATE table SET value=value-5 WHERE value>=5 AND ID=1") 
if (results == 1) YEY! 

は(これはまだ信頼できる方法である?)

1

あなたはSERIALIZABLE分離レベルでトランザクションを使用する必要があります。

0

MySQLの更新用にデータリビジョンを使用する必要があります。

関連する問題