Знаете ли вы про многопоточность в базах данных
Что проверяют
Понимаете ли вы, что происходит, когда много потоков приложения одновременно работают с одной базой: конкурентный доступ, изоляция, блокировки. Следом обычно дают сценарий «два запроса одновременно списывают деньги с одного счёта» и просят его разобрать.
Как отвечать
Разделите вопрос на две стороны. Со стороны Java:
- соединения: JDBC
Connectionне потокобезопасен, каждый поток берёт своё из пула. В Spring Boot пул по умолчанию HikariCP сmaximumPoolSize10. Больше соединений не значит быстрее, база упирается в ядра и диск; - транзакции:
@Transactionalпривязывает соединение к потоку,EntityManagerтоже нельзя делить между потоками; synchronized: работает внутри одной JVM, а приложение обычно запущено в нескольких экземплярах.
Со стороны базы:
- MVCC: в PostgreSQL читатели не блокируют писателей, каждая транзакция видит свой снимок данных;
- уровни изоляции: Read Committed по умолчанию в PostgreSQL, Repeatable Read в MySQL InnoDB. Чем выше уровень, тем меньше аномалий: грязное чтение, неповторяемое чтение, фантомы, аномалии сериализации;
- пессимистичная блокировка:
SELECT ... FOR UPDATE, для очередей задачFOR UPDATE SKIP LOCKED, чтобы воркеры не ждали друг друга; - оптимистичная блокировка: поле
@Versionв JPA, конфликт приходит какOptimisticLockException, операцию повторяют; - атомарный апдейт:
UPDATE account SET balance = balance - ? WHERE id = ? AND balance >= ?часто решает задачу без явных блокировок.
Дедлоки база находит сама и откатывает одну из транзакций, в PostgreSQL с кодом 40P01, сбой сериализации приходит с кодом 40001. Код должен уметь повторить транзакцию, а блокировки брать в одном порядке.
Где спотыкаются
Считают, что Read Committed защищает от потерянного обновления. Два потока читают баланс, оба вычитают и записывают, одно списание теряется.
Держат транзакцию открытой во время внешнего HTTP-вызова. Соединение занято, блокировки висят, пул заканчивается.
Путают гонку в памяти JVM с аномалиями транзакций и лечат вторые через volatile и synchronized.