Должен ли я зафиксировать или откатить транзакцию чтения?
У меня есть запрос на чтение, который я выполняю в транзакции, чтобы я мог указать уровень изоляции. Как только запрос будет завершен, что мне делать?
- совершение сделки
- откат транзакции
- ничего не делать (что приведет к откату транзакции в конце блока using)
каковы последствия каждого из них?
using (IDbConnection connection = ConnectionFactory.CreateConnection())
{
using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
{
using (IDbCommand command = connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandText = "SELECT * FROM SomeTable";
using (IDataReader reader = command.ExecuteReader())
{
// Read the results
}
}
// To commit, or not to commit?
}
}
EDIT: вопрос не в том, если транзакция следует использовать или если есть другие способы установить уровень транзакции. Вопрос в том, имеет ли значение, что транзакция, которая ничего не изменяет, фиксируется или откатывается. Есть ли разница в производительности? Влияет ли это на другие соединения? Есть еще отличия?
12 ответов:
вы совершаете. Период. Другой разумной альтернативы нет. Если вы начали транзакцию, вы должны ее закрыть. Фиксация освобождает любые блокировки, которые у вас могли быть, и одинаково разумна с уровнями изоляции ReadUncommitted или Serializable. Полагаться на неявный откат - хотя, возможно, технически эквивалентно-это просто плохая форма.
Если это вас не убедило, просто представьте себе следующего парня, который вставляет инструкцию update в середину вашего кода и должен отслеживать неявный откат, который происходит и удаляет его данные.
Если вы ничего не изменили, то вы можете использовать либо фиксацию, либо откат. Любой из них освободит любые блокировки чтения, которые вы приобрели, и поскольку вы не внесли никаких других изменений, они будут эквивалентны.
Если вы начинаете транзакцию,то лучше всего всегда фиксировать ее. Если внутри вашего блока use(transaction) возникнет исключение, транзакция будет автоматически откатана.
IMHO может иметь смысл обернуть запросы только для чтения в транзакциях, поскольку (особенно в Java) вы можете сказать, что транзакция будет "только для чтения", что, в свою очередь, драйвер JDBC может рассмотреть возможность оптимизации запроса (но не обязательно, поэтому никто не помешает вам выдать
INSERTтем не менее). Например, драйвер Oracle полностью избегает блокировки таблиц на запросах в транзакции, помеченной как "только для чтения", что обеспечивает высокую производительность в приложениях с интенсивным чтением.
просто примечание, но вы также можете написать этот код следующим образом:
using (IDbConnection connection = ConnectionFactory.CreateConnection()) using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted)) using (IDbCommand command = connection.CreateCommand()) { command.Transaction = transaction; command.CommandText = "SELECT * FROM SomeTable"; using (IDataReader reader = command.ExecuteReader()) { // Do something useful } // To commit, or not to commit? }и если вы немного перестроите вещи, вы можете также переместить блок using для IDataReader наверх.
считают вложенные транзакции.
большинство СУБД не поддерживают вложенные транзакции или пытаются эмулировать их очень ограниченным способом.
например, в MS SQL Server откат во внутренней транзакции (которая не является реальной транзакцией, MS SQL Server просто подсчитывает уровни транзакций!) откатит все, что произошло в outmost операции (которая является реальной сделки).
некоторые обертки базы данных откат во внутренней транзакции может рассматриваться как признак того, что произошла ошибка, и откатывать все в самой внешней транзакции, независимо от того, была ли совершена самая внешняя транзакция или откат.
таким образом, фиксация является безопасным способом, когда вы не можете исключить, что ваш компонент используется каким-либо программным модулем.
обратите внимание, что это общий ответ на вопрос. Пример кода умело работает вокруг проблемы с внешней транзакцией, открывая новую подключение базы данных.
Что касается производительности: в зависимости от уровня изоляции, выбор может потребовать различной степени блокировки и временных данных (снимков). Это очищается при закрытии транзакции. Не имеет значения, делается ли это с помощью фиксации или отката. Может быть незначительная разница в затрачиваемом времени процессора-фиксация, вероятно, быстрее разбирается, чем откат (на два символа меньше) и другие незначительные различия. Очевидно, что это справедливо только для чтения операции!
совершенно не требуется: другой программист, который может прочитать код, может предположить, что откат подразумевает условие ошибки.
Если вы помещаете SQL в хранимую процедуру и добавляете это над запросом:
set transaction isolation level read uncommittedтогда вам не нужно прыгать через какие-либо обручи в коде C#. Установка уровня изоляции транзакций в хранимой процедуре не приводит к тому, что этот параметр применяется ко всем будущим видам использования этого соединения (что вы должны беспокоиться о других параметрах, так как соединения объединены в пул). В конце хранимой процедуры он просто возвращается к тому, что было соединением инициализируется С.
откат в основном используется в случае ошибки или исключительных обстоятельств, а фиксация-в случае успешного завершения.
мы должны закрыть транзакции с фиксацией (для успеха) и откатом (для неудачи), даже в случае транзакций только для чтения, где это, похоже, не имеет значения. На самом деле это важно, согласованность и перспективность.
транзакция только для чтения может логически "отказать" во многих отношениях, например:
- запрос делает не возвращать ровно одну строку, как ожидалось
- хранимая процедура вызывает исключение
- полученные данные оказываются несогласованными
- пользователь прерывает транзакцию, потому что это занимает слишком много времени
- тупик или тайм-аут
Если COMMIT и ROLLBACK используются правильно для транзакции только для чтения, она будет продолжать работать так, как ожидалось, если в какой-то момент будет добавлен код записи DB, например, для кэширования, аудита или статистика.
неявный откат должен использоваться только для ситуаций "фатальной ошибки", когда приложение завершает работу или завершает работу с неустранимой ошибкой, сбоем сети, сбоем питания и т. д.
учитывая, что чтение не изменяет состояние, я бы ничего не сделал. Выполнение фиксации ничего не сделает, кроме потери цикла для отправки запроса в базу данных. Вы не выполнили операцию, которая изменила состояние. Аналогично и для отката.
однако вы должны обязательно очистить свои объекты и закрыть свои соединения с базой данных. Не закрытие соединений может привести к проблемам, если этот код вызывается повторно.
Если вы установили AutoCommit false, то да.
в эксперименте с JDBC (драйвер Postgresql) я обнаружил, что если select query ломается(из-за таймаута), то вы не можете инициировать новый запрос select, если вы не откат.
в вашем примере кода, где у вас есть
/ / сделай что-нибудь полезное
вы выполняете инструкцию SQL, которая изменяет данные ?
Если нет, то нет такой вещи, как "чтение" транзакции... В транзакции содержатся только изменения из инструкций Insert, Update и Delete (которые могут изменять данные)... Вы говорите о блокировках, которые SQL Server накладывает на данные, которые Вы читаете, из-за других операции, которые влияют на эти данные. Уровень этих блокировок зависит от уровня изоляции SQL Server.
но вы не можете зафиксировать или откатить что-либо, если ваш оператор SQL ничего не изменил.
Если вы меняете данные, то вы можете изменить уровень изоляции без явного запуска транзакции... Каждый отдельный оператор SQL неявно входит в транзакцию. явно запуск транзакции необходим только для того, чтобы гарантировать, что 2 или более операторы находятся в пределах одной транзакции.
Если все, что вы хотите сделать, это установить уровень изоляции транзакций, а затем просто установить CommandText команды в "установить уровень изоляции транзакций Repeatable Read" (или любой другой уровень, который вы хотите), установите CommandType в CommandType.Текст, и выполнить команду. (вы можете использовать команду.ExecuteNonQuery ())
Примечание: Если вы делаете несколько операторов чтения и хотите, чтобы все они "видели" то же состояние базы данных, что и первый во-первых, тогда вам нужно установить верхний уровень изоляции повторимое чтение или Сериализуемое...
нужно ли блокировать чтение одних и тех же данных другими? Зачем использовать транзакцию?
@Joel-мой вопрос лучше сформулировать так: "зачем использовать транзакцию для запроса чтения?"
@Stefan-если вы собираетесь использовать adhoc SQL, а не сохраненный proc, то просто добавьте WITH (NOLOCK) после таблиц в запросе. Таким образом, вы не несете накладные расходы (хотя и минимальные) в приложении и базе данных для транзакции.
SELECT * FROM SomeTable WITH (NOLOCK)EDIT @ Comment 3: Поскольку у вас был "sqlserver" в тегах вопросов, я предположил, что mssqlserver является целевым продуктом. Теперь, когда этот момент был прояснен, я отредактировал теги, чтобы удалить конкретную ссылку на продукт.
Я все еще не уверен, почему вы хотите сделать транзакцию на чтение op в первую очередь.
Comments