Блокировка уровня строки в Mysql
У меня есть 5 строк в таблице (от 1 до 5). Я хочу заблокировать строку 2 для какого-то обновления, и в то же время, если кто-то пытается обновить строку 4, то он должен иметь возможность обновить.
Я пытаюсь это с кодом ниже, но я чувствую, что его блокировка размещения на уровне таблицы, а не на уровне строки.
------ сессия 1
START TRANSACTION;
SELECT * FROM test WHERE t=1 FOR UPDATE;
UPDATE test SET NAME='irfandd' WHERE t=2;
COMMIT;
----- сессия 2 (которая блокируется)
START TRANSACTION;
UPDATE test SET NAME='irfandd' WHERE t=4;
COMMIT;
2 ответа
Вместо FOR UPDATE
использование LOCK IN SHARE MODE
, FOR UPDATE
предотвращает чтение строк другими транзакциями. LOCK IN SHARE MODE
позволяет читать, но предотвращает обновление.
Ссылка: руководство по MySQL
------ сессия 1
START TRANSACTION;
SELECT * FROM test WHERE t=1 LOCK IN SHARE MODE;
UPDATE test SET NAME='irfandd' WHERE t=2;
COMMIT;
----- сессия 2 (которая больше не блокируется:))
START TRANSACTION;
UPDATE test SET NAME='irfandd' WHERE t=4;
COMMIT;
Обновить:
Понимая, что таблица не имеет индекса на t
У меня есть следующее объяснение:
Во-первых, транзакция T1 блокирует строку 1 в SELECT * FROM test WHERE t=1 FOR UPDATE
Затем транзакция T2 пытается выполнить UPDATE test SET NAME='irfandd' WHERE t=4
, Чтобы выяснить, какие строки затронуты, необходимо просканировать все строки, включая строку 1. Но это заблокировано, поэтому T2 должен дождаться окончания T1. Если есть какой-либо индекс, WHERE t=4
можно использовать индекс, чтобы решить, содержит ли строка 1 t=4
или нет, так что ждать не нужно.
Вариант 1: добавить индекс на test.t
так что ваше обновление может использовать его.
Вариант 2: использовать LOCK IN SHARE MODE
, который предназначен только для установки блокировки чтения. К сожалению, эта опция создает тупик. Интересно, что транзакция T2 выполняется (обновление строки 4), а T1 - неудача (обновление строки 2). Кажется, что T1 также блокирует чтение строки 4, и, поскольку T2 модифицирует ее, T1 завершается сбоем из-за уровня изоляции транзакции ( по умолчанию REPEATABLE READ). Окончательное решение будет играть с уровнями изоляции транзакций, используя READ UNCOMMITTED
или же READ COMMITTED
уровни транзакций.
Самый простой вариант 1, ИМХО, но это зависит от ваших возможностей.
Я нашел ниже вариант более подходящим, я генерирую 40000 номеров из одновременной сессии в одно и то же время. Я не нашел дубликатов. Без приведенной ниже команды я сгенерировал 10000 номеров и нашел 5 дубликатов.
НАЧАТЬ СДЕЛКУ
ВЫБЕРИТЕ * ИЗ ТЕСТА, ГДЕ t=1 ДЛЯ ОБНОВЛЕНИЯ;
ОБНОВЛЕНИЕ теста SET NAME = 'irfandd' WHERE t = 2;
COMMIT;