Транзакции - Spring Framework в деталях
HTML-код
- Опубликовано: 13 янв 2025
- Транзакции являются важным инструментом для построения отказоустойчивых информационных систем, работающих в условиях постоянной высокой нагрузки и обеспечивающих одновременную работу десятков, сотен, а то и тысяч пользователей.
В этом ролике я постарался рассказать про проблемы, возникающие в многопользовательских информационных системах, про транзакции и требования ACID к транзакционным системам, уровни изолированности и распространение транзакций, а так же демонстрирую API для управления транзакциями, предоставляемые Spring Framework.
#java #spring #транзакции #transaction
Мой сайт: alexkosarev.name/
Паблик в VK: public2...
Канал в Telegram: t.me/+TZCuO38v...
Стать доном: donut/s...
Донаты в Boosty: boosty.to/akos...
Донаты в Tinkoff: www.tinkoff.ru...
*Таймкоды:*
00:00:00 *Вступление*
00:01:40 *Проблемы при работе с данными*
00:02:35 *Понятие транзакции*
00:03:15 *Аномалии при параллельной работе с данными*
00:04:04 *Грязные чтения*
00:07:41 *Потерянные изменения*
00:10:46 *Потерянные изменения (атомарные операции)*
00:12:20 *Не повторяющиеся чтения*
00:14:27 *Чтение фантомов*
00:16:54 *Требования ACID*
00:17:40 *Atomicity (атомарность)*
00:18:19 *Consistency (согласованность)*
00:19:19 *Isolation (изолированность)*
00:20:06 *Durability (устойчивость)*
00:20:49 *Уровни изолированности транзакций*
00:21:25 *Read uncommitted*
00:22:59 *Read committed*
00:24:28 *Repeatable read*
00:25:57 *Serializable*
00:26:58 *Какой уровень выбрать?*
00:28:07 *Нюансы уровней изолированности в разных СУБД*
00:29:22 *Распространение транзакций (пропагация)*
00:30:10 *REQUIRED*
00:30:37 *SUPPORTS*
00:31:03 *MANDATORY*
00:31:23 *REQUIRES_NEW*
00:32:34 *NOT_SUPPORTED*
00:32:55 *NEVER*
00:33:16 *NESTED*
00:34:35 *Физические и логические транзакции*
00:35:50 *Практика*
00:37:36 *Управление транзакциями через @Transactional*
00:38:30 *Реализация @Transactional в Spring*
00:39:27 *Параметры аннотации @Transactional*
00:43:33 *Демонстрация работы @Transactional*
00:45:00 *Нюанс с вызовом @Transactional метода в пределах одного сервиса*
00:54:19 *Управление транзакциями с помощью XML-конфигурации*
01:00:29 *Управление транзакциями с помощью TransactionTemplate*
01:06:54 *Управление транзакциями с помощью TransactionManager*
01:14:51 *Какой способ выбрать?*
01:16:39 *Где располагать управление транзакциями?*
01:18:39 *Итоги*
01:19:17 *Полезный совет*
Это пожалуй *самое понятное и всеобъемлющее объяснение* в российском сегменте RUclips 👍
У Вас очень крутой материал, на ютубе мало кто затрагивает такие темы)))
Спасибо Вам и удачи!!!))))
Без запинки говорит, как будто читает.
Темы актуальные понимает.
Спасибо!
Огромная благодарность за такой содержательный материал! Уголок сельского джависта - это находка для каждого, кто хочет саморазвиваться и понимать как оно работает изнутри.
Большое спасибо не раз, Александр!
Ранее по крупицам собирал эти знания в конспекты, а тут все в одном видео. Огромное спасибо автору!
Блин, как круто что наткнулся на этот канал. Находка прям. Дибильные алгоритмы предлагают чаще всего всякий шлак, когда есть такое.... Приятно слушать, всё понятно. Подпись.
Лучший гайд по транзакциям. Мега респект
очень нужная и крутая тема!!! Спасибо автору👍👍👍
Спасибо за материал! Очень нравится подача, редко встретишь в ру сегменте и грамотную речь и качество материала)
Супер контент!! С нетерпением жду материала про реактивщину)
Наконец-то нашел точное и понятное объяснение propagation=NESTED. Спасибо!
Наконец-то новый фильм на выходной :)
Много полезного, простая и наглядная подача. Спасибо автору!
Спасибо тебе большое за подробный рассказ! 👍🏻
Спасибо, Александр, за вашу работу. Удивительная концентрация полезной информации на единицу времени. Пожалуй, лучшая, что я видел в обучающих видео на ютубе.
Ноль воды. Всё по делу. Все с классными примерами. Не бросай канал) для вызова транзакционных методов "изнутри" делаю класс обертку, чтобы избежать самовпрыска иои бинфакторивпрыска.
Спасибо, всё отлично разобрано, подтянул себя по Транзакциям
Спасибо большое. ОЧЕНЬ четко и доходчиво все объяснено.
Мне твое видео очень сильно помогло на собеседовании хорошо раскрыть эту тему! Благодарю!)
Спасибо за труд, все очень подробно и понятно. 👍👍👍
Было бы круто еще раскрыть, то как @Transational работает в связке с @Async
Дай Бог тебе здоровья, дружище!
очень хорошие ролики делаешь,особенно security нравится 👍 Спасибо.Здоровья тебе и твоим близким 🤗
Спасибо большое, очень хорошо систематизировано!
Огромнейшее спасибо! 🔥
спасибо огромное!
отличный материал с хорошей подачей! Спасибо
Спасибо! Отличный материал! Однозначно подписка.
О, да! Как раз хотел изучиить тему транзакции. Очень круто, спасибо! Я еще пока что не смотрел ролик, но уверен, что материал качественый. Ждем от тебя разбора Сессии, Кафки.
Супер! Спасибо за материал
Спасибо за видео! Очень полезное)
Super!🔥
Спасибо за видос, очень полезный
Очень полезно, спасибо
Очень крутой видос! С нетерпением жду что то подобное про хибер
Спасибо тебе огромное ! Очень всё понятно объяснил. Подписался)
Очень познавательно, спасибо!
как всегда топ, все мы немного сельские программисты
Спасибо )
Спасибо за видео!
Спасибо за контент !)🔥
Не, ну после такого точно подписка))
Хорошее видео (ещё не смотрел, но знаю заранее)
Хотелось бы послушать от вас про hibernate, там много нюансов, особенно в контексте сохранения/обновления/удаления сущностей с one to many, many to one, many to many, ну и про знаменитую n + 1)
Особенно интересует кейс, когда есть сущность А, у которой есть 2 oneToMany (или manyToMany) коллекции зависимых сущностей B и C. И нужно достать лист сущностей А вместе с их зависимыми сущностями, причем с пагинацией (обычный джойн фетч не помогает)
Есть несколько способов это сделать, хочется узнать, как лучше
Учтём пожелания
очень подробно без воды !!! Автор молодец!
Спасибо!
Очень крутое видео, спасибо, долго ждал новое видео
спасибо!
в поддержку)
Потрясающе!
Лайк подписка жаль что так мало подписчиков, но со временем должны набраться, потому что контент полезный
Спасибо!
Кстати, 53:00 - Spring поддерживает self-injection через @Autowired
Это просто кладезь информации с очевидными примерами. Уже всем скинул ссылку на это видео. Наглядность примеров поразила! Подписался, теперь буду все смотреть :) Спасибо за проделанную работу!
А можно ли комбинировать аннотации и темлпейт или менеджер? Последние нормально будут считывать пропагацию или уровни изолированности?
Да, комбинировать можно, в конечном итоге всё равно будет 2+ обращения к менеджеру, просто одно будет в АОП-совете
Класс, спасибо за твои видео, наткнулся случайно на них, но из кучи контента как то зацепила подача информации. Я, правда, недавно в IT и свой путь начал с нагрузочника, но планирую в разрабы перейти, поэтому твои видео для меня открытие просто. В основном вся информация, которая попадает на ютубе, однообразная, то есть у всех как то однотипно сделано, без сложностей особых. Но для моего уровня видно, что ты уже опыт большой имеешь и много для меня непонятных моментов, из за которых приходится по 10 раз видео пересматривать и разобираться в коде. Да, не просто, в основном, много непонятного, но потом, после того, как разобрался, многое в работе кажется простым и за это спасибо) Удачи тебе и побольше подписчиков твоему каналу!) И знай, что есть люди, которые ищут хорошие каналы для обучения и однажды кто-то найдет твой и скажет спасибо, так что не останавливайся)
очень жесткое видео - что касаемо знаний
15:03 "Чтение фантомов" тут небольшая оговорка. А так материал ценный. Спасибо.
Добрый день. Спасибо за огромную работу. Очень понятно объясняете!
Комментарий на счет readOnly. Никакие исключения не выкидываются, если транзакция что-то будет писать в бд, этот флаг для оптимизации транзакции.
Почему программист сельский? Говоришь грамотно, шаришь и объясняешь годно. Спасибо.
Отлично, гляну после этого ролика про AOP. Как раз почему-то тесты в 5 главе (исходников) для книги Pro Kotlin почему-то не идут.
Потому что сельский) Я весь отпуск занимался сбором урожаев и заготовкой дров)
@@shurik_codes Наверное неплохо противодействует выгоранию..
Клёво! Расскажи про Kafka плиз
23:30 говорите по поводу того, что уровень read committed не помогает от аномалии потерянного изменения (обновления - lost update) могу ошибаться или я не так понял, но вроде бы это не так.
В read committed не воспроизодится грязное чтение и потерянное обновление.
В постгресе, например, потерянное обновление не воспроизводится.
Стандарт SQL не предполагает потерю обновления SQL92 - 4.28 SQL-transactions
lost update в этом стандарте нет
Жалко нельзя два плюса поставить видео, спасибо!
Добрый день! Спасибо за шикарный урок! Какие книги можете посоветовать по транзакциям? И вообще книги - "must have" для Java разработчика?
По транзакциям - не скажу, по остальному:
- Книги Роберта Мартина (в первую очередь "Чистый код" и "Чистая архитектура")
- Паттерны объектно-ориентированного программирования
- Книги про алгоритмы и структуры данных (Грокаем алгоритмы, Алгоритмы Скиены и Стивенса)
- Книги посвящённые предметно-ориентированному проектированию (синюю и особенно красную)
- Реактивные шаблоны проектирования
Можно ли эти уровни транзакций применять отдельно к разным строкам в таблице? Если они связаны реляциями с другими записями в других таблицах - можно же применять эти уровни к отдельным записям, а не рестартить базу каждый раз после замены конфига. И писать это не в конфиг а в отдельную метаинфу по записи при создании. И вроде как такой подход более оптимален с точки зрения разных вариаций использования
По пожеланиям: - транзакционная отправка в кафку (постоянно слышу этот вопрос при разработке)
почитайте про паттерн outbox, реализация с помощью debezium
согласованность это как раз про бизнес требования
спасибо - когда будет что-то новое
Мне кажется на 15:04 маленькая оговорка - имеется "это и есть неповторяющееся чтение" должно быть "чтение фантома"
Очень информативный видос!
Задаюсь вопросом, нужно ли оборачивать селект в транзакцию с изоляцией, например, read_committed? Если ее в транзакцию не завернуть, то поведение будет такое же - прочитаем только записи с зафиксированными изменениями. Или я что-то упустил?
Всё зависит от конкретной СУБД, но, поскольку read_uncommitted по умолчанию нигде не используется (вроде бы), все запросы и так выполняются в рамках транзакции с уровнем изоляции read_committed (например, в PostgreSQL) и выше (например, в MySQL). Так что в целом нет смысла. Смысл в ручном управлении транзакциями при операциях чтения появляется, когда, например, хочется ограничить общее время исполнения.
Отличный ролик, как всегда! Можно название шрифта?
JetBrains Mono
Есть в планах работа с сессиями?)
Какие сессии подразумеваются? Если Hibernate/JPA, то когда доберусь до этой темы.
@@shurik_codes я думал про сессии приложения именно
@@shurik_codes Jpa/hibernate мне кажется многие хотябы имеют представление, а вот я не смог найти нормальные видео или рускоязычные статьи про сессии приложения.
@@paradiesd HTTP?
@@shurik_codes Да, плюс конфигурация, чтобы можно было написать самому логику создания сессии , было бы мне кажется очень интересно
про друге транзукции если можно - платёжную систему какую-нить самую популярную
A zachen implementacia interface final? V chem profit?
С точки зрения чистоты архитектуры класс должен быть либо абстрактным, т.е. поддерживать дальнейшее расширение, либо финальным.
Hello Mentor. Could you share about kafka with spring boot. Please!
Sure, I'll do it someday
добрый день, а можете поделиться книгами для java разработчиков, которые читаете и можете посоветовать для развития? спасибо 🙌
А это примеры на MySQL? Прошу автора помочь разобраться!
Кажется в Postgres это работает не совсем так. Из того что прочитал на открытых ресурсах:
Блокировка происходит на команды insert, update, delete, select for. Поговорим про read_comitted и repeatabble_read:
Транзакция 1 делает изменения через update тем самым блокирует работу со строкой
Транзакция 2 делает update ожидая пока транзакция 1 сделает коммит или ролбэк
Сценарий для repeatabble_read:
- Транзакция 1 делает коммит
- Транзакция 2 видит что изменения были с этими данными (Postgre поддерживает версионноость) снимки с БД показали разные версии и Транзакция 2 откатывается!
ВТОРОЙ ВАРИАНТ С РОЛБЭК
- Транзакция 1 делает ролбэк
- Транзакция 2 сделав снимок видит что изменений нет при update и деает транзакции выполнить этот скрипт!
Теперь сценарий для read_comitted:
- Транзакция 1 делает коммит
- Транзакция 2 делает коммит
Конкретно в этом ролике примеры в MySQL
Если у нас есть сервис с методом transactional в котором идет вызов двух других методов в других сервисах также с аннтоацией transactional, то при ошибке во втором сервисе откатится ли транзакция в первом?
Если выбран вариант пропагации, выполняющий код всех методов в одной транзакции (например, PROPAGATION_REQUIRED), то да.
@@shurik_codes Благодарю
Автор ролика утверждает, что запрос "update t_account set c_balance = c_balance - 100 where id = 200" является атомарной операцией. Он говорит про базы данных, и насколько мне известно, он действительно прав. Но хотелось бы понимать, почему всё что выполняется в рамках одного оператора в базе данных является атомарной операцией?? В Java изменение поля balance = balance - 100 не являлось бы атомарной операцией, потому что поле balance сначала вычитывается, потом от него вычитается 100, и потом результат снова присваивается полю balance. За счёт чего в базах данных по-другому??
@user-fh5yj5cr8d всё-таки, как-то непонятно... Ну хорошо, открылся этот самый курсор, т.е. мы начали работу со снэпшотом данных на определённый момент. И что это меняет? К тому моменту как мы присваиваем полю c_balance значение c_balance(из снэпшота) - 100 другая транзакция могла поле c_balance уже поменять и закомитить. Получается потерянное обновление. Где здесь атомарность?
Вот и лето закончилось, вентилятор с кадра пропал(
Вернём в следующем году)
Не совсем понял, что автор подразумевал под "атомарной операцией" при потерянном изменении. set c_balance = c_balance - 100. Это атомарная?
Ну да, это атомарный UPDATE
А для чего нужно было объявлять класс final? Без final можно было бы прямо на метод в классе аннотацию повесть бед хоровода с интерфейсом.
UPD: пардон, досмотрел, для демонстрации было показано )
Затем, что это распространённая практика - делать финальными классы, которые не планируется расширять, а те, что планируется расширять - абстрактными. Придумано не мной, основная цель - соблюдение принципа открытости/закрытости, на мой взгляд делает код более строгим и устойчивым.
@@shurik_codes Ну если команда договорится, то можно и не расширять класс. Да и в 99% случаев никто не расширяет сервис в каком-нибудь CRUD. Но лезть в контекст спринга из класса с бизнес-логикой не лучший компромисс только ради поклонения советам из книг по clean code, которые вообще писались в отрыве от фреймворков и их особенностей. Этот код потом будет вызывать постоянные вопросы у других разработчиков, которые и не догадывались что весь хоровод только из-за ключевого слова final, так как так красивее. Я думал пример показан для экстремальных случаев, а оказалось это "распространённая практика".
Мне кажется автор ошибся - lost update не допускается ни на одном из четырех озвученных уровнях изоляции.
В MySQL lost update даже на уровне изолированности Repeatable read проявляется, так что нет, я не ошибся, всё зависит от реализации конкретной СУБД. Я демонстрировал пример на уровне read uncommitted
Небольшое уточнение: аномалия с потерянными изменениями может проявляться в двух случаях.
В первом может иметь место потеря изменений при выполнении атомарных операций без использования транзакций (напр, UPDATE T_ACCOUNT SET C_BALANCE = C_BALANCE + 100 WHERE ID = 1). В теории использование транзакций с любым уровнем изолированности должно решать эту аномалию.
Во втором случае потеря изменений возникает при присвоении колонке нового значения, вычисляемого вне запроса на обновление. Пример такой транзакции был как раз продемонстрирован в ролике:
BEGIN;
SELECT @balance := C_BALANCE FROM T_ACCOUNT WHERE ID = 1;
UPDATE T_ACCOUNT SET C_BALANCE = @balance + 100 WHERE ID = 1;
COMMIT;
И вот во втором случае всё зависит от реализации используемой СУБД: так MySQL теряет изменения даже при уровне изолированности транзакции REPEATABLE READ, а PostgreSQL - только на уровне READ COMMITTED, если память меня не подводит. Ну и есть мнение, что для решения второго варианта этой аномалии лучше использовать блокировки, а не полагаться на уровни изолированности транзакций.
Всегда думал что грязное чтение это про то, что именно в момент транзакции можно подхватить некорректные данные. Пролил свет на то, что данные фиксируются.
используешь два монитора сразу, признавайся?!
Использовал, заменил на один 3440x1440
круто, и у меня такой был)@@shurik_codes
Read commited устраняет проблему потерянных изменений вроде бы. Может есть тонкости в конкретных СУБД, но данный ролик говорит в общем о существующем стандарте, и иногда упоминается некоторые специфичные вещи.
ru.wikipedia.org/wiki/%D0%A3%D1%80%D0%BE%D0%B2%D0%B5%D0%BD%D1%8C_%D0%B8%D0%B7%D0%BE%D0%BB%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D1%82%D1%80%D0%B0%D0%BD%D0%B7%D0%B0%D0%BA%D1%86%D0%B8%D0%B9 (Таблица в конце тоже об этом говорит)
И строка "Стандарт SQL-92 определяет шкалу из четырёх уровней изоляции: Read uncommitted, Read committed, Repeatable read, Serializable. Первый из них является самым слабым, последний - самым сильным, каждый последующий включает в себя все предыдущие." также на это намекает
В видео сказано (23:25), что нет
Из ответа к другому комментарию:
Аномалия с потерянными изменениями может проявляться в двух случаях.
В первом может иметь место потеря изменений при выполнении атомарных операций без использования транзакций (напр, UPDATE T_ACCOUNT SET C_BALANCE = C_BALANCE + 100 WHERE ID = 1). В теории использование транзакций с любым уровнем изолированности должно решать эту аномалию.
Во втором случае потеря изменений возникает при присвоении колонке нового значения, вычисляемого вне запроса на обновление. Пример такой транзакции был как раз продемонстрирован в ролике:
BEGIN;
SELECT @balance := C_BALANCE FROM T_ACCOUNT WHERE ID = 1;
UPDATE T_ACCOUNT SET C_BALANCE = @balance + 100 WHERE ID = 1;
COMMIT;
И вот во втором случае всё зависит от реализации используемой СУБД: так MySQL теряет изменения даже при уровне изолированности транзакции REPEATABLE READ, а PostgreSQL - только на уровне READ COMMITTED, если память меня не подводит. Ну и есть мнение, что для решения второго варианта этой аномалии лучше использовать блокировки, а не полагаться на уровни изолированности транзакций.
@@shurik_codes , понятно, не привычно просто слышать о проблеме потерянных изменений в таком ключе. Когда говорят о кейсе потерянного изменения имеют ввиду возможность этих потерь именно исключительно при апдейте, абстрагируясь от кейса предварительного чтения (для вычисления значения изменяемых данных). Поэтому эта проблема и рассматривается как самая наименьшая и решаемая любым уровнем изоляции.
А кейс чтения перед изменением - это чаще оговорка в процессе рассказа о грязном чтении. Ну потому что корень проблемы в том, что прочитанные ранее данные уже неактуальные (то есть проблема больше в ранее прочитанном, а не при изменении). В вики этот кейс, как я понял, тоже вынесен в раздел грязного чтения (хотя сам он в большой степени, конечно, про чтение незакомиченного и откатанного).
Но я понял вас, спасибо.
Спасибо за видево!