PHP / MySQL - jak zapobiec dwóm żądaniom * Aktualizacja

Mam pytanie ... przykład: użytkownik kupi coś za swoje USD

Sprawdź saldo w USDOdejmij USD ze swojego kontaZłóż zamówienie -> kolejka zamówieńużytkownik otrzymuje swój przedmiot, a drugi otrzymuje swoje USD

Powiedzmy, że użytkownicy wykonują 5 żądań w tej samej sekundzie (bardzo szybko). Tak więc możliwe jest (i zdarza się), że uruchomionych jest 5 żądań. Ma tylko pieniądze na zakup tylko od 1 wniosku. Teraz żądania są tak szybkie, że skrypt sprawdza jego równowagę, ale nie jest tak szybki, że odejmuje pieniądze ze swojego konta. Żądania przejdą dwa razy! Jak to rozwiązać?

Używam LOCK w mysql przed rozpoczęciem procesu:

IS_FREE_LOCK - sprawdź, czy istnieje blokada dla tego użytkownika, jeśli nie -> 2.GET_LOCK - ustawia blokadęzłożyć zamówienie / transakcjęRELEASE_LOCK - zwalnia blokadę

Ale to naprawdę nie działa. Czy jest inny sposób?

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));
}

Aktualizacja Przetestowano funkcję transakcji za pomocą SELECT ... FOR 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();

questionAnswers(3)

yourAnswerToTheQuestion