MySQL: заблокирует ли транзакция строку?


13

Я не пробовал использовать транзакции MySQL раньше, я просто хочу кое-что прояснить.

Если два пользователя выполнят запрос в очень точное время, как MySQL справится с этим? например, пользователи пытаются обновить запись.

user1: обновить таблицу set column = column - 4, где column_id = 1;

user2: обновление таблицы set column = column - 7, где column_id = 1;

Теперь, если я использую транзакции, MySQL выберет, какой запрос будет выполнен первым, и заблокирует второго пользователя, пока первый запрос не будет зафиксирован? Это будет блокировка таблицы или строки?

Что если третий пользователь выдаст оператор select? Какое значение будет возвращать MySQL?

PS это будет на Innodb.

Ответы:


17

Один такой оператор работает так же с MyISAM или InnoDB, с транзакцией или с autocommit = ON. Он блокирует достаточно для выполнения запроса, тем самым блокируя другое соединение. Когда закончено, другое соединение продолжается. Во всех случаях столбец скоро уменьшается на 11.

Третий пользователь может увидеть значение, уменьшенное на 0, 4, 7 или 11. «Очень точное время» на самом деле невозможно, потому что в какой-то момент выполнения каждого оператора проверяется / устанавливается / любой однопотоковый блокиратор , То есть они будут сериализованы, настолько быстро, что вы не сможете их увидеть.

InnoDB блокирует только строки, а не таблицы. (ОК, оператор DDL делает более смелые блокировки.)

Что становится более интересным, так это транзакция, которая изменяет две вещи или занимает заметное количество времени:

Преднамеренный случай: один предмет, но требуется время:

BEGIN;
SELECT something;
think about it for a while
UPDATE that something;
COMMIT;

Выбор должен быть написан так:

SELECT something  FOR UPDATE;

Это говорит другим соединениям: «Я собираюсь обновить строку; пожалуйста, не путайте меня». (Я привожу этот пример, потому что многие новички упускают эту тонкость.)

Случай тупика: возиться с 2 вещами:

BEGIN;    -- in one connection
UPDATE thing_1;
UPDATE thing_2;
COMMIT;

BEGIN;    -- in another connection, at the "exact same time"
UPDATE thing_2;
UPDATE thing_1;
COMMIT;

Это классический пример тупика - каждый хватает одно, а потом тянется к другому. Очевидно, что это не может быть сделано для работы. Одна транзакция уничтожена; другой завершает. Следовательно, вы должны проверить на наличие ошибок, чтобы вы могли обнаружить это.

Обычной реакцией на тупик является воспроизведение всей неудачной транзакции. К тому времени другое соединение не будет мешать, и оно должно продолжаться без проблем. (ОК, еще одно соединение может создать еще один тупик.)

Задержка: если два соединения захватывают несколько вещей в одном и том же порядке, то одно может быть отложено до завершения другого. Чтобы не допустить «ожидания вечно», по умолчанию существует 50 секунд innodb_lock_wait_timeout. Ваша пара простых UPDATEsна самом деле пример этого случая. Один закончится быстро; другой останавливается, пока не закончится первый.

Обратите внимание, как Deadlock можно (в некоторых случаях) превратить в Задержку, последовательно упорядочив вещи, к которым вы прикасаетесь.

autocommit = 1: с этим параметром и без вызова BEGINкаждый оператор эффективно:

BEGIN;
your statement
COMMIT;

autocommit = 0: это проблема с ожиданием Когда вы выполняете запрос записи, BEGINнеявно генерируется. Тем не менее, это ваша ответственность в конечном итоге выпустить COMMIT. Если вам это не удастся, вы удивитесь, почему ваша система зависла. (Еще одна распространенная ошибка новичка.) Мой совет: «Никогда не используйте =0».

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.