Odpowiedzi:
W przypadku programu SQL Server można argumentować, że operacja zatwierdzenia to nic innego jak zapisanie LOP_COMMIT_XACT w pliku dziennika i zwolnienie blokad, co oczywiście będzie szybsze niż ROLLBACK każdej akcji wykonanej przez Ciebie od czasu BEGIN TRAN.
Jeśli rozważasz każde działanie transakcji, a nie tylko zatwierdzenie, nadal twierdzę, że twoje oświadczenie nie jest prawdziwe. Wyłączając czynniki zewnętrzne, na przykład prędkość dysku dziennika w porównaniu do prędkości dysku danych, prawdopodobnie wycofanie jakiejkolwiek pracy wykonanej przez transakcję będzie szybsze niż wykonanie pracy w pierwszej kolejności.
Wycofanie polega na odczytaniu sekwencyjnego pliku zmian i zastosowaniu ich do stron danych w pamięci. Pierwotna „praca” musiała generować plan wykonania, pozyskiwać strony, łączyć wiersze itp.
Edycja: To zależy nieco ...
@JackDouglas wskazał na ten artykuł, który opisuje jedną z sytuacji, w których wycofywanie może potrwać znacznie dłużej niż pierwotna operacja. Przykładem jest 14-godzinna transakcja, nieuchronnie wykorzystująca równoległość, której cofnięcie zajmuje ponad 48 godzin, ponieważ wycofywanie jest w większości jednowątkowe. Najprawdopodobniej również wielokrotnie zmarnujesz pulę buforów, więc nie będziesz już cofać zmian na stronach w pamięci.
Tak więc poprawiona wersja mojej wcześniejszej odpowiedzi. O ile wolniej jest cofać? Biorąc wszystko pod uwagę, w przypadku typowej transakcji OLTP tak nie jest. Poza granicami typowego „cofnięcie” może potrwać dłużej niż „zrób”, ale (czy jest to potencjalne przekręcenie języka?) Dlaczego będzie zależeć od tego, jak to zrobiono.
Edycja2: Kontynuując dyskusję w komentarzach, oto bardzo wymyślny przykład, aby wykazać, że wykonywana praca jest głównym czynnikiem określającym względny koszt zatwierdzenia i wycofania jako operacji.
Utwórz dwie tabele i spakuj je nieefektywnie (marnowane miejsce na stronę):
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
Uruchom „złe” zapytanie o aktualizację, mierząc czas potrzebny do wykonania pracy i czas potrzebny na wydanie zatwierdzenia.
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
Zrób to jeszcze raz, ale wydaj i zmień wycofanie.
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
Z @ Rows = 1 uzyskuję dość spójny:
Przy @ wierszach = 100:
Przy @ wierszach = 1000:
Powrót do pierwotnego pytania. Jeśli mierzysz czas poświęcony na wykonanie pracy plus zatwierdzenie, wycofywanie jest bardzo przydatne, ponieważ większość tej pracy spędza na szukaniu wiersza do aktualizacji, a nie na modyfikowaniu danych. Jeśli patrzysz na operację zatwierdzenia w izolacji, powinno być jasne, że zatwierdzenie wykonuje bardzo małą „pracę” jako taką. Zatwierdzenie to „Gotowe”.
begin tran
tylko zwiększa licznik transakcji. Jeśli cię zrozumiałem, rdbms wykonuje wszystkie zadania (łączy wiersze, generuje plany wykonania ...) w COMMIT?
W przypadku Oracle cofnięcie może potrwać wiele razy dłużej niż czas potrzebny na cofnięcie zmian. To często nie ma znaczenia, ponieważ
W przypadku SQL Server nie jestem pewien, czy sytuacja jest taka sama, ale ktoś inny powie, jeśli nie jest ...
Jeśli chodzi o „dlaczego”, powiedziałbym, że rollback
powinno to być rzadkie , zwykle tylko wtedy, gdy coś poszło nie tak i oczywiście commit
może być znacznie częstsze - dlatego warto zoptymalizować pod kątemcommit
Cofanie nie jest po prostu „och, nieważne” - w wielu przypadkach naprawdę musi cofnąć to, co już zrobiono. Nie ma reguły, że operacja wycofywania zawsze będzie wolniejsza lub zawsze szybsza niż operacja pierwotna, chociaż nawet jeśli pierwotna transakcja przebiegała równolegle, wycofywanie jest jednowątkowe. Jeśli czekasz, sugeruję, że najbezpieczniej jest po prostu czekać.
Wszystko to zmienia się oczywiście wraz z SQL Server 2019 i przyspieszonym odzyskiwaniem bazy danych (co za zmienną karą pozwala na natychmiastowe wycofanie bez względu na rozmiar danych).
Nie wszystkie transakcje sprawią, że ich działania zatwierdzające będą działały znacznie lepiej niż ich wycofywanie. Jednym z takich przypadków jest operacja usuwania w SQL. Gdy transakcja usuwa wiersze, wiersze te są oznaczane jako rekordy duchów. Po wydaniu zatwierdzenia i uruchomieniu zadania czyszczenia rekordu widma tylko te rekordy są „usuwane”.
Jeśli zamiast tego wydano wycofanie, po prostu usuwa on znaki duchów z tych rekordów, a nie intensywne instrukcje wstawiania.
Nie wszyscy są. PostgreSQL nie potrzebuje więcej czasu na wycofanie, niż na zatwierdzenie, ponieważ dwie operacje są w rzeczywistości identyczne pod względem I / O dysku. Nie sądzę, że jest to kwestia optymalizacji pod kątem zatwierdzania, ponieważ jest to pytanie o to, do jakich innych zapytań optymalizuje się.
Podstawowym pytaniem jest, w jaki sposób rozwiązujesz układ na dysku i jak wpływa to na zatwierdzenie kontra wycofanie. Główne bazy danych, które wycofują się wolniej niż zatwierdzanie, mają tendencję do przenoszenia danych, szczególnie z tabel klastrowych, z głównych struktur danych i umieszczania ich w segmencie wycofywania podczas aktualizacji danych. Oznacza to, że aby zatwierdzić, po prostu upuszczasz segment wycofania, ale aby wycofać, musisz skopiować wszystkie dane z powrotem.
W przypadku PostgreSQL wszystkie tabele są tabelami sterty, a indeksy są oddzielne. Oznacza to, że podczas wycofywania lub zatwierdzania danych nie trzeba ponownie porządkować. To sprawia, że zatwierdzanie i wycofywanie jest szybkie.
Jednak sprawia, że niektóre inne rzeczy są nieco wolniejsze. Na przykład wyszukiwanie klucza podstawowego musi przejść przez plik indeksu, a następnie musi trafić do tabeli stosów (zakładając, że nie ma odpowiednich indeksów pokrywających). To nie jest wielka sprawa, ale dodaje dodatkowe wyszukiwanie strony, a może nawet kilka losowych wyszukiwania stron (jeśli w tym wierszu pojawiło się wiele aktualizacji), aby sprawdzić inne informacje i widoczność.
Szybkość tutaj nie jest jednak kwestią optymalizacji w PostgreSQL dla operacji zapisu względem operacji odczytu. Jest niechęć do uprzywilejowania niektórych operacji odczytu nad innymi. W konsekwencji PostgreSQL działa średnio tak dobrze, jak inne bazy danych. To tylko niektóre operacje, które mogą być szybsze lub wolniejsze.
Myślę więc, że faktyczna odpowiedź jest taka, że bazy danych są zoptymalizowane pod kątem określonych obciążeń po stronie odczytu, co prowadzi do problemów po stronie zapisu. Zazwyczaj tam, gdzie pojawia się pytanie, zatwierdzenia zwykle, choć nie zawsze, będą uprzywilejowane w stosunku do wycofań. Zależy to jednak od implikacji wykonania jednego z nich (aktualizacje różnią się od usuwania).