encyklopedia.space

Two-Phase Commit (2PC)

Two-Phase Commit (ang. Two-Phase Commit, skrót 2PC) – protokół koordynacji transakcji w systemach rozproszonych, który umożliwia atomową i spójną aktualizację danych przechowywanych w wielu bazach danych lub innych zasobach.

Historia

Protokoły koordynacji transakcji zostały opracowane w latach 1970–1980 w ramach badań nad systemami rozproszonymi. Pierwsze formalne opracowanie Two-Phase Commit pojawiło się w pracach Jima Graya i Andreasza Reutera w 1978 roku. Od tego czasu protokół jest standardowym elementem systemów DBMS takich jak Oracle, Microsoft SQL Server czy MySQL.

Idea i zasada działania

Podstawową ideą 2PC jest podział procesu zatwierdzania transakcji na dwie odrębne fazy, które odbywają się pod kontrolą jednego węzła – koordynatora. Koordynator komunikuje się z uczestniczącymi w transakcji uczestnikami (ang. participants), z których każdy zarządza własną lokalną transakcją.

Faza 1 – prepare (przygotowanie)

  1. Koordynator wysyła do wszystkich uczestników żądanie prepare.
  2. Każdy uczestnik wykonuje wszystkie operacje transakcji w trybie tymczasowym i sprawdza, czy może je zatwierdzić (brak konfliktów, dostępność zasobów, spełnienie ograniczeń integralności).
  3. Uczestnik odsyła koordynatorowi jedną z dwóch odpowiedzi:
    • vote‑commit – gotowy do zatwierdzenia.
    • vote‑abort – niezdolny do zatwierdzenia, wymusza anulowanie.

Faza 2 – commit/abort (zatwierdzenie lub anulowanie)

  1. Jeżeli koordynator otrzymał vote‑commit od wszystkich uczestników, rozsyła komunikat global‑commit. Wtedy każdy uczestnik trwale zapisuje zmiany i zwalnia zasoby.
  2. Jeżeli choć jeden uczestnik odpowiedział vote‑abort lub nie odpowiedział w określonym czasie, koordynator wysyła global‑abort. Wszyscy uczestnicy wycofują zmiany, przywracając stan sprzed transakcji.

Właściwości

  • Atomowość – transakcja jest albo w pełni zatwierdzona, albo całkowicie wycofana.
  • Spójność – po zakończeniu transakcji wszystkie bazy danych spełniają reguły integralności.
  • Izolacja – operacje wewnątrz transakcji nie wpływają na inne transakcje do momentu zatwierdzenia.
  • Trwałość – po zatwierdzeniu zmiany pozostają zapisane pomimo awarii systemu.

Ograniczenia i problemy

Pomimo swojej prostoty, Two-Phase Commit ma kilka istotnych ograniczeń:

  • Blokowanie zasobów – w fazie przygotowania każdy uczestnik trzyma zablokowane zasoby, co może prowadzić do zakleszczeń.
  • Jednopunkt awarii – koordynator jest krytycznym węzłem; jego awaria w trakcie protokołu może spowodować, że uczestnicy pozostaną w nieokreślonym stanie (uncertain state).
  • Słaba odporność na awarie sieci – utrata komunikacji pomiędzy koordynatorem a uczestnikiem wymaga mechanizmów timeout i ponawiania, co wydłuża czas odpowiedzi.
  • Skalowalność – liczba wiadomości rośnie liniowo wraz z liczbą uczestników, co ogranicza wydajność w dużych klastrach.

Rozwiązania i alternatywy

Aby złagodzić wady 2PC, opracowano szereg rozszerzeń i alternatywnych protokołów:

  • Three-Phase Commit (3PC) – wprowadza dodatkową fazę prepare‑to‑commit, która eliminuje problemy z blokowaniem w przypadku awarii koordynatora.
  • Protokół Paxos i jego wariant Raft – zapewniają konsensus w systemach rozproszonych i mogą służyć jako podstawa dla transakcji wielowersyjnych.
  • Architektury SAG (Saga) – dzielą długotrwałą transakcję na serię krótkich działań z kompensacyjnymi operacjami odwracającymi, co eliminuje potrzebę blokowania zasobów.
  • Transactional Outbox i Change Data Capture – techniki asynchronicznego utrzymywania spójności danych przy użyciu kolejek wiadomości.

Zastosowania

Protokoł 2PC jest wykorzystywany w wielu rodzajach systemów:

Przykład implementacji (pseudo‑kod)

// Koordynator
sendPrepareToAll();
if (allVotesCommit()) {
    sendGlobalCommit();
} else {
    sendGlobalAbort();
}

// Uczestnik
onPrepare() {
    if (canCommit()) {
        voteCommit();
        awaitDecision(); // commit lub abort
        if (decision == COMMIT) {
            persistChanges();
        } else {
            rollback();
        }
    } else {
        voteAbort();
    }
}

Patrz także