Какие свойства транзакций существуют кроме атомарности?
Что проверяют
Расшифровку ACID знают все, поэтому интервьюер слушает, что вы скажете дальше: какие аномалии допускает изоляция на практике и как это выглядит в Java-коде. Следом почти всегда спрашивают: «Какой уровень изоляции по умолчанию в вашей базе и что он допускает?»
Как отвечать
Кроме атомарности есть три свойства:
- согласованность (Consistency): транзакция переводит базу из одного корректного состояния в другое, не нарушая
NOT NULL,UNIQUE, внешних ключей иCHECK. Инвариант вроде «баланс не уходит в минус» база обеспечит, только если он выражен ограничением, иначе это ответственность кода; - изолированность (Isolation): параллельные транзакции не должны мешать друг другу. Полная изоляция дорогая, поэтому стандарт SQL задаёт четыре уровня:
READ UNCOMMITTED,READ COMMITTED,REPEATABLE READ,SERIALIZABLE. Они различаются допустимыми аномалиями: грязное чтение, неповторяющееся чтение, фантомы. На практике важны ещё потерянное обновление и write skew; - долговечность (Durability): подтверждённый коммит переживёт сбой. Это обеспечивает журнал упреждающей записи: коммит завершён, когда запись в журнале сброшена на диск.
Назовите умолчания: в PostgreSQL и Oracle это READ COMMITTED, в MySQL с InnoDB REPEATABLE READ. В PostgreSQL READ UNCOMMITTED ведёт себя как READ COMMITTED, грязного чтения там нет.
Затем Java-сторона. В Spring уровень задаётся через @Transactional(isolation = ...), по умолчанию Isolation.DEFAULT, то есть уровень базы. От потерянного обновления защищают оптимистическая блокировка с @Version в JPA или пессимистическая через SELECT ... FOR UPDATE, в Spring Data это @Lock(LockModeType.PESSIMISTIC_WRITE).
Где спотыкаются
Путают C из ACID с C из теоремы CAP. В CAP согласованность означает, что все узлы видят одни данные, к ограничениям целостности это не относится.
Считают, что @Transactional откатывает транзакцию при любом исключении. По умолчанию откат только на RuntimeException и Error, для проверяемых исключений нужен rollbackFor.
Думают, что аннотация сработает при вызове метода из того же класса. Вызов через this идёт мимо прокси, и транзакция не открывается.