Блокировка таблицы MySQL: держатель читает и пишет, другие сессии только чтение? - PullRequest
8 голосов
/ 25 октября 2011

Можно ли заблокировать таблицу так, чтобы ее владелец мог читать и писать, а другие сеансы могут только читать?

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

Возможно, снижение производительности при реализации этого сценария было бы слишкомвысокий

Ответы [ 4 ]

3 голосов
/ 13 декабря 2012

В существующих ответах много правильных слов, но никто, похоже, не дал четкого ответа.Я попробую.

Как вы уже видели в документации по 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 разрешения , и б) чтение учетной записи, которая не имеет.

3 голосов
/ 25 октября 2011

Посмотрите на LOCK IN SHARE MODE .

Это позволит вам установить неблокирующие блокировки чтения.

Но помните, это может привести к взаимоблокировкам!Убедитесь, что вы в порядке с процессами, имеющими устаревшую информацию.

0 голосов
/ 13 декабря 2012

Существует SELECT ... FOR UPDATE, который блокирует строки для других вызывающих абонентов, которые делают SELECT ... FOR UPDATE, но не блокирует его для тех, кто делает только SELECT. UPDATE s также будет ожидать блокировки.

Это полезно, когда вы хотите получить значение, а затем отправить обновление обратно, чтобы никто не изменил значение, а вы не заметили. Будьте осторожны, добавив слишком много из них, вы попадете в тупик.

0 голосов
/ 25 октября 2011

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

...