Czy funkcja ROLLBACK jest szybką operacją?

Odpowiedzi:

14

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:

  • 5500 ms na wyszukiwanie / aktualizację
  • Zatwierdź 3ms
  • Cofnięcie o 1 ms

Przy @ wierszach = 100:

  • 8500 ms znaleźć / zaktualizować
  • 15 ms zatwierdzenia
  • 15ms cofnięcie

Przy @ wierszach = 1000:

  • 15000 ms znaleźć / zaktualizować
  • Zatwierdzenie 10ms
  • Cofnięcie o 500 ms

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”.

Mark Storey-Smith
źródło
2
„mniej pracy” niekoniecznie jest „szybsze”
Jack Douglas
Wiedziałem, że begin trantylko zwiększa licznik transakcji. Jeśli cię zrozumiałem, rdbms wykonuje wszystkie zadania (łączy wiersze, generuje plany wykonania ...) w COMMIT?
garik
3
Nie, cała praca jest wykonywana przed zatwierdzeniem. Sama operacja zatwierdzenia robi stosunkowo niewiele.
Mark Storey-Smith
@Mark Zrobiłem kilka trudnych i gotowych testów wstawiając 2-metrowe rzędy i zatwierdzając lub wycofując. Całkowity czas, w tym wycofanie, wahał się od 10s do 30s, w porównaniu do 6s i 14s dla całkowitego czasu, w tym zatwierdzenia. YMMV oczywiście, ale oznacza to, że wycofanie ballpark jest prawie tak długie lub dłuższe niż pierwotna transakcja przynajmniej w moim środowisku.
Jack Douglas,
2
Jeśli miałbyś zmierzyć czas do zakończenia operacji zatwierdzenia, spodziewam się, że byłby minimalny, chyba że punkt kontrolny zostanie wydany w tym samym czasie (który jest osobny i niezwiązany). To w pewnym sensie moje zatwierdzenie, robi niewiele, podczas gdy wycofanie robi wszystko, co wydarzyło się przed zatwierdzeniem, i trochę więcej. Rozbieżność w testach przesądza o innych czynnikach, ale z pewnością postaram się później połączyć kilka skryptów.
Mark Storey-Smith
13

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ż

  1. Podczas wycofywania transakcji nie są blokowane żadne blokady
  2. Jest obsługiwany przez proces tła o niskim priorytecie

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 rollbackpowinno to być rzadkie , zwykle tylko wtedy, gdy coś poszło nie tak i oczywiście commitmoże być znacznie częstsze - dlatego warto zoptymalizować pod kątemcommit

Jack Douglas
źródło
9

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).

Aaron Bertrand
źródło
2
I wszyscy mieliśmy kiedyś rozmowę „cofanie się, zrestartujmy” w pewnym momencie, prawda?
Mark Storey-Smith
Widziałem, jak robi to wielu klientów. Niektóre wychodzą stosunkowo nietknięte, inne mają o wiele mniej szczęścia.
Aaron Bertrand
1
@ MarkStorey-Smith - Jeśli zrestartujesz się w trakcie przywracania, czy SQL Server i tak nie będzie musiał kontynuować przywracania podczas uruchamiania?
Nick Chammas,
2
@Nick to zależy - jeśli na przykład blokowanie przywracania zostało zablokowane przed ponownym uruchomieniem, może on działać znacznie szybciej po ponownym uruchomieniu usługi, ponieważ właśnie ten inny proces został właśnie zabity. W tym scenariuszu jest DUŻO „co, jeśli” - za każdym razem, gdy ponownie uruchomisz serwer lub ponownie uruchomisz usługę w celu „naprawienia” problemu, prawdopodobnie występują o wiele poważniejsze problemy.
Aaron Bertrand
2
@Nick, tak właśnie się dzieje. Mój komentarz miał być „języczkiem w policzek”, w takim stopniu, że nieuchronnie musisz wyjaśnić to wyzwalaczowi szczęśliwych ludzi, którzy chcą ponownie uruchomić komputer, gdy coś nie działa zgodnie z oczekiwaniami.
Mark Storey-Smith
8

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.

StanleyJohns
źródło
Dobry przykład optymalizacji niektórych operacji pod kątem wycofywania.
Mark Storey-Smith
5

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).

Chris Travers
źródło
Dobra odpowiedź, ale jedna drobna sprzeczka: „W przypadku PostgreSQL wszystkie tabele są tabelami sterty, a indeksy są osobne. Oznacza to, że przy wycofywaniu lub zatwierdzaniu nie trzeba ponownie ustawiać żadnych danych”, to nie jest powód, dla którego żadne dane nie muszą należy zmienić układ, raczej dlatego, że „główne bazy danych, które wycofują się wolniej niż zatwierdzanie, mają tendencję do przenoszenia danych”, a pg nie, jak wspomniałeś. Oracle również domyślnie stosuje pamięć masową: główna różnica polega na tym, że Oracle używa funkcji „cofnij” i odzyskuje całe miejsce przy zatwierdzaniu / wycofywaniu, zamiast iść drogą „próżni”.
Jack Douglas