MySQL: чи заблокує транзакцію рядок?


13

Я раніше не намагався використовувати транзакцію MySQL, просто хочу щось уточнити.

Якщо два користувачі виконують запит в той самий час, як MySQL впорається з цим? наприклад, користувачі намагаються оновити запис.

user1: оновити набір стовпців таблиці = стовпець - 4, де column_id = 1;

user2: оновити набір стовпців таблиці = стовпець - 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;

Це говорить іншим підключенням "я маю намір оновити рядок; будь ласка, не псуйте мене". (Я наводжу цей приклад, тому що багато новачок пропускають цю тонкість.)

Випадок тупику: возитися з двома речами:

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насправді є прикладом цього випадку. Один закінчить оперативно; інший затримується до першого закінчення.

Зверніть увагу, як тупик може (в деяких випадках) перетворюватися на затримку, послідовно впорядковуючи речі, до яких ви торкаєтесь.

autocommit = 1: За допомогою цього параметра та без виклику BEGINкожне твердження ефективно:

BEGIN;
your statement
COMMIT;

autocommit = 0: Ця проблема не чекає. Коли ви виконуєте запит запису, а BEGINнеявно генерується. Тим не менш, це ваша відповідальність, щоб врешті-решт видати COMMIT. Якщо ви цього не зробите, вам стане цікаво, чому ваша система повішена. (Ще одна поширена помилка для новачків.) Моя порада: "Ніколи не використовуй =0".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.