- Используйте
ENGINE=InnoDB
для всех ваших таблиц. Используйте транзакции:
BEGIN;
do all the work for a single action
COMMIT;
Классическим примером одного действия являетсяснять деньги с одного счета и добавить его на другой счет.Удаление будет включать проверку овердрафта, и в этом случае вы получите код ROLLBACK
вместо COMMIT
.
Полученные блокировки гарантируют, что все для одного действия либо полностью выполнено, либо ничеговсе сделано.Это применимо даже в случае сбоя системы между BEGIN
и COMMIT
.
Без начала и фиксации, но с autocommit = ON, каждый оператор неявно окружен begin и commit.Это пример UPDATE
в предыдущем ответе - «атомарный».Однако, если деньги, вычтенные с одного счета, необходимо добавить на другой счет, что произойдет, если сбой произойдет сразу после UPDATE
?Деньги исчезают.Итак, вам действительно нужно
BEGIN;
if not enough funds, ROLLBACK and exit
UPDATE to take money from one account
UPDATE to add that money to another account
INSERT into some log or audit trail to track all transactions
COMMIT;
Проверять после каждого шага - ОТКЛЮЧИТЬ и предпринимать уклончивые действия при любой неожиданной ошибке.
Что произойдет, если 2 (или более) действия произойдут в одновремя "?
- Один ждет другого.
- Существует тупик, и на него навязывается ОТВЕРТКА.
Но, ни в коем случаеБудут ли испорчены данные.
Еще одно примечание ... В некоторых случаях вам нужно FOR UPDATE
:
BEGIN;
SELECT some stuff from a row FOR UPDATE;
test the stuff, such as account balance
UPDATE that same row;
COMMIT;
FOR UPDATE
говорит другим темам "Держите рукиВне этой строки я могу изменить его; пожалуйста, подождите, пока я не закончу ".Без FOR UPDATE
другой поток мог бы проникнуть и истощить счет денег, которые, как вы думали, были там.
Комментарии к некоторым из ваших мыслей:
- Одной таблицы обычно достаточно длямного пользователей и их аккаунт.Он будет содержать «текущий» баланс для каждой учетной записи.Я упомянул «бревно»;это будет отдельная таблица;он будет содержать «историю» (в отличие от «текущей» информации).
FOREIGN KEYs
в данном обсуждении в основном не имеют значения.Они служат 2 целям: убедитесь, что в другой таблице есть строка, которая должна быть там;и неявно создайте INDEX
, чтобы ускорить эту проверку. - Конвейер?Если вы выполняете не более ста «транзакций» в секунду, вам нужно беспокоиться о логике
BEGIN..COMMIT
. - «В то же время» и «одновременно» - неправильно используемые термины.Маловероятно, что два пользователя будут одновременно обращаться к базе данных - учитывая задержки браузера, сетевые задержки, задержки ОС и т. Д. Плюс тот факт, что большинство этих шагов заставляют активность переходить в один файл.Сеть заставляет одно сообщение попасть туда раньше другого.Между тем, если одна из ваших «транзакций» занимает 0,01 секунды, кого это волнует, должен ли «одновременный» запрос дождаться его завершения?Дело в том, что то, что я описал, заставит «ждать», если это необходимо, чтобы избежать путаницы в данных.
Все это говорит о том, что все еще могут быть некоторые «в то же время» - если транзакциине касайтесь одних и тех же строк, тогда несколько миллисекунд, которые требуются от BEGIN
до COMMIT
, могут перекрываться.Рассмотрим график двух транзакций, которые произошли почти одновременно:
BEGIN; -- A
pull money from Alice -- A
BEGIN; -- B
pull money from Bobby -- B
give Alice's money to Alan -- A
give Bobby's money to Betty --B
COMMIT; --A
COMMIT; --B