Выберите только разблокированные строки mysql
я заблокировал одну строку в одной транзакции, выполнив запрос
START TRANSACTION;
SELECT id FROM children WHERE id=100 FOR UPDATE;
и в другой транзакции у меня есть запрос, как показано ниже
START TRANSACTION;
SELECT id FROM children WHERE id IN (98,99,100) FOR UPDATE;
это дает ошибку блокировки ожидания превышен тайм-аут.
здесь 100 уже заблокирован (в первой транзакции), но идентификаторы 98,99 не заблокированы.Есть ли возможность вернуть записи 98,99, если только 100 заблокирован в строке выше запроса.Так что результат должен быть как ниже
Id
===
98
99
===
Id 100 следует игнорировать, поскольку 100 заблокирован транзакцией.
4 ответов
MySQL не имеет способа игнорировать заблокированные строки в SELECT. Вам нужно будет найти другой способ отложить строку в сторону как "уже обработанную".
самый простой способ-ненадолго заблокировать строку в первом запросе, чтобы отметить ее как "уже обработанную", затем разблокировать ее и снова заблокировать для остальной части обработки - второй запрос будет ждать завершения короткого запроса" маркер", и вы можете добавить явное условие WHERE для игнорирования уже отмеченных строк. Если нет ... хотите полагаться на первую операцию, способную успешно завершить, вам может потребоваться добавить немного больше сложности с метками времени и такими, чтобы очистить после этих неудачных операций.
выглядит так:SKIP LOCKED
опция, упомянутая в предыдущем ответе, теперь доступна в MySQL. Он не ждет, чтобы получить блокировку строк и позволяет работать со строками, которые в настоящее время не заблокированы.
С MySQL 8.0.0 примечания к выпуску / изменения в MySQL 8.0.1:
InnoDB теперь поддерживает
NOWAIT
иSKIP LOCKED
опцииSELECT ... FOR SHARE
иSELECT ... FOR UPDATE
блокировка инструкций чтения.NOWAIT
заставляет оператор немедленно возвращаться, если запрошенная строка заблокирован другой транзакцией.SKIP LOCKED
удаляет заблокированные строки из результирующего набора. см. раздел Блокировка параллелизма чтения сNOWAIT
иSKIP LOCKED
.
пример использования (полный пример с выходами можно найти в ссылке выше):
START TRANSACTION;
SELECT * FROM tableName FOR UPDATE SKIP LOCKED;
кроме того, было бы неплохо включить предупреждение в справочное руководство ЗДЕСЬ:
запросы, пропускающие заблокированные строки, возвращают несогласованное представление данных. это не подходит для общей транзакционной работы. Однако его можно использовать, чтобы избежать конфликтов блокировки, когда несколько сеансов обращаются к одной и той же таблице очереди.
MySQL не имеет этой функции. Для тех, кто ищет эту тему в целом, некоторые СУБД имеют лучшие / умные функции блокировки, чем другие.
для разработчиков, ограниченных MySQL, лучшим подходом является добавление столбца (или использование существующего, например, столбца состояния), который может быть установлен в "locked" или "in progress" или аналогичный, выполните SELECT ID, * ... WHERE IN_PROGRESS != 1 FOR UPDATE;
чтобы получить идентификатор строки, которую вы хотите заблокировать, выдайте UPDATE .. SET IN_PROGRESS = 1 WHERE ID = XX
чтобы разблокировать записи.
используя LOCK IN SHARE MODE
почти никогда не решение потому что, хотя это позволит вам прочитать старое значение, но старое значение находится в процессе обновления, поэтому, если вы не выполняете неатомную задачу, нет смысла даже смотреть на эту запись.
лучше* СУБД распознают этот шаблон (выберите одну строку для работы и заблокируйте ее, поработайте над ней, разблокируйте ее) и обеспечивают более умный подход, который позволяет вам искать только разблокированные записи. Например, PostgreSQL 9.5+ provide SELECT ... SKIP LOCKED
который выбирает только из разблокированного подмножества строк соответствие запросу. Это позволяет получить эксклюзивную блокировку строки, сервис, который записывает до завершения, а затем обновить и разблокировать соответствующую запись без необходимости блокировать другие потоки/потребителей от возможности работать независимо от себя.
* здесь "лучше"означает с точки зрения атомарных обновлений, многопользовательской архитектуры и т. д. и не обязательно "лучше спроектирован" или "в целом лучше".- Я не собираюсь устраивать здесь огненную войну.
по состоянию на http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
раствор для выполнения SELECT
в режиме блокировки с помощью LOCK IN SHARE MODE
:
SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;