Одне подібне твердження працює так само з 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
".