Никита Летов — Используем @Transactional like a Pro

preview_player
Показать описание
Ближайшая конференция — Joker 2024, 9 октября (Online), 15–16 октября (Санкт-Петербург + трансляция).
— —
Поговорим о том, как не ошибиться при использовании аннотации @Transactional в коде, как не бояться блокировок на БД и сохранить консистентность ваших данных при их динамическом обновлении.

Доклад посвящен использованию транзакций и блокировок при обработке данных БД в условиях асинхронных вызовов высоконагруженного приложения. Будет немного лайвкодинга: спикер покажет возможные ошибки и нюансы использования тех или иных технологий. Стек: Java, Spring Data JPA, Kafka. Иногда разработчики не задумываются, в какой момент транзакция нужна , а в какой нет, когда нужно открыть новую, а когда продолжить текущую. Какие блокировки на БД будут происходить во время выполнения транзакции и как не перегрузить БД очередью из row lock, где использовать таймауты запросов. И как обойтись без блокировки на БД.Целевая аудитория: рядовые бэкенд-разработчики, так как большинство сервисов в той или иной мере работает с динамически обновляемыми данными.

Рекомендации по теме
Комментарии
Автор

Круто что доклад затрагивает локи, изоляции и т д. Спасибо!

mouviem
Автор

54:10 - "произошел deadlock".
Строго говоря, на уровне базы данных, да и по определению - deadlock-а здесь нет.
Deadlock подразумевает, что заблокированы обе транзакции.
А у нас здесь заблокирована только вторая транзакция. Первая - просто простаивает.

Tx1 выполнила "select where id = 1 for update" и заблокировала строку. Сама транзакция не ждет в очереди ожидания на блокировку. Она ей владеет.
Но дальше в коде мы СИНХРОННО стартуем вторую транзакцию Tx2.
Tx2 тоже делает "select where id = 1 for update", и вот она встает в очередь на ожидание блокировки.

Tx2 заблокирована, а Tx1 - нет. Она просто не может выполняться из-за криво написанного кода.
По поведению - да, это похоже на дедлок. Но это ХУЖЕ чем дедлок.

Дело в том, что для реального дедлока нужно как минимум две транзакции и как минимум два ресурса (две строки).
Tx1:
UPDATE accounts set amount = amount + 100.0 where id = 2;
UPDATE accounts set amount = amount + 100.0 where id = 1;

Tx2:
UPDATE accounts set amount = amount + 100.0 where id = 1;
UPDATE accounts set amount = amount + 100.0 where id = 2;

Вот здесь будет дедлок. И PostgreSQL автоматом его задетектит и устранит. СУБД выкинет ошибку, и в приложении ее можно будет отловить.

А в примере в видео - не дедлок. Там просто долго (потенциально бесконечно, если не установили таймаут) выполняющаяся Tx1, и Tx2, которая ее ждет.
И такие ситуации PostgreSQL автоматом не разрулит.
Можно настроить логирование долгих локов (В самом PostgreSQL), и тогда в логи будет спам сообщений о том, что транзакция слишком долго держит лок.

МаксАли-ль
Автор

41 минута - вот почему лучше всегда отставлять явный save; если какая-то логика выполняется, то пусть она будет явной

philthehuman
Автор

Классное видео! Спасибо большое за ваш труд)

ВикторАнатольевич-ес
Автор

Это видео надо дать посмотреть всем любителям побыть умными на собесе и поспрашивать кандидата по поводу транзакций. Даже если в теории кандидат и ответит, не факт что на практике это всё будет работать как в теории.
У нас тоже был один такой, кто любил засыпать кандидатов вопросами по транзакциям, дабы снизить их ожидания по ЗП. Зато когда были баги, сам лез в гугл и удивлялся новому по теме транзакций в спринге и хибере. ЗП же однако получал как за двоих, так как на собесах сбивал ЗП кандидатам. Вот такие есть токсичные недоучки в лидах.

Boyarsskiy
Автор

54:05. Советую разобраться с темой дедлоков и не путать слушателей! Дедлока тут нет, тут обычная ожидающая блокировка. У вас нет взаимной блокировки ресурсов. Первая транзакция не заблокирована ожиданием возможности заблокировать новый ресурс, а вторая при этом не заблокировала ресурсы, необходимые первой.
И причем тут вообще таймаут на запросы и дедлок? У постгреса есть свойство, где указывается таймаут именно для дедлоков.

dmitriys
Автор

Спасибо за доклад!
Хорошо разобраны основные ошибки при работе с транзакциями.
Несмотря на то что я хорошо знаком с тразакциями, некоторые нюансы я взял на заметку.
Единственное что хотел отметить, следует быть осторожным с запуском CompletableFeature.runAsync(...), т.к. паралельные потоки съедают пул подключений к БД, а это в нагруженных приложениях это дефицитный ресурс. Иногда лучше сохранять в последовательных транзакциях, а лучше батчем в одной транзакции.

johnsnow
Автор

Спасибо, очень интересный и приятный доклад!

OlegGrinyuk
Автор

14:33. Уважаемый докладчик, вы перепутали все уровни изоляции транзакций. Вашу проблему (Lost Update ) решает уровень изоляции Read uncommitted. Именно для этого он и предназначен. Уровень Read committed так же решает эту проблему + решает проблему Uncommitted Dependency Problem (Грязное чтение). Repeatable read это еще более строгий уровень изоляции и в данном примере он не нужен. Более того, ваш пример не демонстрирует этот уровень.
Вы рассуждаете об уровнях изоляции, применимых конкретно к PostgreSQL, но не отмечаете это в докладе. Лучше было бы рассказать про стандартное описание уровней, а потом уже отметить особенности PostgreSQL. Ну и поработать с примерами получше.
У вас не заработал изначально RC так как параллельные транзакции считывают одни и те же данные и инкриментируют их в результате получается результат меньше, чем должен быть. Для решения этой проблемы нужна блокировка данных уже при чтении, а не только при записи. Поэтому уровень RR и заработал, но вы объяснили и показали совсем другие проблемы.

dmitriys
Автор

Очень полезный материал, спасибо !
Единственное, что непонятно
На 13.00 минуте Никита говорит про dirty read(про откат транзакции), а на примере показывает lost update(чтение одного и того же ресурса), действительно read committed решат lost update, но все же это путает :)

ДмитрийПохлебаев-еш
Автор

Спасибо за доклад, очень полезная тема была разобрана. Продолжайте в том же духе

katacode
Автор

Молодой Адам Сендлер жестко пояснил про транзакции

vermilinguas
Автор

49:00 Какой бред. Уровень Serializable уже блокирует данные при чтении, так как он решает проблему неповторяемого чтения. Какая еще блокировка?! Блокировки используют тогда, когда хотят вручную управлять уровнями изоляции, при этом уровень изоляции СУБД ставят на возможный минимум. Ну ладно, потом докладчик догадался проверить уровень.
Подготовка материала и понимание докладчиком темы - на низком уровне, как будто сам только вчера разобрался с транзакциями.

dmitriys
Автор

Чувак похож на того над кем он там смеялся из стаковерфлоу, в разрезе чтобы избежать дедлока на транзакции что нужно выбрать ту же строку но другим методом. Дядь, ты серьезно???? Она у тебя уже залочена в базе, але
Да и как бы коль не хочешь чтобы были дэдлоки то будь добр в нативку и nowait или skip locked

ДанилаФомин-ъх
Автор

Слабый доклад. С множеством ошибок. Автор плохо подготовился или сам не до конца понимает предмет.

wm
Автор

Сейвпойнты не помешали бы в докладе - когда пример не работает, можно из гита свичнуться на заренее закоммиченный пример)

alexhali
Автор

Лучшая оптимизация транзакции это отсутствие транзакции. В данном примере проблема решается нативным sql атомарным инкрементом.

Boyarsskiy
Автор

Менеджер трат - самое бесполезное что есть в банковских приложениях. Один из банков мне каждый месяц сообщал что 90% моих расходов уходит на путешествия и развлечения. Я обрадовался, да я ж счастливый человек, правда путешествия это были поездки на метро до работы, а развлечения - обед рядом с работой.

Boyarsskiy
Автор

Вставка о СТО 6:51 не подходит: согласно СТО ваши события могут произойти одновременно, т.к. события у вас не разделены в пространстве (одна БД)

CrunkDaG
Автор

Отличный доклад, спасибо ! 😊

По поводу проблемы с сохранением слишком большого количества записей в историю при использовании метода @Recover, кажется мне удалось понять почему он работал не так как надо в докладе
При использовании @Recover - сначала выполняются все попытки в Retryable, и только потом выполняется Recover метод, из-за этого в таблицу с историей попали дополнительные записи

NikolayPanyukov