В программирование есть вещи, о которых, в среднем, все помнят. Ведь это именно те вопросы, за которые спрашивают на собеседованиях. Именно это мы штудируем перед сном. Но увы, жизнь такова, что даже в прописных истинах, порой, мы ошибаемся. Уровни изоляции транзакций — как раз таки одна из таких тем.
Давайте совсем коротенечко вспомним, что такое Transaction isolation levels, и с чем его едят. Данный инструмент — это, на самом деле, просто одна из настроек создаваемых вами транзакций. В теории, у этой настройки существует 4 разных значения (именно так написано в SQL-стандарте):
Подробно про уровни изоляции читаем на оф. сайте — https://www.postgresql.org/docs/current/transaction-iso.html, а также на русском — https://postgrespro.ru/docs/postgrespro/9.5/transaction-iso.
В настройке Уровней Изоляций есть одна важная вещь. Каждое из значений isolation level описывает самое негативно возможное поведение. То есть, разработчики Баз данных вполне вправе реализовать только самую строгую из настроек (Serializable ), так как она полностью покрывает все кейсы.
Например, можно посмотреть на PostgreSQL. Эта база данных НЕ реализует Read Uncommitted — при этой настройке ваша база данных будет вести, как будто бы это Read Committed. Данная особенность возникла из-за MVCC.
Давайте вернемся к нашим баранам. Как можно накосячить с Уровнями изоляции тразакций? Есть несколько способов.
Вам всегда нужно знать, какой уровень установлен в вашем приложение. И это действительно проблема. Ведь есть несколько значений по умолчанию, причем в каждом слое вашей системы это значение может отличаться. Типа, в PostgreSQL — это Read Committed. В условном JDBC это МОЖЕТ быть что-то другое, а в проклятом ORM — третье. Поэтому лучше явно указывать, какой уровень изоляции вы ожидаете, не надеясь на дефолты.
Почему это важно? Думаю, и так всем понятно. При условном Read committed вы можете сохранить в БД данные, которые на самом деле уже были удалены в параллельной транзакции (то есть, первая транзакция читает сущность из БД, вторая транзакция удаляет эту сущность из БД, и первая транзакция, обновив что-то в сущности, обновляет сущность в БД, которая уже была удалена). А при Serializable вы вообще можете начать получать ошибки при коммите транзакции. Например, если вы пытаетесь вставить две сущности в параллельных транзакциях, каждая из которых зависит от одной и той же старой сущности. В этом случае вы увидите что-то типа:
Caused by: org.postgresql.util.PSQLException: ERROR: could not serialize access due to read/write dependencies among transactions Detail: Reason code: Canceled on identification as a pivot, during commit attempt. Hint: The transaction might succeed if retried.
В общем, с уровнями изоляции стоит дружить. Ведь это крайне мощный инструмент, который может, как супер сильно помочь, ускорив ваше приложение, так и поломать вам бизнес логику, если вы поленитесь почитать описание, или неверно настроите эту опцию.
Категории: Программирование