В существующих ответах много правильных слов, но никто, похоже, не дал четкого ответа.Я попробую.
Как вы уже видели в документации по LOCK TABLES , его нельзя использовать для этой цели, поскольку для блокировки READ
:
Сеанс, который удерживает блокировку, может читать таблицу (но не записывать ее).
и для блокировки WRITE
:
Только сеанс, которыйдержит замок может получить доступ к столу.Ни один другой сеанс не сможет получить к нему доступ, пока не будет снята блокировка.
Этого едва ли можно достичь с помощью произвольной таблицы обработчиков, но этого можно достичь с помощью транзакционного обработчика, то есть InnoDB.
Давайте подумаем, что означает, что один сеанс поддерживает постоянную блокировку записи в таблице, а другие таблицы могут считывать данные из таблицы с точки зрения транзакций.Это означает, что у нас есть открытая долгоживущая транзакция (пусть это будет W
транзакция), которая блокирует таблицу для изменений, а другие транзакции (в других сеансах) могут считывать данные, которые уже изменены, но еще не зафиксированы.С точки зрения уровней изоляции это означает, что мы должны установить уровень изоляции по умолчанию на READ-UNCOMMITTED
, чтобы нам не приходилось изменять уровень изоляции для каждого нового сеанса:
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Но наша транзакцияW
, следует использовать более высокий уровень изоляции, иначе мы не сможем применить блокировку к нашей таблице.READ-COMMITTED
недостаточно силен, но REPEATABLE-READ
это именно то, что мы хотим.То есть для запуска транзакции W
мы должны установить уровень транзакции для текущего сеанса:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Теперь, как заблокировать всю таблицу.Давайте создадим таблицу:
CREATE TABLE t (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
val VARCHAR(45) NOT NULL,
PRIMARY KEY (id)
) ENGINE = InnoDB;
LOCK IN SHARE MODE - это не то, что нам нужно:
Если какая-либо из этих строк [которые читаются] былиизмененный другой транзакцией, которая еще не зафиксирована, ваш запрос ждет, пока эта транзакция не завершится, и затем использует последние значения.
LOCK FOR UPDATE , кажется, делает то, что нам нужно:
SELECT ... FOR UPDATE блокирует строки и все связанные с ними записи индекса.
Теперь все, что нам нужно, это заблокировать строки.Самое простое, что мы можем сделать - это заблокировать первичный ключ.COUNT(*)
выполняет полное сканирование индекса для InnoDB (поскольку InnoDB не знает точное количество строк).
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT COUNT(*) FROM t FOR UPDATE;
INSERT INTO t VALUES (NULL, '');
Теперь вы можете открывать другие сеансы и пытаться читать данные из таблицы и пытаться добавитьили измените существующие данные из этих сеансов.
Проблема, однако, заключается в том, что вы должны зафиксировать изменения в W
, и как только вы фиксируете транзакцию, блокировка снимается и все ожидающие вставки или обновленияприменяются также, даже если вы фиксируете это с помощью:
COMMIT AND CHAIN; SELECT COUNT(*) FROM ti FOR UPDATE;
Мораль этой истории в том, что гораздо проще иметь две учетные записи MySQL: a) запись учетной записи, которая имеет INSERT, UPDATE и DELETE GRANT разрешения , и б) чтение учетной записи, которая не имеет.