Задать вопрос

Уровень изолированности REPEATABLE READ. Почему так происходит?

Кратко.
1. Обнаружил в проекте несогласованность данных
2. Сделал транзакции с уровнем изолированности REPEATABLE READ
3. Проблема не решилась

Теперь немного подробнее.
Классическая задача из главы по Транзакциям, есть магазин, куда пользователь выставляет свой товар. Контрагент покупает выставленный товар.
Почему-то возникает ситуация, при которой уже купленный товар, либо снятый с продажи, покупает еще один пользователь.

Почему так происходит?
Может ли, на это как то влиять, подготовленные запросы?
Использую расширение pdo

Отмена лота

<?php
// shop.php пример
...
$pdo->begintransaction();

if (isset($remove)) 
{
  $que = "SELECT `id` FROM `shops` WHERE `product_id` = ? AND `status` = ''";
  $pre = $pdo->prepare($que);
  $pre->execute([$product]);
  $res = $pre->fetchColumn();

  if ($res)
  {
    $que = "UPDATE `shops` SET `status` = 'Отозвано' WHERE `id` = $res";
    $pdo->exec($que);
  }
}

$pdo->commit();
...
?>


Покупка лота

<?php
// buy.php пример
...
$pdo->begintransaction();
if (isset($product))
{
  $que = "SELECT `id` FROM `shops` WHERE `product_id` = ? AND `status` = ''";
  $pre = $pdo->prepare($que);
  $pre->execute([$product]);
  $res = $pre->fetchColumn();

  if ($res)
  {
    $que = "UPDATE `shops` SET `status` = 'Продано' WHERE `id` = $res";
    $pdo->exec($que);
  }
}
$pdo->commit();
...
?>


Может пригодится
Сервер: Localhost via UNIX socket
Версия сервера: 8.0.23-0ubuntu0.20.04.1 - (Ubuntu)
Кодировка сервера: UTF-8 Unicode (utf8mb4)

nginx/1.18.0
Версия клиента базы данных: libmysql - mysqlnd 7.4.3
Версия PHP: 7.4.3
  • Вопрос задан
  • 98 просмотров
Подписаться 2 Простой Комментировать
Решения вопроса 1
@RizyaRU Автор вопроса
Как оказалось SELECT вовсе не блокирует запись, и она может быть изменена другой транзакцией.

Решение мне подсказали следующее:
1. Обновить запись с нужным ID и статусом
2. Проверить affected rows

Как то так:
<?php
// buy.php пример
...
$pdo->begintransaction();
if (isset($product))
{
	$que = "UPDATE `shops` SET `status` = 'Продано' WHERE `id` = ? AND `status` = ''";
	$pre = $pdo->prepare($que);
    $pre->execute([$product]);

	if ($pre->rowCount())
	{
		// Товар куплен
	}

	else 
	{
		// Статус был изменен другой транзакцией, либо това не найден
	}
}
$pdo->commit();
...
?>
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@MechanID
Админ хостинг провайдера
REPEATABLE READ и так в mysql по умолчанию так что у вас скорее всего ничего не поменялось
возможно вам стоит использовать READ COMMITTED
без кода самих транзакций сказать сложно почему так происходит
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы