Должен ли я совершать или откатывать транзакцию чтения?

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

  • Зафиксировать транзакцию
  • Откат транзакции
  • Ничего не делать (что приведет к откату транзакции в конце используемого блока)

Каковы последствия каждого из них?

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: вопрос заключается не в том, следует ли использовать транзакцию или существуют другие способы установить уровень транзакции. Вопрос в том, имеет ли какая-либо разница, что транзакция, которая ничего не модифицирует, совершена или откатна. Есть ли разница в производительности? Это влияет на другие соединения? Любые другие отличия?

Ответ 1

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

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

Ответ 2

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

Ответ 3

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

Ответ 4

IMHO может иметь смысл обертывать только запросы на чтение в транзакциях (особенно на Java), вы можете сказать, что транзакция "доступна только для чтения", которая, в свою очередь, может использовать JDBC-драйвер для оптимизации запроса (но не имеет значения, поэтому никто не будет препятствовать вам выдавать INSERT тем не менее). Например. драйвер Oracle полностью избежит блокировок таблиц при запросах в транзакции, помеченной только для чтения, которая получает большую производительность при работе с сильно читаемыми приложениями.

Ответ 5

Просто примечание, но вы также можете написать этот код следующим образом:

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?
}

И если вы немного переструктурируете вещи, возможно, вы также сможете переместить блок использования для IDataReader вверх.

Ответ 6

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

set transaction isolation level read uncommitted

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

Ответ 7

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

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

транзакция только для чтения может логически "терпеть неудачу" разными способами, например:

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

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

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

Ответ 8

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

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

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

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

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

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

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

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

Ответ 9

Учитывая, что READ не меняет состояние, я ничего не сделал. Выполнение фиксации ничего не сделает, кроме отходов, которые отправляют запрос в базу данных. Вы не выполнили операцию, которая изменила состояние. Аналогично для отката.

Однако вы должны очистить свои объекты и закрыть свои подключения к базе данных. Не закрывать ваши соединения может привести к проблемам, если этот код вызывается повторно.

Ответ 10

Если вы установите AutoCommit false, тогда YES.

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

Ответ 11

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

  • //Сделайте что-нибудь полезное

    Выполняется ли инструкция SQL, которая изменяет данные?

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

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

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

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

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

Ответ 12

Вам нужно блокировать других от чтения одних и тех же данных? Зачем использовать транзакцию?

@Joel - мой вопрос будет лучше сформулирован как "Зачем использовать транзакцию для чтения?"

@Stefan. Если вы собираетесь использовать AdHoc SQL, а не хранимую процедуру, просто добавьте WITH (NOLOCK) после таблиц в запросе. Таким образом, вы не получаете накладных расходов (хотя и минимально) в приложении и базе данных для транзакции.

SELECT * FROM SomeTable WITH (NOLOCK)

EDIT @Comment 3: Поскольку у вас был "sqlserver" в тегах вопроса, я предположил, что MSSQLServer был целевым продуктом. Теперь, когда эта точка была выяснена, я отредактировал теги, чтобы удалить конкретную ссылку на продукт.

Я все еще не уверен в том, почему вы хотите совершить транзакцию по принципу чтения в первую очередь.