Следует ли зафиксировать или откатить транзакцию чтения? - PullRequest
88 голосов
/ 21 ноября 2008

У меня есть запрос на чтение, который я выполняю в транзакции, чтобы я мог указать уровень изоляции. Что делать после завершения запроса?

  • Совершить транзакцию
  • Откат транзакции
  • Ничего не делать (что приведет к откату транзакции в конце блока 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?
    }
}

РЕДАКТИРОВАТЬ: Вопрос не в том, следует ли использовать транзакцию или есть другие способы установить уровень транзакции. Вопрос в том, имеет ли какое-либо значение то, что транзакция, которая ничего не изменяет, фиксируется или откатывается. Есть ли разница в производительности? Влияет ли это на другие соединения? Есть другие отличия?

Ответы [ 12 ]

50 голосов
/ 22 ноября 2008

Вы совершаете. Период. Там нет другой разумной альтернативы. Если вы начали транзакцию, вы должны закрыть ее. Фиксация освобождает любые блокировки, которые у вас могли быть, и в равной степени чувствительна к уровням изоляции ReadUncommitted или Serializable. Полагаться на неявный откат - хотя, возможно, и технически эквивалентный - это просто плохая форма.

Если это вас не убедило, просто представьте себе следующего парня, который вставляет оператор обновления в середину вашего кода и должен отследить неявный откат, который происходит, и удалить его данные.

25 голосов
/ 21 ноября 2008

Если вы ничего не изменили, вы можете использовать либо COMMIT, либо ROLLBACK. Любой из них снимет любые блокировки чтения, которые вы приобрели, и поскольку вы не внесли никаких других изменений, они будут эквивалентны.

6 голосов
/ 21 ноября 2008

Если вы начинаете транзакцию, то лучше всего всегда совершать ее. Если в вашем блоке использования (транзакции) возникнет исключение, транзакция будет автоматически отменена.

3 голосов
/ 05 мая 2017

Рассмотрим вложенные транзакции .

Большинство РСУБД не поддерживают вложенные транзакции или пытаются эмулировать их очень ограниченным образом.

Например, в MS SQL Server откат во внутренней транзакции (которая не является реальной транзакцией, MS SQL Server просто считает уровни транзакций!) Откатит все, что произошло в outmost транзакция (которая является реальной транзакцией).

Некоторые оболочки баз данных могут рассматривать откат во внутренней транзакции как признак того, что произошла ошибка, и откатить все в самой внешней транзакции, независимо от того, была ли совершена самая крайняя транзакция или откат.

Таким образом, COMMIT является безопасным способом, когда вы не можете исключить, что ваш компонент используется каким-либо программным модулем.

Обратите внимание, что это общий ответ на вопрос. Пример кода ловко обходит проблему с внешней транзакцией, открывая новое соединение с базой данных.

Относительно производительности: в зависимости от уровня изоляции для SELECT может потребоваться различная степень блокировок и временных данных (моментальных снимков). Это очищается, когда транзакция закрыта. Неважно, будет ли это сделано через COMMIT или ROLLBACK. Там может быть незначительная разница в затрачиваемом времени процессора - анализ, вероятно, быстрее, чем ROLLBACK (на два символа меньше) и другие незначительные различия. Очевидно, это верно только для операций только для чтения!

Полностью не требуется: другой программист, который может прочитать код, может предположить, что ROLLBACK подразумевает условие ошибки.

3 голосов
/ 25 марта 2009

ИМХО может иметь смысл обернуть запросы только для чтения в транзакции, поскольку (особенно в Java) вы можете указать транзакции как «только для чтения», что, в свою очередь, драйвер JDBC может рассмотреть возможность оптимизации запроса (но не обязан так что никто не помешает вам выдать INSERT тем не менее). Например. Драйвер Oracle будет полностью избегать блокировок таблиц в запросах в транзакции, помеченной только для чтения, что значительно повышает производительность приложений с интенсивным чтением.

2 голосов
/ 21 ноября 2008

Просто примечание, но вы также можете написать этот код так:

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 вверх.

1 голос
/ 08 сентября 2015

ROLLBACK чаще всего используется в случае ошибки или исключительных обстоятельств, а COMMIT в случае успешного завершения.

Мы должны закрывать транзакции с COMMIT (для успеха) и ROLLBACK (для сбоя), даже в случае транзакций только для чтения, где это, кажется, не имеет значения. На самом деле это имеет значение, для последовательности и будущего.

Транзакция только для чтения может логически «проваливаться» во многих отношениях, например:

  • запрос не возвращает ровно одну строку, как ожидалось
  • хранимая процедура вызывает исключение
  • извлеченные данные признаны несовместимыми
  • пользователь прерывает транзакцию, потому что она занимает слишком много времени
  • тупик или тайм-аут

Если COMMIT и ROLLBACK используются правильно для транзакции только для чтения, она продолжит работать должным образом, если в какой-то момент будет добавлен код записи в БД, например, для кэширования, аудита или статистики.

Неявный ROLLBACK должен использоваться только для ситуаций "фатальной ошибки", когда приложение аварийно завершает работу или завершается с неисправимой ошибкой, сбоем сети, отключением питания и т. Д.

1 голос
/ 22 ноября 2008

Если вы помещаете SQL в хранимую процедуру и добавляете это выше запроса:

set transaction isolation level read uncommitted

тогда вам не нужно прыгать через обручи в коде C #. Установка уровня изоляции транзакции в хранимой процедуре не приводит к тому, что этот параметр применяется ко всем будущим использованиям этого соединения (о чем вам следует беспокоиться при других настройках, поскольку соединения объединяются). В конце хранимой процедуры она просто возвращается к тому, с чем было инициализировано соединение.

0 голосов
/ 13 ноября 2017

Если для AutoCommit задано значение false, тогда ДА.

В эксперименте с JDBC (драйвер Postgresql) я обнаружил, что если запрос на выборку прерывается (из-за тайм-аута), то вы не можете инициировать новый запрос на выборку, пока не выполните откат.

0 голосов
/ 21 ноября 2008

В вашем примере кода, где у вас есть

  1. // Делаем что-нибудь полезное

    Выполняете ли вы оператор SQL, который изменяет данные?

Если нет, то нет такой вещи, как «чтение» транзакции ... только изменения из вставки, обновления и удаления операторов (операторов, которые могут изменить данные) находятся в транзакции ... То, о чем вы говорите, это блокирует, что SQL Server помещает данные, которые вы читаете, из-за ДРУГИХ транзакций, которые влияют на эти данные. Уровень этих блокировок зависит от уровня изоляции SQL Server.

Но вы не можете ничего зафиксировать или ROll Back, если ваш оператор SQL ничего не изменил.

Если вы меняете данные, то вы можете изменить уровень изоляции без явного запуска перехода ... Каждый отдельный оператор SQL неявно входит в транзакцию. явный запуск транзакции необходим только для того, чтобы убедиться, что 2 или более операторов находятся в одной транзакции.

Если все, что вы хотите сделать, это установить уровень изоляции транзакции, то просто установите для CommandText команды значение «Установить повторяемое чтение уровня изоляции транзакций» (или любой другой уровень, который вы хотите), установите для CommandType значение CommandType.Text и выполните команду команда. (вы можете использовать Command.ExecuteNonQuery ())

ПРИМЕЧАНИЕ. Если вы выполняете операторы чтения MULTIPLE и хотите, чтобы все они «видели» то же состояние базы данных, что и первый, вам необходимо установить уровень изоляции top Repeatable Read или Serializable ...

...