Как намеренно заблокировать строку MySQL, чтобы даже SELECT возвращал ошибку?

Я пытаюсь использовать блокировку строк MySQL, чтобы в основном эмулировать MuteEx в строке. Допустим, в моей таблице есть 2 столбца, идентификатор и текстовое поле, а также три записи (1, а) (2, б) и (3, в). ВЫБЕРИТЕ * ИЗ таблицы; вернет эти результаты. Я могу заблокировать определенную строку обычным способом.

START TRANSACTION;
BEGIN;
SELECT * FROM table WHERE id = '2' FOR UPDATE;

Однако, если бы из второго соединения я должен был выбрать SELECT * из таблицы. Он вернет все 3 результата. Есть ли способ блокировки уровня строки, чтобы в основном предотвратить любой SELECT от просмотра/использования заблокированной строки? В основном я пытаюсь запретить кому-либо использовать строку, которая в настоящее время используется/манипулируется, или даже просматривать строку, поскольку ее данные (поскольку она используется/манипулируется) нельзя доверять точности во время SELECT .


person bahhumbug    schedule 12.01.2010    source источник
comment
Возможно, это не то, что вы (или я) хотите услышать, но похоже, что если второй селектор также использует SELECT ... FOR UPDATE, он будет ждать, пока первоначальная блокировка не будет снята.   -  person Timo    schedule 05.06.2019


Ответы (4)


Если вы установите уровень изоляции транзакции SERIALIZABLE, InnoDB будет неявно добавлять LOCK IN SHARE MODE ко всем операторам SELECT.

Этот режим конфликтует с блокировками, установленными SELECT FOR UPDATE, и SELECT заблокируются.

Однако обратите внимание, что InnoDB может заблокировать больше строк, чем удовлетворяет условию WHERE. Это связано с тем, что блокируются все отсканированные строки, а не только совпадающие.

Скажем, у вас есть индекс col1 и этот запрос:

SELECT  *
FROM    mytable
WHERE   col1 = 1
        AND col2 = 2
FOR UPDATE

использует этот индекс.

Это заблокирует все записи с col1 = 1, даже с col2 <> 2.

person Quassnoi    schedule 12.01.2010

Вам нужен LOCK IN SHARE MODE. Использование его с SELECT гарантирует, что никто другой не блокирует строки с помощью FOR UPDATE.

e.g.

Клиент А делает SELECT * FROM table WHERE type=2 FOR UPDATE

Клиент B делает SELECT * FROM table LOCK IN SHARE MODE и зависает здесь

Клиент А пишет/ВСТАВЛЯЕТ/ОБНОВЛЯЕТ что-то, затем делает COMMIT

Клиент B теперь размораживается и возобновляет обработку

person servermanfail    schedule 22.02.2011

На самом деле данным строк можно доверять, даже когда вы манипулируете ими.

Если вы начнете транзакцию из одного соединения, другие соединения не увидят никаких ваших изменений, пока вы не зафиксируете транзакцию.

person Timo Stamm    schedule 12.01.2010

Я не знаю, есть ли для этого специальный блокирующий механизм, но мой первый инстикт состоял бы в том, чтобы дать таблице столбец состояния (например, locked) и установить для него значение 1 при блокировке строки. Чувствительное к этому select всегда будет добавлять условие WHERE locked != '1' к любому запросу.

Кстати, я не знаю, что вы делаете, но разве эту задачу не следует выполнять на уровень или два выше ядра базы данных?

person Pekka    schedule 12.01.2010
comment
Мне просто было любопытно, возможно ли это из базы данных. Поскольку мой текущий метод казался немного неуклюжим. Проект является многопоточным, и каждый поток должен выбирать работу из базы данных (работа постоянно добавляется из нескольких разных областей), однако никакие два потока не должны одновременно выполнять одну и ту же работу, поэтому моим текущим решением был мьютекс на функция, которая выбрала доступную работу, а затем специально обновила эти строки, чтобы сказать, что они обрабатываются, прежде чем освобождать мьютекс. Думаю, я уже делаю лучшее решение. - person bahhumbug; 12.01.2010