ROLLBACK - быстрая операция?

Ответы:

14

Для SQL Server вы можете утверждать, что операция фиксации - это не что иное, как запись LOP_COMMIT_XACT в файл журнала и снятие блокировок, что, конечно, будет быстрее, чем ROLLBACK каждого действия, выполненного вашей транзакцией после BEGIN TRAN.

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

Откат читает последовательный файл изменений и применяет их к страницам данных в памяти. Исходная «работа» должна была генерировать план выполнения, приобретать страницы, соединять строки и т. Д.

Изменить: Это зависит немного ...

@JackDouglas указал на эту статью которая описывает одну из ситуаций, когда откат может занять значительно больше времени, чем исходная операция. Примером является 14-часовая транзакция, неизбежно использующая параллелизм, для отката которой требуется более 48 часов, поскольку откат в основном однопоточный. Скорее всего, вам также придется многократно изменять объем пула буферов, поэтому вы больше не отменяете изменения страниц в памяти.

Итак, пересмотренный вариант моего более раннего ответа. Насколько медленнее откат? Учитывая все остальные обстоятельства, для типичной транзакции OLTP это не так. За пределами обычного, это может занять больше времени, чтобы «отменить», чем «сделать», но (это потенциальный скороговорка?), Почему будет зависеть от того, как было «сделать».

Редактировать 2: Следуя обсуждению в комментариях, вот очень надуманный пример, демонстрирующий, что выполняемая работа является основным фактором, определяющим относительную стоимость фиксации и отката как операций.

Создайте две таблицы и упакуйте их неэффективно (потерянное место на странице):

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
SET NOCOUNT ON;
GO

CREATE TABLE dbo.Foo
(
    col1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , col2 CHAR(4000) NOT NULL DEFAULT REPLICATE('A', 4000)
)

CREATE TABLE dbo.Bar
(
    col1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , col2 CHAR(4000) NOT NULL DEFAULT REPLICATE('A', 4000)
)
GO

INSERT dbo.Foo DEFAULT VALUES
GO 100000

INSERT dbo.Bar DEFAULT VALUES
GO 100000

Запустите «неверный» запрос на обновление, измеряя время, затраченное на выполнение работы, и время, необходимое для выдачи коммита.

DECLARE 
    @StartTime DATETIME2
    , @Rows INT

SET @Rows = 1

CHECKPOINT
DBCC DROPCLEANBUFFERS

BEGIN TRANSACTION

SET @StartTime = SYSDATETIME()

UPDATE
    dbo.bar
SET
    col2 = REPLICATE('B', 4000)
FROM
    dbo.bar b
INNER JOIN
    (
    SELECT TOP(@Rows)
        col1
    FROM
        dbo.foo
    ORDER BY
        NEWID()
    ) f
ON  f.col1 = b.col1
OPTION (MAXDOP 1)

SELECT 'Find and update row', DATEDIFF(ms, @StartTime, SYSDATETIME())

SET @StartTime = SYSDATETIME()

COMMIT TRANSACTION

SELECT 'Commit', DATEDIFF(ms, @StartTime, SYSDATETIME())
GO

Сделайте то же самое снова, но выпустите и измерьте откат.

    DECLARE 
    @StartTime DATETIME2
    , @Rows INT

SET @Rows = 1

CHECKPOINT
DBCC DROPCLEANBUFFERS

BEGIN TRANSACTION

SET @StartTime = SYSDATETIME()

UPDATE
    dbo.bar
SET
    col2 = REPLICATE('B', 4000)
FROM
    dbo.bar b
INNER JOIN
    (
    SELECT TOP(@Rows)
        col1
    FROM
        dbo.foo
    ORDER BY
        NEWID()
    ) f
ON  f.col1 = b.col1
OPTION (MAXDOP 1)

SELECT 'Find and update row', DATEDIFF(ms, @StartTime, SYSDATETIME())

SET @StartTime = SYSDATETIME()

ROLLBACK TRANSACTION

SELECT 'Rollback', DATEDIFF(ms, @StartTime, SYSDATETIME())
GO

С @ Rows = 1 я получаю разумную последовательность:

  • 5500мс для поиска / обновления
  • 3ms коммит
  • Откат 1 мс

С @ Rows = 100:

  • 8500мс найти / обновить
  • 15мс коммит
  • Откат 15мс

С @ Rows = 1000:

  • 15000 мс найти / обновить
  • 10мс коммит
  • Откат 500 мс

Вернуться к исходному вопросу. Если вы измеряете время, затраченное на выполнение работы плюс фиксация, откат выигрывает руки, потому что большая часть этой работы уходит на поиск строки для обновления, а не на изменение данных. Если вы смотрите на операцию фиксации в отдельности, должно быть ясно, что фиксация делает очень мало «работы» как таковой. Совершить это «Я сделал».

Марк Стори-Смит
источник
2
«меньше работы» не обязательно «быстрее»
Джек Дуглас
Я знал, что begin tranпросто увеличивает счетчик транзакций. Если я вас понял, rdbms выполняет все задачи (объединяет строки, генерирует планы выполнения ...) в COMMIT?
Гарик
3
Нет, вся работа выполняется перед коммитом. Сама операция фиксации делает сравнительно мало.
Марк Стори-Смит
@Mark. Я провел несколько грубых и готовых тестов, вставляя 2-метровые строки и фиксируя их или откатываясь. Общее время, включая откат, варьировалось от 10 до 30 секунд, в то время как общее время, включая фиксацию, составляло от 6 до 14 секунд. YMMV, конечно, но это указывает на то, что откат приблизителен к длине или длине, по сравнению с исходной транзакцией, по крайней мере, в моей среде.
Джек Дуглас
2
Если бы вы измерили время для завершения операции фиксации, я бы ожидал, что оно будет минимальным, если контрольная точка не будет выдана в одно и то же время (которое является отдельным и не связанным). С моей точки зрения, фиксация делает очень мало, тогда как откат делает все, что произошло до фиксации, плюс немного больше. Разница в ваших тестах предполагает наличие других факторов, но я, конечно, постараюсь собрать несколько сценариев позже.
Марк Стори-Смит
13

Для Oracle откат может занять много раз больше времени, чем откат изменений. Это часто не имеет значения, потому что

  1. При откате транзакции блокировки не удерживаются
  2. Он обрабатывается фоновым процессом с низким приоритетом

Для SQL Server я не уверен, что ситуация такая же, но кто-то еще скажет, если это не так ...

Что касается «почему», я бы сказал, что это rollbackдолжно быть редким , обычно только если что-то пошло не так, и, конечно commit, вероятно, будет гораздо более распространенным - так что имеет смысл оптимизировать дляcommit

Джек Дуглас
источник
9

Откат не просто «о, неважно» - во многих случаях он действительно должен отменить то, что он уже сделал. Нет правила, что операция отката всегда будет медленнее или всегда быстрее, чем исходная операция, хотя даже если исходная транзакция выполнялась параллельно, откат однопоточный. Если вы ждете, я полагаю, что безопаснее просто продолжать ждать.

Все это меняется с SQL Server 2019 и, конечно, с ускоренным восстановлением базы данных (которое, с учетом штрафа, которое также является переменным, обеспечивает мгновенный откат независимо от размера данных).

Аарон Бертран
источник
2
И у всех нас был разговор о том, что «требуется откат, давай перезагрузим его» в какой-то момент, верно?
Марк Стори-Смит
Я видел, как многие клиенты это делают. Некоторые выходят относительно невредимыми, другие гораздо менее удачливы.
Аарон Бертран
1
@ MarkStorey-Smith - Если вы перезагружаете промежуточный откат, разве SQL Server не должен продолжать откат при запуске?
Ник Чаммас
2
@Ник, который зависит - например, если откат был заблокирован до перезагрузки, он может вести себя намного быстрее после перезапуска службы, потому что этот другой процесс был просто убит. В этом сценарии очень много «что, если» - каждый раз, когда вы перезагружаете сервер или перезапускаете сервис, чтобы «исправить» проблему, возможно, есть еще более серьезные проблемы.
Аарон Бертран
2
@ Ник, да, именно так и происходит. Мой комментарий должен был быть «языком в щеке», настолько, что вам неизбежно придется объяснять это счастливым людям, которые хотят ударить, перезагружаться, когда что-то идет не так, как ожидалось.
Марк Стори-Смит
8

Не у всех транзакций активность коммитов будет намного лучше, чем у отката. Одним из таких случаев является операция удаления в SQL. Когда транзакция удаляет строки, эти строки помечаются как призрачные записи. Как только коммит выдан и запускается задача очистки записи-призрака, только эти записи «удаляются».

Если вместо этого был выполнен откат, он просто удаляет метки-призраки из этих записей, а не интенсивные операторы вставки.

StanleyJohns
источник
Хороший пример того, как определенные операции оптимизированы для отката.
Марк Стори-Смит
5

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

Основной вопрос заключается в том, как вы обращаетесь к макету на диске и как это влияет на коммит против отката. Основные БД, которые откатываются медленнее, чем фиксация, как правило, перемещают данные, особенно из кластерных таблиц, из основных структур данных и помещают их в сегмент отката при обновлении данных. Это означает, что для фиксации вы просто отбрасываете сегмент отката, но для отката вы должны скопировать все данные обратно.

Для PostgreSQL все таблицы являются таблицами кучи, а индексы являются отдельными. Это означает, что при откате или фиксации никакие данные не должны переупорядочиваться. Это делает коммит и откат быстрыми.

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

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

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

Крис Траверс
источник
Хороший ответ, но одна небольшая оговорка: «Для PostgreSQL все таблицы являются таблицами кучи, а индексы являются отдельными. Это означает, что при откате или фиксации не требуется перестановка данных», это не причина, по которой данные не должны быть реорганизованным, скорее потому, что «основные БД, которые откатываются медленнее, чем коммиты, как правило, перемещают данные», а pg нет, как вы упомянули. По умолчанию Oracle также использует кучи-хранилище: основное отличие заключается в том, что Oracle использует «отмену» и освобождает все пространство при фиксации / откате, а не по «вакуумному» маршруту.
Джек Дуглас