Как обработать тонны одновременного запроса UPDATE на одну запись в базе данных MySQL и определить, кто пришел первым - PullRequest
3 голосов
/ 28 июля 2011

Я разработал сайт, показывающий книги в аренду, используя php и mySQL. Когда кто-то нажимает на книгу, отображается информационная страница с таймером обратного отсчета, и у пользователя есть 10 секунд, чтобы решить, хочет ли он одолжить ее. Если время истекло, пользователь должен вернуться и снова щелкнуть книгу, чтобы отобразить «страницу заимствования». Когда пользователь нажимает на книгу, PHP сначала проверяет, просматривает ли кто-нибудь книгу в данный момент. Если его никто не просматривает, PHP отобразит «страницу заимствования», и пользователь получит 10сек эксклюзивности для книги. В течение этих 10 секунд другие пользователи не смогут открыть страницу или просмотреть «кнопку заимствования». Все работает и работает, за исключением того, что я не знаю, как это сделать правильно, поэтому только один пользователь может сначала получить 10-секундную эксклюзивность. Моя логика такова:

Таблица MySQL:

Книга (book_ID, title, current_user, expire_time)

а. Когда пользователь нажимает на книгу: PHP Проверьте, если expire_time

б. Если истина (предыдущее время просмотра истекло), установите в поле current_user идентификатор пользователя запрашивающей стороны и установите время истечения сейчас + 10 секунд:

UPDATE Book SET current_user = '$ userID', expire_time = ADDTIME (NOW (), '00: 00: 10 ')

с. Если false (все еще в пределах времени просмотра), дополнительно проверьте, равно ли поле current_user идентификатору пользователя запрашивающей стороны.

д. если true, отобразить «страницу заимствования».

е. если false, выведите сообщение об ошибке «кто-то еще просматривает книгу. Пожалуйста, вернитесь через 10 секунд»

Я тестировал одновременное нажатие на книгу на двух компьютерах. В большинстве случаев только один компьютер может получить доступ к странице и получить эксклюзивность в течение 10 секунд. Однако было одно время, когда оба компьютера успешно открывали страницу «заимствовать книгу» одновременно. Это определенно не то, что я хочу. Представьте, что существует 100 веб-запросов с просьбой открыть одну и ту же книгу, и половина из них проходит, пока есть только одна книга, доступная для аренды.

Книги заимствованы только для иллюстрации. Что я хочу знать, так это то, что когда на сервер MySQL одновременно поступает множество запросов, как получить или выбрать первый запрос, а остальное приостановить?

Ответы [ 2 ]

1 голос
/ 28 июля 2011

Я бы инкапсулировал блокировку в классе, тогда вы можете сделать это:

$book = new Book($book_id);
$success = $book->getLock($user_id);
if (!$success)
  echo 'Lock failed';
else
  echo 'You got 10 seconds to buy, sucka!';


    class book {
        private $id = false;
        private $initialized = false; 
        public function __construct($id = false) {
            if (!is_numeric($id) || $id === false)
                return false;
                    $this->id = $id;
            $this->initialized = true;
            return;
        }
        public function getLock($userID = false) {
            if ($this->initialized !== true)
                return false;

            if (!is_numeric($userID) || $userID === false)
                return false;

           // check for a current lock
           $sql = 'SELECT current_user, expire_time FROM Book WHERE id = '.$this->id;

           // do whatever you do, get back the row
           $avail = do_query($sql);

          // the book is already locked
          if ($avail['current_user'] > 0 && strtotime($avail['expire_time']) > time())
        return false;

            // assert the lock
            $sql = 'UPDATE Book SET current_user='.$userID.', expire_time=ADDTIME(NOW(), "00:00:10") WHERE id = '.$this->id;

            // use whatever method you use for db access
            do_query($sql);

            // verify the lock
            $sql = 'SELECT current_user FROM Book WHERE id = '.$this->id;

            // do whatever you do for db access, get the id field
            $result = do_query($sql);

            // if these match, lock was a success
            if ($result == $userID)
                return true;

            return false;
        }
        public function releaseLock($userID=false) {
            if ($this->initialized !== true)
                return false;

            if (!is_numeric($userID) || $userID === false)
                return false;

            // verify the lock is in the specified user's id
            $sql = 'SELECT current_user FROM Book WHERE id = '.$this->id;

            // do whatever you do for db access, get the id field
            $result = do_query($sql);

            // if these match, lock is actively assigned to this user
            if ($result == $userID)
                return false;

            // release it
            $sql = 'UPDATE Book SET current_user=NULL, expire_time=NULL WHERE id = '.$this->id;
            do_query($sql);
            return true;
        }

    }
0 голосов
/ 28 июля 2011

Вам необходимо добавить проверку для "книги, которая уже занята" в вашу инструкцию UPDATE.Т.е. в вашем предложении WHERE помимо ID также проверьте, что expire_time закончилось.После ОБНОВЛЕНИЯ необходимо прочитать запись обратно, чтобы проверить, успешно ли вы ее «зарезервировали».Таким образом, проходит только одно из ваших обновлений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...