У нас есть совместный банковский счет с балансом 200 долларов
Я иду в банкомат и вставляю свою карту в автомат, машина проверяет, что у меня есть баланс 200 долларов
Тем временем, вы идете в банк и просите 50 долларов, кассир открывает ваш счет и подтверждает, что у вас есть деньги.
Я запрашиваю снятие 200 долларов, автомат считает мои деньги, дает мне 200 долларов и устанавливает мой баланс на уровне 0 долларов
Кассир считает ваши деньги и дает вам 50 долларов, затем система обновляет баланс на счете как 150 долларов ( $ 200 - вывод $ 50).
Итак, теперь у нас есть 250 долларов наличными, а на счету осталось 150 долларов. 200 долларов прибыли.
База данных должна была использовать блокировки, чтобы предотвратить одновременное выполнение обеих транзакций.
Проблема в том, что если вы обрабатываете каждую транзакцию таким образом, мы потеряем параллелизм и снизится производительность, поэтому существуют разные уровни изоляции транзакций
, которые используются в зависимости от сценария, например, вы можете не использовать заботиться о том, чтобы кто-то мог изменить данные, прочитанные в транзакции.
http://en.wikipedia.org/wiki/Isolation_%28database_systems%29
Вы должны изучить их и понять сценарии, в которых они применимы.
Несколько дней назад я ответил на вопрос о SO и привел пример, демонстрирующий ситуацию, когда блокировка позволяет нескольким пользователям одновременно вставлять строки в таблицу с увеличением ] id
без использования AUTO_INCREMENT
.
Рассмотрим следующую схему в качестве примера:
CREATE TABLE demo_table (id int) ENGINE=INNODB;
-- // Add few rows
INSERT INTO demo_table VALUES (1), (2), (3);
Затем мы можем сделать следующее:
START TRANSACTION;
-- // Get the MAX(id) so that we increment it by one
SELECT @x := MAX(id) FROM your_table FOR UPDATE;
+---------------+
| @x := MAX(id) |
+---------------+
| 3 |
+---------------+
1 row in set (0.00 sec)
Синтаксис FOR UPDATE
- это то, что фактически блокирует строки, считываемые этим запросом.
Не фиксируя транзакцию, мы запускаем другой отдельный сеанс (моделируя одновременного пользователя) и делаем то же самое:
START TRANSACTION;
-- // Get the MAX(id) as well
SELECT MAX(id) FROM demo_table FOR UPDATE;
База данных будет ждать, пока блокировка, установленная в предыдущем сеансе, не будет снята, перед выполнением этого запроса.
Таким образом, переключившись на предыдущий сеанс, мы можем вставить новую строку и зафиксировать транзакцию:
-- // Insert a new row with id = MAX(id) + 1
INSERT INTO demo_table VALUES (@x + 1);
COMMIT;
После того, как первый сеанс зафиксирует транзакцию, блокировка будет снята, и будет возвращен запрос во втором сеансе:
+---------+
| MAX(id) |
+---------+
| 4 |
+---------+
1 row in set (8.19 sec)
Обратите внимание, что без блокировки второй сеанс вернулся бы немедленно, но с 3
как MAX (id)
вместо 4
. Если бы оба сеанса вставили строку с id
из MAX (id) + 1
, оба бы вставили id = 4
. Вы можете смоделировать тот же тест без бита FOR UPDATE
, чтобы увидеть, как это обрабатывается без блокировок.
Блокировка может иметь решающее значение для предотвращения одновременного изменения данных двумя пользователями. Вы можете подумать, что это маловероятно, но в зависимости от приложения существует значительный риск, если одни и те же данные часто меняются разными пользователями.
Представьте себе следующую ситуацию без использования блокировок: Джон открывает свой экран (он не знает, что использует базу данных, он всего лишь конечный пользователь, который смотрит на красивый экран), изменяет некоторые данные , а затем нажмите «Сохранить». Допустим, Джон открывает экран в 9:30, а затем сохраняет данные в 9:32.
Однако Мэри открыла точно такой же экран и тот же рекорд в 9:29. В то время она увидела те же данные, что и Джон в 9:30. Затем она обновляет запись и нажимает «Сохранить» в 9:31.
Какие данные были сохранены? Джона или Мэри?
Мэри с радостью продолжает работать над другими записями, и когда она возвращается позже, чтобы снова открыть запись, она видит, что ее изменения были потеряны, и вместо этого видит изменения Джона !!
Помните, что блокировку нужно использовать с умом, чтобы предотвратить неожиданные побочные эффекты. Например, предположим, что ваша программа блокирует запись каждый раз, когда кто-то открывает ее и вносит изменения. Что произойдет, если Джон заблокирует запись и оставит свой экран сеанса открытым, чтобы пообедать, или он потеряет соединение? Блокировка может оставаться заблокированной и неизменной в течение долгого времени, запрещая всем остальным изменять (или даже просматривать) эту запись. Другим соображением может быть производительность, потому что время, необходимое базе данных для блокировки и разблокировки записей, может стать заметным для большого количества транзакций.
Понимание блокировки имеет решающее значение для поддержания счастливых пользователей и целостности данных. Пожалуйста, посмотрите документацию.