Ответы:
Для 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 я получаю разумную последовательность:
С @ Rows = 100:
С @ Rows = 1000:
Вернуться к исходному вопросу. Если вы измеряете время, затраченное на выполнение работы плюс фиксация, откат выигрывает руки, потому что большая часть этой работы уходит на поиск строки для обновления, а не на изменение данных. Если вы смотрите на операцию фиксации в отдельности, должно быть ясно, что фиксация делает очень мало «работы» как таковой. Совершить это «Я сделал».
begin tran
просто увеличивает счетчик транзакций. Если я вас понял, rdbms выполняет все задачи (объединяет строки, генерирует планы выполнения ...) в COMMIT?
Для Oracle откат может занять много раз больше времени, чем откат изменений. Это часто не имеет значения, потому что
Для SQL Server я не уверен, что ситуация такая же, но кто-то еще скажет, если это не так ...
Что касается «почему», я бы сказал, что это rollback
должно быть редким , обычно только если что-то пошло не так, и, конечно commit
, вероятно, будет гораздо более распространенным - так что имеет смысл оптимизировать дляcommit
Откат не просто «о, неважно» - во многих случаях он действительно должен отменить то, что он уже сделал. Нет правила, что операция отката всегда будет медленнее или всегда быстрее, чем исходная операция, хотя даже если исходная транзакция выполнялась параллельно, откат однопоточный. Если вы ждете, я полагаю, что безопаснее просто продолжать ждать.
Все это меняется с SQL Server 2019 и, конечно, с ускоренным восстановлением базы данных (которое, с учетом штрафа, которое также является переменным, обеспечивает мгновенный откат независимо от размера данных).
Не у всех транзакций активность коммитов будет намного лучше, чем у отката. Одним из таких случаев является операция удаления в SQL. Когда транзакция удаляет строки, эти строки помечаются как призрачные записи. Как только коммит выдан и запускается задача очистки записи-призрака, только эти записи «удаляются».
Если вместо этого был выполнен откат, он просто удаляет метки-призраки из этих записей, а не интенсивные операторы вставки.
Не все такие. Для отката PostgreSQL требуется не больше времени, чем для фиксации, поскольку две операции фактически идентичны с точки зрения дискового ввода-вывода. На самом деле я не думаю, что это вопрос оптимизации для коммитов, а вопрос того, для каких других запросов оптимизируется один.
Основной вопрос заключается в том, как вы обращаетесь к макету на диске и как это влияет на коммит против отката. Основные БД, которые откатываются медленнее, чем фиксация, как правило, перемещают данные, особенно из кластерных таблиц, из основных структур данных и помещают их в сегмент отката при обновлении данных. Это означает, что для фиксации вы просто отбрасываете сегмент отката, но для отката вы должны скопировать все данные обратно.
Для PostgreSQL все таблицы являются таблицами кучи, а индексы являются отдельными. Это означает, что при откате или фиксации никакие данные не должны переупорядочиваться. Это делает коммит и откат быстрыми.
Тем не менее, это делает некоторые другие вещи немного медленнее. Например, поиск первичного ключа должен пройти через индексный файл, а затем он должен попасть в таблицу кучи (при условии, что не существует применимых индексов покрытия). Это не такая уж большая проблема, но она добавляет дополнительный поиск страниц или, возможно, даже несколько случайных поисков страниц (если в этой строке произошло много обновлений), чтобы проверить другую информацию и видимость.
Скорость здесь, однако, не является вопросом оптимизации в PostgreSQL для операций записи по сравнению с операциями чтения. Нежелание отдавать привилегии некоторым операциям чтения выше других. Следовательно, PostgreSQL работает в среднем примерно так же, как и другие базы данных. Это просто определенные операции, которые могут быть быстрее или медленнее.
Поэтому я думаю, что реальный ответ заключается в том, что базы данных оптимизированы для определенных рабочих нагрузок на стороне чтения, и это приводит к проблемам на стороне записи. Обычно, когда возникает вопрос, коммиты, хотя и не всегда, предпочтительнее откатов. Это, однако, зависит от последствий выполнения любого из них (обновления отличаются от удалений).