Блокирует ли мой запрос строку для следующего использования? - PullRequest
2 голосов
/ 04 апреля 2019

Это моя структура кода в php:

function myfunction(){

    try{    
        // begin Transaction

        // get last row by this query:
        // SELECT id FROM tableName WHERE t_date+INTERVAL 1 MINUTE< NOW() LIMIT 1
        // store the returned id inside $id if exists, $id=0 otherwise (out of id for now)

        // update the row to the current time to show it is fetched now by this query:
        // UPDATE tableNAME SET t_date=NOW() WHERE id=$id

        // end of Transaction
        // commit   

    }catch (PDOException $e){
        // failed, rollback
        $id = 0;
    }

    return $id
}

Как видите, моя функция:

  1. использовала транзакцию для блокировки строки.
  2. возвращает уникальный идентификатор строки, который не был возвращен за последнюю 1 минуту.
  3. запрошенный пользователь владеет этим идентификатором в течение 1 минуты.
  4. другие пользователи могут иметь этот идентификатор через 1 минуту.

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

Вопрос

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

Если два пользователя (или более) открывают страницу одновременно и по следующим причинам:

  • они имеют различное соединение PDO и
  • MySQL может запускать два или более запросов одновременно в разных потоках и
  • , так как я изолирую запрос на выборку внутри транзакции,

Всегда ли я получаю уникальный результат, как я ожидал?

Я прочитал @symcbean ответ о MySQL очереди запросов? и он упомянул:

Очень маловероятно, что 2 запроса могут прийти одновременно - но не невозможно

Помогает ли моя структура кода избежать параллельных запросов?

1 Ответ

2 голосов
/ 04 апреля 2019

Учитывая сценарий, когда два запроса поступают одновременно, все транзакции гарантируют поведение ACID для операций базы данных каждого потока.Это не означает, что два набора выбора / обновления не могут происходить одновременно;на самом деле это может произойти.

Один из способов справиться с этим - использовать SELECT ... FOR UPDATE:

SELECT id FROM tableName WHERE t_date + INTERVAL 1 MINUTE < NOW() LIMIT 1 FOR UPDATE;
UPDATE tableNAME SET t_date=NOW() WHERE id = $id;

Предложение FOR UPDATE сообщит MySQL:заблокировать единственную совпадающую строку id, чтобы даже если появился другой поток, он не смог заблокировать и, следовательно, не смог обновить.

Редактировать:

Как заметил @tadman, вы можете даже использовать один запрос здесь:

UPDATE tableNAME
SET t_date = NOW()
WHERE id = (SELECT id FROM tableName WHERE t_date + INTERVAL 1 MINUTE < NOW() LIMIT 1);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...