TL / DR: транзакции не по своей сути предотвращают все условия гонки. Вам по-прежнему нужны блокировка, обработка прерывания и повторной попытки или другие защитные меры во всех реальных реализациях баз данных. Транзакции не являются секретным соусом, который вы можете добавить к своим запросам, чтобы обезопасить их от всех эффектов параллелизма .
Изоляция
Что вы задаете своим вопросом - это I в КИСЛОТА - изоляция . Академически чистая идея заключается в том, что транзакции должны обеспечивать идеальную изоляцию, чтобы результат был таким же, как если бы каждая транзакция выполнялась последовательно. В действительности это редко имеет место в реальных реализациях RDBMS; возможности варьируются в зависимости от реализации, и правила могут быть ослаблены использованием более слабого уровня изоляции , например READ COMMITTED
. На практике вы не можете предполагать, что транзакции предотвращают все условия гонки , даже при SERIALIZABLE
изоляции.
Некоторые РСУБД обладают более сильными способностями, чем другие. Например, PostgreSQL 9.2 и новее имеют довольно хорошую SERIALIZABLE
изоляцию, которая обнаруживает большинство (но не все) возможных взаимодействий между транзакциями, а прерывает все, кроме одной из конфликтующих транзакций . Таким образом, он может безопасно выполнять транзакции параллельно.
Мало, если таковые имеются 3 , системы имеют действительно совершенную SERIALIZABLE
изоляцию, которая предотвращает все возможные расы и аномалии, включая такие проблемы, как эскалация блокировки и взаимоблокировка упорядочивания блокировки.
Даже при сильной изоляции некоторые системы (например, PostgreSQL) будут прерывать конфликтующие транзакции, вместо того, чтобы заставлять их ждать и запускать их последовательно. Ваше приложение должно помнить, что оно делало, и повторить попытку транзакции. Таким образом, хотя транзакция не позволяет сохранять аномалии, связанные с параллелизмом, в БД, это делается способом, который не прозрачен для приложения.
Атомарность
Возможно, основная цель транзакции базы данных заключается в том, что она обеспечивает атомарную фиксацию . Изменения не вступят в силу, пока вы не совершите транзакцию. Когда вы фиксируете, все изменения вступают в силу в тот же момент, что и другие транзакции. Ни одна транзакция не может увидеть только некоторые изменений, которые совершает транзакция 1,2 . Точно так же, если вы ROLLBACK
, то ни одно из изменений транзакции не будет замечено какой-либо другой транзакцией; как будто ваша транзакция никогда не существовала.
Это A в ACID .
Прочность
Еще одна долговечность - D в ACID . Он указывает, что когда вы совершаете транзакцию, она должна быть действительно сохранена в хранилище, которое переживет ошибку, такую как потеря питания или внезапная перезагрузка.
Консистенция:
См. Википедия
Оптимистичный контроль параллелизма
Вместо того, чтобы использовать блокировки и / или высокие уровни изоляции, для ORM, таких как Hibernate, EclipseLink и т. Д., Обычно используется оптимистический контроль параллелизма (часто называемый "оптимистической блокировкой") для преодолеть ограничения более слабых уровней изоляции при сохранении производительности.
Ключевой особенностью этого подхода является то, что он позволяет распределять работу между несколькими транзакциями, что является большим плюсом для систем с большим количеством пользователей и могут иметь длительные задержки между взаимодействиями с любым данным пользователем.
Ссылки
В дополнение к текстовым ссылкам см. главу документации PostgreSQL о блокировке, изоляции и параллелизме . Даже если вы используете другую СУБД, вы многое узнаете из концепций, которые она объясняет.
1 Я игнорирую редко используемый уровень изоляции READ UNCOMMITTED
здесь для простоты; это допускает грязное чтение.
2 Как указывает @meriton, следствие не обязательно верно. Фантомные чтения встречаются во всем, что ниже SERIALIZABLE
. Одна часть незавершенной транзакции не видит некоторые изменения (транзакцией, еще не совершенной), затем следующая часть незавершенной транзакции видит изменения, когда другая транзакция фиксирует .
3 Что ж, IIRC SQLite2 делает это благодаря блокировке всей базы данных при попытке записи, но это не то, что я бы назвал идеальным решением проблем параллелизма.