PHP/MySQL-कैसे दो अनुरोधों को रोकने के लिए*अपडेट करें




transactions (4)

इस तरह मैं कई साल पहले ऐसा करने के लिए करता था ..

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

(क्या यह अभी भी एक विश्वसनीय तरीका है?)

मेरे पास कुछ सवाल है ... उदाहरण: उपयोगकर्ता अपने अमरीकी डालर के लिए कुछ खरीद लेगा

  1. अपने यूएसडी बैलेंस की जांच करें
  2. अपने खाते से अमरीकी डालर घटाएं
  3. एक ऑर्डर करें -> ऑर्डर कतार
  4. उपयोगकर्ता को अपना आइटम मिलता है और दूसरे को अपना अमरीकी डालर मिलता है

कहते हैं, उपयोगकर्ता एक ही दूसरे (बहुत तेज़) में 5 अनुरोध बनाते हैं तो यह संभव है (और हो सकता है) कि 5 अनुरोध चल रहे हैं। उसके पास केवल 1 अनुरोध से ही खरीदने के लिए पैसे हैं अब अनुरोध इतनी तेज़ हैं, कि स्क्रिप्ट उसकी शेष राशि की जांच करता है, लेकिन इतनी तेज़ नहीं है, कि वह अपने खाते से पैसे घटाता है तो अनुरोध दो बार पारित होंगे! इसे कैसे हल करें?

प्रक्रिया शुरू करने से पहले मैं 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));
}

अद्यतन चयन के साथ लेनदेन समारोह का परीक्षण ... अद्यतन के लिए

$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();

सबसे पहले, आपको लेनदेन का उपयोग करना पड़ता है, लेकिन यह पर्याप्त नहीं है आपके लेनदेन में, आप SELECT FOR UPDATE उपयोग कर सकते हैं

यह मूल रूप से कह रहा है, "मैं उन अभिलेखों को अपडेट करने जा रहा हूं जो मैं चुन रहा हूं" , इसलिए यह एक ही ताले सेट कर रहा है जो एक UPDATE सेट होगा। लेकिन याद रखें कि ऑटोकोमाइट बंद होने के साथ लेनदेन के अंदर ऐसा होना चाहिए।


आपको सिरिअलजेल अलगाव स्तर पर लेनदेन का उपयोग करना होगा।


आपको MySQL अद्यतन के लिए डेटा संशोधन का उपयोग करने की आवश्यकता है।







transactions