PostgreSQL i ustawienia spójności zapisu dla konkretnego połączenia

Tłumaczenie artykułu przygotowano specjalnie dla studentów kursu „Bazy Danych“. Zainteresowany rozwojem w tym kierunku? Zapraszamy na Dzień Otwarty, na którym szczegółowo opowiemy o programie, cechach formatu online, kompetencjach oraz perspektywach zawodowych, które czekają na absolwentów po ukończeniu nauki.

PostgreSQL i ustawienia spójności zapisu dla konkretnego połączenia

PostgreSQL i ustawienia spójności zapisu dla konkretnego połączenia
W Compose mamy do czynienia z wieloma bazami danych, co daje nam możliwość bliższego zapoznania się z ich funkcjonalnością i niedociągnięciami. W miarę jak uczymy się doceniać funkcjonalne cechy nowych baz danych, czasami zaczynamy myśleć o tym, jak dobrze byłoby, gdyby podobne funkcje były dostępne również w bardziej dojrzałych narzędziach, z którymi pracujemy już od dłuższego czasu. Jedną z nowych cech, której chcieliśmy doświadczać w PostgreSQL, była konfigurowalna spójność zapisu dla połączenia w całym klastrze. Okazało się, że już ją mamy, a dzisiaj chcemy podzielić się z Wami informacjami na temat tego, jak możecie z niej skorzystać.

Dlaczego to dla mnie ważne?

To, jak ma się zachowywać klaster, zależy od Waszej aplikacji. Weźmy na przykład aplikację do płatności. Potrzebujecie 100% spójności w klastrze, więc musicie włączyć synchronizowane zatwierdzenia, aby Wasza baza danych czekała, aż wszystkie zmiany zostaną wprowadzone. Jednak jeśli Wasza aplikacja to szybko rozwijająca się sieć społecznościowa, na pewno wolicie szybką reakcję nad 100% spójnością. Aby to osiągnąć, możecie w swoim klastrze korzystać z asynchronicznych zatwierdzeń.

Poznajcie kompromis

Będziecie musieli pójść na kompromis między spójnością danych a wydajnością. PostgreSQL zmienia się z punktu widzenia spójności, ponieważ konfiguracja domyślna w takim przypadku jest przewidywalna i nie wiąże się z niespodziankami. A teraz przyjrzyjmy się kompromisom.

Kompromis 1: Wydajność

Jeśli klaster PostgreSQL nie wymaga spójności, może działać asynchronicznie. Zapis jest dokonywany w liderze klastra, a jego replikom aktualizacje będą przesyłane po kilku milisekundach. Kiedy klaster PostgreSQL wymaga spójności, musi działać synchronizująco. Zapis będzie dokonany w liderze klastra, który przekaże aktualizację replikom i poczeka na potwierdzenie, że każdy z nich dokonał zapisu, zanim odpowie klientowi, który zainicjował zapis, informując go, że zakończył się powodzeniem. Różnica praktyczna między tymi podejściami polega na tym, że metoda asynchroniczna wymaga dwóch skoków sieciowych, a metoda synchronizująca – czterech.

Kompromis 2: Spójność

W przypadku awarii lidera w tych dwóch podejściach rezultat będzie różny. Jeśli operacja jest wykonywana asynchronicznie, to w przypadku wystąpienia takiego błędu nie wszystkie zapisy będą zarejestrowane przez replikę. Ile zostanie utracone? Zależy to od samej aplikacji i efektywności replikacji. Replikacja Compose uniemożliwi replikom zostanie liderem, jeśli ilość danych w nich jest o 1 MB mniejsza niż w liderze, co oznacza, że potencjalnie może zostać utracone do 1 MB zapisów przy pracy asynchronicznej.

W trybie synchronizującym to nie ma miejsca. Jeśli lider zawiodął, wszystkie repliki są aktualizowane, ponieważ każdy zapis potwierdzony przez lidera musi być potwierdzony w replikach. Oto spójność.

Zachowanie synchronizujące ma sens w aplikacji do płatności, gdzie spójność ma wyraźną przewagę w znalezieniu kompromisu między spójnością a wydajnością. Najważniejsze dla takiej aplikacji to poprawne dane. A teraz przypomnij sobie o sieci społecznościowej, w której głównym celem jest zdobycie uwagi użytkownika poprzez jak najszybsze odpowiadanie na zapytania. W takim przypadku wydajność z mniejszą liczbą skoków sieciowych i mniejszym czasem oczekiwania na potwierdzenia będzie priorytetem. Jednak kompromis między wydajnością a spójnością nie jest jedynym, o którym warto myśleć.

Kompromis 3: Awarie

Bardzo ważne jest zrozumienie, jak klaster zachowuje się w czasie awarii. Rozważmy sytuację, gdy jedna lub więcej replik przestaje działać. Gdy zatwierdzenia są przetwarzane asynchronicznie, lider będzie kontynuował działanie, to znaczy będzie przyjmował i przetwarzał zapisy, nie czekając na brakujące repliki. Gdy repliki wracają do klastra, doganiają lidera. Przy synchronnej replikacji, jeśli repliki nie odpowiadają, to lider nie będzie miał innego wyjścia i będzie czekał na potwierdzenie zatwierdzenia, aż replika wróci do klastra i będzie mogła przyjąć i potwierdzić zapis.

Jedno połączenie na transakcję?

Każda aplikacja potrzebuje szczególnego rodzaju kombinacji spójności i wydajności. Oczywiście jeśli nie jest naszą aplikacją do opłacania rachunków, którą wyobrażamy sobie jako całkowicie spójną, ani naszą niemal efemeryczną aplikacją społecznościową. W wszystkich innych przypadkach będą chwile, gdy niektóre operacje muszą być synchronizowane, a inne – asynchroniczne. Może nie chcesz, aby system czekał, aż wiadomość wysłana na czacie zostanie zatwierdzona, ale jeśli w tej samej aplikacji odbywa się płatność, to trzeba będzie czekać.

Wszystkie te decyzje podejmuje oczywiście twórca aplikacji. Właściwe podejmowanie decyzji o tym, kiedy zastosować dane podejście, pomoże w pełni wykorzystać możliwość klastra. Ważne jest, aby twórca mógł przełączać się między nimi na poziomie SQL dla połączeń i transakcji.

Zapewnienie kontroli w praktyce

Domyślnie PostgreSQL zapewnia spójność. Jest to kontrolowane parametrem serwera synchronous_commit. Domyślnie jest on ustawiony na włączony, ale ma trzy inne opcje: local, remote_write lub wyłączony.

Ustawienie tego parametru na wyłączony powoduje, że wszystkie synchronne zatwierdzenia są zatrzymywane, nawet w systemie lokalnym. Parametr w local określa tryb synchronny dla systemu lokalnego, ale zapisy do replik są wykonywane asynchronicznie. Remote_write idzie jeszcze dalej: zapisy do replik są wykonywane asynchronicznie, ale wracają, gdy replika przyjmie zapis, ale nie zapisze go na dysku.

Rozważając dostępny zakres opcji, wybieramy zachowanie i, pamiętając, że włączony są to synchronne zapisy, wybierzemy local dla asynchronicznych zatwierdzeń w sieci, pozostawiając lokalne zatwierdzenia synchronnymi.

Teraz powiemy Ci, jak skonfigurować to w mgnieniu oka, ale wyobraź sobie, że zainstalowaliśmy synchronous_commit do local na serwerze. Zastanawialiśmy się, czy można zmieniać parameter synchronous_commit w locie, i okazało się, że można to zrobić, a co więcej, istnieją na to aż dwa sposoby. Pierwszy – to ustawienie sesji połączenia w następujący sposób:

SET SESSION synchronous_commit TO ON;  
// Twoje zapisy idą tutaj

Wszystkie kolejne zapisy w sesji będą potwierdzać operacje zapisu dla replik, zanim zwrócą pozytywny wynik połączonemu klientowi. Oczywiście, jeśli nie zmienisz ustawienia synchronous_commit ponownie. Można pominąć część SESSION w poleceniu, ponieważ będzie ona miała domyślną wartość.

Drugi sposób jest dobry, gdy po prostu chcesz upewnić się, że otrzymujesz synchronizowaną replikację dla jednej transakcji. W wielu bazach danych typu 'NoSQL' nie ma pojęcia transakcji, ale w PostgreSQL już tak. W tym przypadku uruchamiasz transakcję, a następnie ustawiasz synchronous_commit do włączony przed wykonaniem zapisu dla transakcji. COMMIT zakończy transakcję, używając dowolnej wartości parametru synchronous_commit, która była ustawiona w danym momencie, chociaż najlepiej jest ustawić zmienną wcześniej, aby upewnić się, że inni deweloperzy rozumieją, że zapisy nie są asynchroniczne.

BEGIN;  
SET LOCAL synchronous_commit TO ON;  
// Twoje zapisy idą tutaj
COMMIT;  

Wszystkie komendy commit transakcji będą teraz potwierdzane, jak zapisane w replikach jeszcze przed tym, jak baza danych zwróci pozytywną odpowiedź połączonemu klientowi.

Konfiguracja PostgreSQL

Do tego momentu wyobrażaliśmy sobie system PostgreSQL z synchronous_commit, zainstalowanym w local. Aby to było realne po stronie serwera, będziesz musiał ustawić dwa parametry konfiguracji serwera. Kolejny parametr synchronous_standby_names wejdzie w swoje prawa, gdy synchronous_commit będzie w włączony. Określa, które repliki mają prawo do synchronizowanych commitów, a my ustawimy go na *, co będzie oznaczać wykorzystanie wszystkich replik. Te wartości są zazwyczaj konfigurowane w pliku konfiguracyjnym poprzez dodanie:

synchronous_commit = local  
synchronous_standby_names='*'

Ustawiając parametr synchronous_commit na wartość local, tworzymy system, w którym lokalne dyski pozostają synchronizowane, ale commity replik sieciowych są domyślnie asynchroniczne. Oczywiście, jeśli zdecydujemy się uczynić te commity synchronizowanymi, jak pokazano powyżej.

Jeśli śledziłeś rozwój projektu Governor, mogliście zauważyć niektóre ostatnie zmiany (1, 2), które pozwoliły użytkownikom Governor testować te opcje i kontrolować ich spójność.

Kilka słów więcej…

Jeszcze tydzień temu powiedziałbym, że nie da się tak precyzyjnie skonfigurować PostgreSQL. To właśnie wtedy Kurt, członek zespołu platformy Compose, upierał się, że taka możliwość istnieje. Uspokoił moje wątpliwości i znalazł w dokumentacji PostgreSQL następujące:

PostgreSQL i ustawienia spójności zapisu dla konkretnego połączenia

Ten parametr może być zmieniany w dowolnym momencie. Zachowanie każdej transakcji określają ustawienia obowiązujące podczas commitowania. Dlatego możliwe i korzystne jest, aby dla niektórych transakcji commity odbywały się synchronnie, a dla innych asynchronnie. Na przykład, aby zmusić jedną multistatement transakcję do wykonywania commitów asynchronicznie, gdy wartość parametru domyślnie jest przeciwna, należy ustawić SET LOCAL synchronous_commit TO OFF w transakcji.

Dzięki takiej niewielkiej modyfikacji w pliku konfiguracyjnym umożliwiliśmy użytkownikom kontrolowanie ich spójności i wydajności.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster