MySQL блокировка строк через php - PullRequest
8 голосов
/ 22 марта 2012

Я помогаю другу с помощью веб-формы, которая предназначена для его бизнеса.Я пытаюсь подготовить его к работе с несколькими пользователями.Я настроил его так, чтобы непосредственно перед тем, как запись отображалась для редактирования, я блокировал запись следующим кодом:

$query = "START TRANSACTION;";
mysql_query($query);
$query = "SELECT field FROM table WHERE ID = \"$value\" FOR UPDATE;";
mysql_query($query);

(хорошо, это значительно упрощено, но в этом суть mysql)

Кажется, не работает.Однако, когда я перехожу к mysql из командной строки, входя в систему с тем же пользователем и выполняю

START TRANSACTION;
SELECT field FROM table WHERE ID = "40" FOR UPDATE;

, я могу эффективно заблокировать веб-форму от доступа к записи «40» и получить предупреждение о превышении времени ожидания.1009 *

Я пытался использовать BEGIN вместо START TRANSACTION.Я попытался сначала выполнить SET AUTOCOMMIT = 0 и запустить транзакцию после блокировки, но я не могу заблокировать строку из кода PHP.Поскольку я могу заблокировать строку из командной строки, я не думаю, что есть проблема с настройкой базы данных.Я действительно надеюсь, что есть кое-что простое, что я упустил в своем чтении.

К вашему сведению, я работаю на XAMPP версии 1.7.3, которая имеет Apache 2.2.14, MySQL 5.1.41 и PHP 5.3.1.

Заранее спасибо.Это моя первая публикация, но в прошлом я почерпнул много знаний из этого сайта.

Ответы [ 5 ]

12 голосов
/ 22 марта 2012

Проблема не в синтаксисе вашего кода, а в том, как вы пытаетесь его использовать.

непосредственно перед отображением записи для редактирования. Я блокирую запись с помощью следующего кода

Исходя из этого, я предполагаю, что вы выбираете и «блокируете» строку, затем отображаете эту страницу редактирования для своего пользователя, затем, когда они отправляют изменения, она сохраняет и «разблокирует» таблицу.Здесь кроется основная проблема.Когда ваша страница загружена, PHP завершает работу и закрывает соединение MySQL.Когда это происходит, все блокировки немедленно снимаются.Вот почему консоль ведет себя иначе, чем ваш PHP.Эквивалентом в консоли будет выход из программы.

Вы не можете заблокировать строки таблицы для редактирования на длительный период.Это не их дизайн.Если вы хотите заблокировать запись для редактирования, вам нужно отследить эти блокировки в другой таблице.Создайте новую таблицу с именем «edit_locks» и сохраните заблокированный идентификатор записи, редактирование идентификатора пользователя и время его блокировки.Если вы хотите открыть запись для редактирования, заблокируйте всю таблицу edit_locks и сделайте запрос, чтобы узнать, заблокирована ли запись кем-то другим.Если это не так, вставьте запись о блокировке, если она есть, затем отобразите заблокированную ошибку.Когда пользователь сохраняет или отменяет, удалите запись блокировки из edit_locks.Если вы хотите упростить задачу, просто заблокируйте эту таблицу в любое время, когда ваша программа захочет ее использовать.Это поможет вам избежать состояния гонки.

Существует еще один сценарий, который может вызвать проблему.Если пользователь открывает запись для редактирования, а затем закрывает браузер без сохранения или отмены, блокировка редактирования останется там навсегда.Вот почему я сказал хранить время, когда он был заблокирован.Сам редактор должен делать AJAX-вызов каждые 2 минуты или около того, чтобы сказать: «Мне все еще нужен замок!».Когда программа PHP получает этот запрос «relock», она должна выполнить поиск блокировки, а затем обновить текущую метку времени.Таким образом, отметка времени на блокировке всегда обновляется в течение 2 минут.Вам также необходимо создать другую программу для удаления старых устаревших блокировок.Это должно выполняться в работе cron каждые несколько минут.Он должен найти все блокировки с отметкой времени старше 5 минут и удалить их.Если временная метка старше этой, то, очевидно, редактор был близок к тому, как или временная метка была бы обновлена ​​в течение 2 минут.

Как упоминали некоторые другие, вы должны попытаться использовать MySQLi .Он расшифровывается как «MySQL Improved» и является заменой старого интерфейса.

1 голос
/ 11 октября 2015

Это старая дискуссия, но, возможно, люди все еще следуют ей.Я использую метод, аналогичный JMack, но включаю информацию о блокировке в таблицу, которую я хочу заблокировать строкой.Мои новые столбцы - LockTime и LockedBy.Чтобы попытаться заблокировать, я делаю:

UPDATE table
SET LockTime='$now',LockedBy='$Userid'
WHERE Key='$value' AND (LockTime IS NULL OR LockTime<'$past' OR LockedBy='$Userid')

($ прошлый 4 минуты назад)

Если это не удастся, у кого-то еще есть блокировка.Я могу явно разблокировать, как указано ниже, или позволить моей блокировке истечь:

UPDATE table
SET LockTime=NULL,LockedBy=''
WHERE Key='$value' AND LockedBy='$Userid'

Задание cron может удалить старые блокировки, но в этом нет особой необходимости.

0 голосов
/ 22 марта 2012

проблема в командах mysql. Вы можете использовать mysqli для этого

http://nz.php.net/manual/en/class.mysqli.php

или PDO. Описано здесь:

Примеры транзакций PHP + MySQL

НТН

0 голосов
/ 22 марта 2012

Вы не должны использовать API-интерфейс mysql, потому что легко допускать ошибки, которые допускают sql-инъекции и тому подобное, а также не хватает некоторой функциональности. Я подозреваю, что транзакция является одним из них, потому что если я не ошибаюсь, каждый запрос отправляется «сам по себе», а не в более широком контексте.

Однако решение состоит в том, чтобы использовать некоторые другие API, я предпочитаю mysqli, потому что он очень похож на mysql и широко поддерживается. Вы также можете легко переписать свой код, чтобы использовать mysqli.

Для функциональности транзакции установите auto-commit в false и зафиксируйте себя, когда захотите. Это так же, как запуск и остановка транзакций.

Для справки смотрите:

http://www.php.net/manual/en/mysqli.autocommit.php

http://www.php.net/manual/en/mysqli.commit.php

0 голосов
/ 22 марта 2012

Используйте для этого PDO (и все операции с базой данных):

$value = 40;

try {
    $dbh = new PDO("mysql:host=localhost;dbname=dbname", 'username', 'password');
} catch (PDOException $e) {
    die('Could not connect to database.');
}

$dbh->beginTransaction();

try {
    $stm = $dbh->prepare("SELECT field FROM table WHERE ID = ? FOR UPDATE");
    $stm->execute(array($value));
    $dbh->commit();
} catch (PDOException $e) {
    $dbh->rollBack();
}

Если вам нужно использовать устаревшие функции mysql_ *, вы можете что-то вроде:

mysql_query('SET AUTOCOMMIT=0');
mysql_query('START TRANSACTION');
mysql_query($sql);
mysql_query('SET AUTOCOMMIT=1');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...