Как работает команда Update
Подскажите, пожалуйста, правильно ли я понимаю, что последовательность команд
BEGIN;
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 12345
COMMIT;
будет выполняться следующим образом:
- СУБД находит строку , соответствующую acctnum = 12345
- СУБД накладывает эксклюзивную блокировку на найденную строку
- СУБД считывает значение ячейки balance, далее изменяет это значение и записывает обратно
- Выполняется команда COMMIT
- СУБД снимает блокировку только после выполнения команды COMMIT
Заранее благодарю!
P.S. База данных Postgres, уровень изоляции транзакций READ COMMITED.
Ответы (2 шт):
Да, в общем, правильно.
Есть только нюансы, если на шаге 2 выясняется, что строка уже заблокирована другой транзакцией. В этом случае события будут развиваться чуть по другому.
Когда блокировка другой транзакцией будет отпущена, то тут возможны два варианта. Если та другая транзакция сделала rollback, и значит ничего не поменяла в БД, то наша транзакция просто продолжит выполнение по описанным в вопросе шагам.
Если же другая транзакция сделала commit, то возможна ситуация, что условие acctnum = 12345 уже не выполняется, поэтому оно заново проверяется. Если условие для строки все еще выполняется, то далее продолжается процесс с шага 2. Если же для стороки условие acctnum = 12345 больше не выполняется, то в результате самой операции UPDATE ни одна строка не будет изменена. Выполнение продолжится, т.е. будет сделан COMMIT, транзакция завершена без изменения данных.
- выполняются before statement триггера
- СУБД ищет строку, соответствующую acctnum = 12345
- выполняет проверку, что эта версия строки видима текущей транзакции
- если строку найти не удалось - переходим к п.14
- выполняются before update for each row триггера. Если таковой существует, то выполняем манипуляции из п.8 и возвращаемся обратно сюда
- проверяется существование instead of update триггера. Если есть - то выполняем их и продолжаем с п.14
- если выполняем операцию над FDW таблицей - передаём дальнейшее управление FDW машинерии и продолжаем с п.14
- достаём наиболее актуальную версию, попутно запрашивая пишущую блокировку на неё. Если какая-то транзакция удерживает блокировку - то ждём её и затем достаём возможно изменённую актуальную версию строки. Если транзакция завершилась rollback - то берём, конечно, не ту версию строки что пыталась сделать на транзакция, а последнюю зафиксированную. После разруливания блокировок и получения данных строки можем выяснить:
- строка была удалена - тогда уходим на пункт п.14 (в том числе если сюда мы пришли из before update for each row триггера)
- строка была изменена и более не подходит под требуемые условия поиска - аналогично уходим на пункт п.14
- всё случилось, у нас есть блокировка строки и данные строки, продолжаем работать (возврат к п.5 если пришли оттуда)
- выполняем в памяти требуемые операции обновления данных
- помечаем строку удалённой, пишем в таблицу новую версию строки, добавляем в индексы сведения о новой версии строки
- выполняются after update for each row триггера
- формируем returning структуру, если применимо
- переход на пункт 2
- выполняются after statement триггера
Все взятые блокировки освобождаются только при завершении транзакции.
Вот отсюда можно начать читать, ничего ли я не забыл описать важного и не напутал ли порядок.