Відповіді:
Для SQL Server ви можете стверджувати, що операція фіксації - це не що інше, як запис LOP_COMMIT_XACT у файл журналу та звільнення блокування, що, звичайно, буде швидше, ніж ROLLBACK кожної дії, здійсненої вами транзакції з BEGIN TRAN.
Якщо ви розглядаєте кожну дію транзакції, а не лише вчинення, я все-таки стверджую, що ваше твердження не відповідає дійсності. Виключаючи зовнішні фактори, наприклад швидкість диска журналу порівняно зі швидкістю диска даних, швидше за все, відкат будь-якої роботи, виконаної транзакцією, буде швидше, ніж виконання роботи в першу чергу.
Відкат - це зчитування послідовного файлу змін та застосування їх до сторінок даних в пам'яті. Початковий "твір" повинен був створити план виконання, придбати сторінки, об'єднати рядки тощо.
Редагувати: це залежить трохи ...
@JackDouglas вказав на цю статтю, яка описує одну з ситуацій, коли відкат може зайняти значно більше часу, ніж оригінальний процес. Прикладом є 14-годинна транзакція, яка неминуче використовує паралелізм, який вимагає 48+ годин для відкату, оскільки відкат - це переважно однопоточна передача. Ви, швидше за все, також неодноразово відбиваєте буферний пул, тому більше не обертаєте зміни на сторінках пам'яті.
Отже, переглянута версія моєї попередньої відповіді. На скільки повільніше відкат? Всі інші речі, що стосуються типової трансакції OLTP, це не так. Поза типовими межами може знадобитися більше часу, ніж "скасувати", ніж "зробити", але (це потенційний твістер язика?), Чому це буде залежати від того, як було зроблено "робити".
Edit2: Виходячи з обговорення в коментарях, ось дуже надуманий приклад, який демонструє, що виконувана робота є головним фактором для визначення відносних витрат комісії проти відкату як операцій.
Створіть дві таблиці та упакуйте їх неефективно (витрачений простір на сторінку):
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 я отримую досить послідовний:
З @ рядками = 100:
З @ рядками = 1000:
Повернутися до початкового питання. Якщо ви вимірюєте час, витрачений на роботу, а також фіксацію, відкат виграє руки, оскільки більшість цієї роботи витрачається на пошук рядка для оновлення, а не на фактичну зміну даних. Якщо ви дивитесь на операцію фіксації поодиноко, то повинно бути зрозуміло, що фіксація дуже мало "працює" як така. Коміт - це "я закінчив".
begin tran
просто збільшує лічильник транзакцій. Якщо я вас зрозумів, rdbms виконує всі завдання (об'єднує рядки, генерує плани виконання ...) в COMMIT?
Для Oracle відкат може зайняти в багато разів більше часу, який знадобився для внесення змін, які повертаються назад. Це часто не має значення, оскільки
Для SQL Server я не впевнений, чи ситуація така, але хтось скаже, якщо це не так ...
Щодо "чому", я б сказав, що це rollback
повинно бути рідкісним , як правило, тільки якщо щось пішло не так, і, звичайно, commit
це буде набагато частіше - тому є сенс оптимізувати дляcommit
Відкат - це не просто "о, неважливо" - у багатьох випадках справді потрібно скасувати те, що вже було зроблено. Не існує правила, що операція відкату завжди буде повільнішою або завжди швидшою, ніж початкова операція, хоча навіть якщо оригінальна транзакція проходила паралельно, відкат є однопоточним. Якщо ви чекаєте, я вважаю, що найбезпечніше просто продовжувати чекати.
Це все змінюється, звичайно, і SQL Server 2019, і прискореним відновленням бази даних (що при штрафі, який також є змінним, дозволяє миттєвий відкат незалежно від розміру даних).
Не всі транзакції дозволять виконувати свою діяльність набагато краще, ніж їх відкат. Один з таких випадків - операція видалення в SQL. Коли операція видаляє рядки, ці рядки позначаються як записи-привиди. Після видачі комісії та запуску завдання очищення записів привидів лише ці записи "видалено".
Якщо замість цього був виданий відкат, він просто видаляє позначення привидів з цих записів, а не оператори інтенсивного вставки.
Не всі є. PostgreSQL не потребує більше часу для того, щоб повернути назад, ніж здійснити, оскільки дві операції фактично однакові з точки зору вводу / виводу диска. Я насправді не думаю, що це питання настільки оптимізованого для здійснення зобов’язань, як питання про те, для яких інших запитів оптимізується.
Основне питання полягає в тому, як ви звертаєтесь до макета на диску і як це впливає на фіксування та відкат Основні ДБ, які повертаються повільніше, ніж здійснюють, мають тенденцію переміщувати дані, особливо з кластеризованих таблиць, з основних структур даних та розміщувати їх у сегменті відкату при оновленні даних. Це означає, що для того, щоб скористатись, ви просто відкинете сегмент відкату, але щоб повернути назад, вам потрібно скопіювати всі дані назад.
Для PostgreSQL всі таблиці є куповими таблицями, а індекси - окремими. Це означає, що під час відкочування або вчинення жодних даних не потрібно переробляти. Це робить прихильність і відкат обох швидко.
Однак деякі інші речі роблять трохи повільніше. Наприклад, пошук первинного ключа повинен пройти файл індексу, після чого він повинен потрапити на таблицю купи (якщо не застосовувати індекси, що застосовуються). Це не велика справа, але це додає додатковий пошук сторінки або, можливо, навіть декілька випадкових пошуку сторінок (якщо в цьому ряду було багато оновлень), щоб перевірити наявність іншої інформації та видимості.
Однак швидкість тут не є питанням оптимізації в PostgreSQL для операцій запису проти читання. Це небажання привілейовувати деякі операції з читання над іншими. Отже, PostgreSQL працює в середньому приблизно так само, як і інші db. Просто певні операції можуть бути швидшими або повільнішими.
Тому я думаю, що реальна відповідь полягає в тому, що базові дані оптимізовані для певних навантажень на стороні читання, і це призводить до проблем з боку запису. Зазвичай там, де виникає питання, комісії зазвичай, хоча і не завжди, віддають перевагу відмовам. Однак це залежить від наслідків виконання будь-якого (оновлення відрізняються від видалення).