Distributed SQL: kiedy wybrać Spanner, CockroachDB lub YugabyteDB
Sprawdź, kiedy distributed SQL uzasadnia koszt, jak porównać Spanner, CockroachDB i YugabyteDB oraz jak bezpiecznie planować obciążenia wieloregionowe.

Co oznacza distributed SQL
Distributed SQL to architektura relacyjnej bazy danych, która rozkłada dane i przetwarzanie transakcji na wiele maszyn, a aplikacjom udostępnia jedną logiczną bazę SQL. Zachowuje tabele, joiny, indeksy, ograniczenia i transakcje ACID, a następnie dodaje automatyczne partycjonowanie, replikację i odzyskiwanie po awarii.
System zwykle należy do tej kategorii, gdy łączy następujące cechy:
- Relacyjny schemat i interfejs zapytań SQL
- Skalowanie poziome między węzłami bazy
- Spójność transakcyjną między partycjami
- Automatyczną replikację i failover
- Skoordynowane działanie jako jedna logiczna baza
Ta definicja ma znaczenie, ponieważ baza nie staje się distributed SQL tylko dlatego, że dodano do PostgreSQL lub MySQL repliki odczytowe. Primary z replikami nadal kieruje zapisy przez jeden główny serwer. Sharding zarządzany przez aplikację rozdziela zapisy, ale zmusza ją do decydowania, gdzie mieszkają rekordy i jak działają operacje między shardami. Distributed SQL przenosi dużą część tej odpowiedzialności do bazy.
Między klasycznym RDBMS a NoSQL
Distributed SQL łączy relacyjny model programowania klasycznego RDBMS ze skalowaniem poziomym znanym z rozproszonych magazynów danych. Tradycyjne wdrożenia PostgreSQL i MySQL dobrze działają, gdy jedna instancja primary obsługuje zapisy, a awaria regionu nie wymaga ciągłego zapisu gdzie indziej. Repliki odczytowe, cache, pule połączeń i lepsze indeksy potrafią rozszerzać ten model przez lata.
Wiele baz NoSQL uprościło dystrybucję, ograniczając joiny, transakcje lub gwarancje spójności. To nadal rozsądne dla dużych strumieni zdarzeń, tymczasowych cache i rekordów rzadko uczestniczących w transakcjach wielowierszowych. Klaster relacyjny wymaga większej koordynacji, ponieważ aplikacje oczekują, że ograniczenia i transakcje pozostaną poprawne po podziale danych między węzły.
W praktyce różnica dotyczy tego, kto zarządza złożonością. Przy ręcznym shardingu zespoły aplikacyjne tworzą routing, równoważą dane, koordynują zmiany schematu i obsługują operacje dotykające kilku shardów. Distributed SQL dostarcza te mechanizmy, choć inżynierowie nadal muszą projektować schematy i zapytania dla systemu sieciowego.
Problemy, które ma rozwiązywać
Distributed SQL jest przeznaczony dla aplikacji, których wymagania dotyczące dostępności, położenia geograficznego lub wzrostu liczby zapisów przekroczyły architekturę z jedną instancją primary. Typowe przykłady to globalny SaaS, system rezerwacji, który nie może sprzedać więcej niż ma, oraz księga finansowa, której reguły muszą przetrwać awarie węzłów.
Może wyeliminować sharding w aplikacji i zmniejszyć zależność od jednego miejsca zapisu. Pozwala też umieszczać dane blisko użytkowników lub w zatwierdzonych jurysdykcjach. Te korzyści mają cenę: więcej replik, większy ruch sieciowy, więcej koordynacji i tryby awarii nieobecne na jednym serwerze.
Klasyczna zarządzana relacyjna baza pozostaje lepszym wyborem domyślnym, gdy obciążenie wygodnie mieści się w jednym regionie. Distributed SQL uzasadnia koszt, gdy własny sharding, regionalny failover lub geograficzne reguły danych stałyby się osobnym dużym systemem inżynieryjnym.
Jak distributed SQL działa od środka
Distributed SQL dzieli dane na replikowane partycje i koordynuje zmiany za pomocą konsensusu oraz rozproszonych protokołów transakcyjnych. Baza ukrywa większość tej mechaniki za SQL, ale jej działanie nadal wpływa na opóźnienia, przepustowość, projekt schematu i reakcję na incydenty.
Partycje określają miejsce rekordów
Klaster dzieli logiczne tabele na mniejsze jednostki, które mogą niezależnie przenosić się między węzłami. Spanner zwykle nazywa je splits, CockroachDB używa ranges, a YugabyteDB tablets. Każda jednostka obejmuje część przestrzeni kluczy tabeli lub indeksu.
Granice partycji mogą wynikać z zakresów, hashy lub jawnych reguł geograficznych. Zakres uporządkowany według identyfikatora klienta ułatwia skanowanie powiązanych rekordów, ale rosnący identyfikator może kierować nowe zapisy do jednej partycji. Rozkład hash równomierniej rozkłada zapisy, lecz utrudnia uporządkowane skany i rozmieszczenie tenantów. W schematach produkcyjnych często łączy się identyfikator tenanta z inną wartością, aby dane pozostawały dostępne bez skupiania wszystkich zapisów w jednym miejscu.
Indeksy wtórne wymagają własnego rozproszonego przechowywania. Zapis jednego wiersza może więc aktualizować tabelę bazową i kilka wpisów indeksu w różnych partycjach. Indeks tani na jednym serwerze może w klastrze dodać pracę konsensusu i ruch sieciowy.
Replikacja i konsensus chronią każdą partycję
Każda partycja zwykle ma kilka replik, a grupa konsensusu decyduje o zaakceptowanej kolejności zmian. CockroachDB i YugabyteDB używają replikacji opartej na Raft. Spanner używa Paxos wraz ze swoją infrastrukturą czasu.
Leader lub leaseholder koordynuje zapisy dla grupy replik. System zapisuje zmianę na wystarczającej liczbie replik, by utworzyć kworum, zanim uzna ją za zatwierdzoną. Gdy węzeł znika, pozostali członkowie mogą wybrać lub wyznaczyć innego koordynatora, o ile kworum pozostaje dostępne.
Kworum to wymóg matematyczny, a nie obietnica, że każda awaria będzie niegroźna. Grupa trzech replik zwykle toleruje jedną niedostępną replikę. Utrata dwóch członków oznacza, że pozostała kopia nie może bezpiecznie przyjmować zapisów, bo nie potrafi dowieść, że większość nie poczyniła postępu gdzie indziej. Rozmieszczenie między domenami awarii jest równie ważne jak liczba replik.
Transakcje rozproszone koordynują kilka partycji
Transakcja dotykająca jednej partycji często kończy się przy niewielkiej koordynacji. Transakcja obejmująca kilka partycji potrzebuje wspólnej decyzji o commicie, aby każdy uczestnik zastosował swoje zapisy albo je wycofał.
Dokładny protokół zależy od produktu, ale zwykle obejmuje odczyt lub blokowanie istotnych wersji, walidację równoczesnych zmian, replikację intencji lub rekordów tymczasowych oraz finalizację commitu. Długie transakcje zwiększają okno konfliktów. Duże wsady mogą angażować wiele grup konsensusu i powodować skoki opóźnień, nawet gdy pojedyncze instrukcje wyglądają prosto.
Dlatego ważny jest projekt transakcji uwzględniający sieć. Grupuj powiązane wiersze pod zgodnymi prefiksami partycji, gdy baza wspiera tę strategię. Utrzymuj krótkie transakcje, nie czekaj na zewnętrzne usługi przy otwartej transakcji i nie wkładaj tysięcy niepowiązanych rekordów do jednej jednostki atomowej bez pomiaru skutków.
Czas i kolejność wymagają jawnych mechanizmów
Rozproszone węzły nie mają idealnie zsynchronizowanego zegara, więc każdy produkt potrzebuje sposobu na uporządkowanie transakcji. Spanner używa granic niepewności TrueTime i oczekiwania na commit, aby zapewnić zewnętrzną spójność. Inne systemy mogą łączyć zegary fizyczne z komponentami logicznymi, śledzeniem zależności i protokołami transakcyjnymi.
Koordynacja zegarów wpływa na serializowalne wykonanie, odczyty followerów i snapshoty. Aplikacje powinny używać znaczników czasu transakcji bazy zamiast zakładać, że znaczniki generowane przez osobne serwery aplikacyjne ustanawiają wiarygodną globalną kolejność.
Lokalność steruje ścieżką sieciową
Konfiguracja lokalności decyduje, gdzie znajdują się repliki i który region koordynuje zapisy rekordu. Odczyty mogą być szybkie, jeśli odpowiednia replika jest blisko wywołującego. Silnie uporządkowany zapis nadal musi dotrzeć do replik potrzebnych dla kworum, więc jego opóźnienie odzwierciedla wybraną topologię.
Dobre rozmieszczenie wynika z obciążenia, a nie ze schematu organizacji firmy. Jeśli większość zapisów tenanta z UE pochodzi z Europy, umieszczenie tam koordynatora zapisu pozwala uniknąć międzykontynentalnej podróży na początku każdej transakcji. Rekord współdzielony globalnie, np. licznik aktualizowany ze wszystkich regionów, nie może być lokalny dla każdego zapisującego i może stać się punktem konfliktu.
Kiedy distributed SQL jest właściwym wyborem
Distributed SQL ma sens, gdy odporność geograficzna, pozioma pojemność zapisów lub poprawność między partycjami są na tyle ważne, by uzasadniać stałe koszty koordynacji. Duża firma nie potrzebuje go automatycznie, a mały produkt może go potrzebować, jeśli jego obietnica biznesowa obejmuje ścisłą dostępność regionalną.
Warunki uzasadniające ocenę
Poważną ocenę warto rozpocząć, gdy spełnionych jest kilka z tych warunków:
- Usługa musi działać mimo awarii strefy lub regionu
- Popyt na zapisy zbliża się do praktycznej granicy jednej bazy primary
- Ręczny sharding pochłonąłby dużo czasu zespołu aplikacyjnego
- Transakcje muszą pozostawać poprawne między węzłami lub lokalizacjami
- Rekordy wymagają egzekwowalnego rozmieszczenia geograficznego
Warunki te powinny mieć liczby. Zdefiniuj wymagany RTO, RPO, opóźnienie transakcji, szczytową liczbę zapisów i domeny awarii. Ogólne żądanie globalnego skalowania nie wystarcza do wyboru architektury.
Sami użytkownicy regionalni nie są decydującym powodem. Aplikacja skupiona na treści może umieścić serwery WWW i cache blisko użytkowników, zachowując jedną bazę regionalną. Repliki odczytowe wspierają regionalne przeglądanie, jeśli lekko nieaktualne wyniki są akceptowalne. Przesłanki są silniejsze, gdy użytkownicy w kilku lokalizacjach muszą wykonywać niskolatencyjne zapisy na powiązanych danych.
Warunki sprzyjające prostszej bazie
Klasyczna usługa relacyjna jest zwykle lepsza przy umiarkowanym ruchu, zapisach z jednego regionu i odzyskiwaniu, które może obejmować planowaną promocję bazy. Oferuje dojrzałe narzędzia, szeroką zgodność rozszerzeń, znane debugowanie i niższy rachunek za infrastrukturę.
Wymagania dotyczące bardzo niskich opóźnień również mogą przemawiać za jedną regionalną instancją primary. Lokalny trwały zapis kończy się znacznie szybciej niż zapis do kworum przez odległe regiony. Systemy analityczne zwykle powinny rozdzielać transakcje operacyjne i długie skany zamiast oczekiwać, że jeden klaster będzie świetny w obu zadaniach.
Liczą się też możliwości zespołu. Usługi zarządzane ograniczają pracę przy sprzęcie, poprawkach i płaszczyźnie sterowania, ale nie usuwają konfliktów w schemacie, ponowień transakcji, planowania zapytań, zarządzania pojemnością ani obsługi incydentów po stronie aplikacji. Gdy zespół nie ma czasu testować awarii, wdrożenie rozproszonej bazy może zwiększyć ryzyko.
Próg decyzji oparty na alternatywach
Najmocniejsze uzasadnienie pojawia się, gdy alternatywa już jest skomplikowana. Jeżeli inżynierowie zamierzają tworzyć routing tenantów, mapy shardów, zasady transakcji między shardami, procedury regionalnej promocji i osobne narzędzia migracyjne, baza dostarczająca te funkcje zasługuje na dokładną ocenę.
Jeśli alternatywą jest jedna zarządzana instancja PostgreSQL z repliką odczytową i przetestowanymi backupami, migracja wymaga jasnych dowodów. Najpierw wykonaj benchmark obecnego systemu. Nasycenie CPU może wynikać z nieefektywnego zapytania, słabego zarządzania połączeniami, nadmiaru indeksów lub braku cache, a nie z potrzeby poziomego skalowania zapisów.
Spójność, dostępność i opóźnienia
Distributed SQL zwykle zachowuje spójność transakcyjną podczas awarii, odmawiając operacji, które nie mogą dotrzeć do wymaganego kworum. Chroni to zatwierdzony stan, ale część żądań może czekać lub kończyć się błędem przy podziale sieci.
CAP opisuje zachowanie podczas awarii
Twierdzenie CAP ma zastosowanie, gdy komunikacja między częściami klastra zostaje przerwana. Dla dotkniętych danych system nie może jednocześnie gwarantować linearyzowalnej spójności i udanych odpowiedzi z każdej odizolowanej strony. Baza nastawiona na spójność pozwala działać stronie z kworum, a gdzie indziej odrzuca niebezpieczne zapisy.
CAP nie wyjaśnia opóźnień podczas normalnej pracy. Nawet gdy wszystkie łącza działają, repliki muszą się komunikować. Decyzja inżynieryjna obejmuje zachowanie podczas podziału oraz ilość koordynacji akceptowanej przez aplikację w zdrowym systemie.
Aplikacja musi jawnie obsługiwać niedostępność. Timeouty, błędy transakcji możliwe do ponowienia i tymczasowa utrata regionu zapisu są normalnymi możliwościami. Zwrócenie sukcesu z obu odizolowanych regionów byłoby gorsze dla salda lub rezerwacji, ponieważ uzgodnienie nie musiałoby mieć poprawnej automatycznej odpowiedzi.
Silne odczyty i celowo nieaktualne odczyty różnią się
Silny odczyt obserwuje stan bazy zgodny z żądaną gwarancją kolejności. Część produktów udostępnia też odczyty followerów lub odczyty o ograniczonej nieaktualności, które wymieniają świeżość na mniejsze opóźnienie i mniej pracy koordynatora zapisów.
Wybór powinien wynikać z odczytywanego pola. Opis produktu często może pochodzić z nieco starszej repliki. Świeżo zmienione hasło, bieżące saldo konta lub pozostałe zapasy powinny korzystać z odpowiednio silnej ścieżki albo spójności sesyjnej. Nie oznaczaj wszystkich odczytów jako nieaktualne dla szybkości, a potem nie odtwarzaj poprawności w kodzie usługi.
Zachowanie read-your-writes wymaga testów z rzeczywistym sterownikiem i warstwą routingu. Po aktualizacji kolejne żądanie może trafić do innego serwera aplikacyjnego albo endpointu bazy. Tokeny sesji, granice transakcji lub ustawienie silnego odczytu mogą być konieczne, aby użytkownik zobaczył zaakceptowaną zmianę.
Izolacja steruje wynikami współbieżności
Izolacja transakcji określa, jakie anomalie mogą wystąpić w równoczesnych transakcjach. Izolacja serializowalna ma sprawić, by zakończone transakcje wyglądały tak, jakby wykonały się pojedynczo, nawet jeśli baza uruchamiała je równolegle.
Wykonanie serializowalne może przerwać jednego uczestnika, gdy równoczesnych operacji nie da się bezpiecznie uporządkować. Takie przerwanie chroni przed złym wynikiem, a nie oznacza uszkodzenia bazy. Aplikacje potrzebują ograniczonych ponowień całej transakcji, wraz ze wszystkimi odczytami, które wpłynęły na jej zapisy.
Ponowienia muszą być idempotentne poza bazą. Jeśli kod wysyła e-mail lub wywołuje dostawcę płatności przed pewnym commitem, ponowienie może powtórzyć skutek uboczny. Zapisz zdarzenie outbox w transakcji bazy, zatwierdź je, a zewnętrzną akcję dostarcz osobnym workerem.
Odległość wyznacza dolną granicę opóźnienia zapisu
Transakcja między regionami nie zakończy się szybciej niż wiadomości wymagane przez jej protokół. Osiemdziesięciomilisekundowy czas round-trip między członkami kworum realnie wydłuża operację, zanim uwzględni się wykonanie zapytania, utrzymanie indeksów, pracę aplikacji i kolejki.
Kosztowny wzorzec to często kilka sekwencyjnych transakcji w jednej akcji użytkownika. Jeśli checkout wykonuje insert zamówienia, rezerwację zapasów, aktualizację stanu płatności i zapis audytowy jako cztery blokujące commity, koszty sieci się sumują. Połączenie zmian bazy mających jeden wynik atomowy może usunąć zbędne round tripy, natomiast zewnętrzne wywołania płatności powinny pozostać poza otwartą transakcją.
Mierz percentyle opóźnień, nie średnie. Zmiany leadera, konflikty, przestoje storage i ponowienia są widoczne w ogonie rozkładu. Projekt spełniający cel dla mediany, lecz chybiający 99. percentyl podczas zwykłego równoważenia, nadal może powodować widoczne błędy użytkowników.
Porównanie Spanner, CockroachDB i YugabyteDB
Spanner, CockroachDB i YugabyteDB rozwiązują podobne problemy z dystrybucją, ale różnią się modelem wdrożenia, zgodnością, implementacją transakcji i założeniami operacyjnymi. Wybór wymaga testów zachowania aplikacji, a nie decyzji opartej wyłącznie na wspólnej etykiecie SQL.
| Obszar | Google Spanner | CockroachDB | YugabyteDB |
|---|---|---|---|
| Główny interfejs SQL | Dialekt GoogleSQL lub PostgreSQL | SQL zgodny z PostgreSQL przez protokół PostgreSQL | YSQL zgodny z PostgreSQL oraz YCQL w stylu Cassandra |
| Podstawa replikacji | Grupy Paxos z kolejnością opartą na TrueTime | Replikacja Raft przez ranges | Replikacja Raft przez tablets |
| Typowa forma dostarczenia | Zarządzana baza Google Cloud | Usługa zarządzana lub wdrożenie self-managed | Usługa zarządzana lub wdrożenie self-managed |
| Ryzyko dla przenośności | Dialekt i zachowanie specyficzne dla platformy | Luki w funkcjach, rozszerzeniach i semantyce PostgreSQL | Różnice wersji i funkcji między YSQL a PostgreSQL |
| Naturalny przypadek oceny | Systemy Google Cloud potrzebujące globalnego rozmieszczenia transakcyjnego | Zespoły szukające rozproszonej pracy w stylu PostgreSQL | Zespoły chcące dostępu w stylu PostgreSQL lub wyboru API SQL i Cassandra |
Spanner pasuje do zarządzanej strategii Google Cloud
Spanner pasuje do organizacji gotowych korzystać z zarządzanej bazy Google Cloud i projektować pod jej dialekt, topologię oraz model działania. TrueTime wspiera transakcje o zewnętrznej spójności, więc zatwierdzone transakcje respektują kolejność czasu rzeczywistego w ramach udokumentowanej semantyki.
Jego dialekt PostgreSQL może zmniejszyć różnice składni SQL, ale dialekt nie oznacza pełnej zgodności z PostgreSQL. Rozszerzenia, funkcje administracyjne, katalogi systemowe, typy danych, sterowniki i założenia ORM nadal wymagają weryfikacji. Przed uznaniem istniejącej aplikacji za przenośną zespół powinien zinwentaryzować każdą zależność od bazy.
Spanner zasługuje na szczególną uwagę, gdy docelowy system już zależy od tożsamości, sieci, obserwowalności i kontroli regionalnych Google Cloud. Model zarządzany usuwa administrację węzłami bazy, lecz projekt schematu, strojenie zapytań, limity, koszty i odzyskiwanie aplikacji nadal należą do klienta.
CockroachDB pasuje do rozproszonych aplikacji w stylu PostgreSQL
CockroachDB pasuje do zespołów chcących dostępu aplikacyjnego w stylu PostgreSQL przy dystrybucji danych transakcyjnych między ranges. Domyślnie używa izolacji serializowalnej, dlatego aplikacje muszą poprawnie ponawiać transakcje odrzucone z powodu konfliktów lub kolejności.
Zgodność warto testować na poziomie migracji, sterownika i ORM. Rozszerzenia PostgreSQL oraz wyspecjalizowane zachowania mogą być nieobecne lub inne. Zapytania zależne od planów wykonania na jednym węźle mogą też działać inaczej po podziale tabel i indeksów między ranges.
Przenoszenie ranges i automatyczne równoważenie upraszczają zmiany pojemności, ale zły wybór klucza głównego nadal może tworzyć gorące ranges. Abstrakcje wieloregionowe ułatwiają opis lokalności tabel, jednak programiści muszą zdecydować, które rekordy są regionalne, które globalne i gdzie koordynować zapisy.
YugabyteDB pasuje do YSQL i mieszanych wymagań API
YugabyteDB pasuje do aplikacji ceniących relacyjny interfejs zgodny z PostgreSQL i mogących skorzystać z osobnego API zgodnego z Cassandra. YSQL zapewnia tabele relacyjne i transakcje rozproszone, natomiast YCQL opiera się na innym modelu danych i nie należy go traktować jako drugiej drogi do każdej operacji YSQL.
Warstwa storage rozdziela dane przez tablets. Projekt tabel, dzielenie tabletów, rozmieszczenie indeksów i zakres transakcji wpływają na to, jak praca rozkłada się po klastrze. Aplikacje PostgreSQL nadal wymagają testów zgodności rozszerzeń, funkcji, narzędzi i zachowania planera.
Różne podejścia do wdrożenia mogą odpowiadać politykom infrastruktury wymagającym kontroli nad rozmieszczeniem. Przy self-managed taka kontrola przenosi odpowiedzialność operacyjną na klienta: aktualizacje, procedury naprawcze, pojemność, obserwowalność, certyfikaty, backupy i testy awarii muszą mieć właścicieli.
Użyteczny test produktu opiera się na danych z aplikacji
Dobre porównanie uruchamia to samo reprezentatywne obciążenie na każdym realnym kandydacie. Testuj tworzenie schematu, migracje, SQL generowany przez ORM, ponowienia transakcji, odtwarzanie backupów, failover, skalowanie i zapytania o największym wolumenie.
Nie porównuj wyłącznie szczytowej liczby transakcji na sekundę. Zapisuj opóźnienia p50, p95 i p99, wskaźniki konfliktów i ponowień, bajty przesyłane między regionami, zwielokrotnienie storage, czas odtworzenia oraz pracę operatora podczas symulowanego incydentu. Najlepszy wybór spełnia cele poprawności i odzyskiwania przy akceptowalnych kosztach i obciążeniu operacyjnym.
Globalny SaaS z użytkownikami regionalnymi
Globalna aplikacja SaaS korzysta z distributed SQL, gdy tenanci potrzebują regionalnego rozmieszczenia danych i dostępu transakcyjnego bez osobnych stosów baz danych dla każdej geografii. Projekt działa najlepiej, gdy tenancy jest jawne w schemacie, a większość transakcji pozostaje w obrębie jednego tenanta.
Lokalność tenanta powinna wynikać z umów i ruchu
Identyfikator tenanta może sterować rozmieszczeniem, dzięki czemu europejskie rekordy pozostają w zatwierdzonych lokalizacjach europejskich, a dane innego klienta w kraju lub regionie określonym w umowie. Pozwala to zachować jeden logiczny schemat przy różnych politykach fizycznych.
Reguły rozmieszczenia muszą obejmować więcej niż tabelę bazową. Wpisy indeksów, change streams, dane tymczasowe, backupy i eksportowane rekordy mogą zawierać dane regulowane. Polityka przypinająca wiersze, ale wysyłająca globalny indeks wtórny gdzie indziej, może naruszać zamierzoną granicę.
Izolacja tenantów wpływa też na wydajność. Duży tenant może przeciążyć wspólną partycję lub zdominować węzeł. Hashowanie lub podpartycjonowanie wewnątrz tenanta może być potrzebne, lecz powinno zachować efektywny dostęp do transakcji w jego zakresie.
Regionalne odczyty potrzebują jawnej polityki świeżości
Panele z przewagą odczytów mogą korzystać z pobliskich replik, jeśli niewielkie opóźnienie danych jest akceptowalne. Zmiany konta, decyzje autoryzacyjne i ekrany potwierdzenia po transakcji wymagają silniejszego zachowania. Klasyfikuj ścieżki zapytań według wymagań świeżości zamiast przyjmować jedno globalne ustawienie.
Rozmieszczenie zapisów powinno podążać za typowym zapisującym dla każdego tenanta. Gdy pracownicy klienta pracują głównie w Singapurze, koordynowanie zapisów na innym kontynencie tworzy zbędne opóźnienia. Procedura migracji tenanta powinna aktualizować rozmieszczenie bez utraty zapisów, naruszenia rezydencji lub pozostawienia cache aplikacji wskazujących stare lokalizacje.
Globalny kod aplikacji musi tolerować zmiany
Leaders się zmieniają, węzły restartują, a routing zmienia się podczas utrzymania. Sterowniki potrzebują rozsądnych timeoutów, polityk ponawiania, odnawiania połączeń i logiki restartu transakcji. Ponowienia powinny używać jittera i limitu, aby przeciążony klaster nie dostał natychmiastowej, zsynchronizowanej fali powtórzonych żądań.
Monitoring powinien rozdzielać opóźnienie użytkowników według regionu i klasy tenanta. Globalna średnia może ukryć odległą grupę klientów płacącą za kilka dodatkowych podróży sieciowych. Identyfikatory trace łączące zakresy API z instrukcjami bazy ułatwiają znalezienie błędów lokalności.
Przepływy finansowe i księgi
Przepływy finansowe zyskują, gdy ograniczenia bazy i transakcje egzekwują reguły księgi mimo awarii i równoczesnych żądań. Dystrybucja sama nie tworzy poprawnej księgowości, dlatego schemat musi zakodować zasady, których nie wolno złamać.
Księga powinna zachować audytowalną sekwencję wpisów
Księga zorientowana na dopisywanie rejestruje każdy ruch jako wpisy, zamiast stale zastępować jedną wartość salda bez historii. Każde księgowanie powinno mieć stabilny identyfikator transakcji, konta, kwoty, walutę, znacznik czasu biznesowego i metadane utworzenia. Reguły podwójnego zapisu należy sprawdzić przed commitem, aby obciążenia i uznania bilansowały się w jednostce księgowania.
Cache salda może przyspieszać odczyty, ale musi zmieniać się w tej samej transakcji co wpisy albo być wyraźnie traktowany jako dane pochodne. Zadania uzgadniające powinny porównywać sumy pochodne z wpisami źródłowymi i zgłaszać różnice bez cichego przepisywania historii.
Globalna kolejność rzadko jest potrzebna dla każdego konta. Transakcje dotyczące jednego konta lub pary transferu potrzebują spójnej kolejności, a niezależne konta mogą działać równolegle. Projekt wokół tej granicy ogranicza konflikty w porównaniu z jedną globalną sekwencją lub wierszem rozliczenia.
Idempotencja czyni ponowienia bezpiecznymi
API płatności, kolejki i webhooki ponawiają po timeoutach, więc każda operacja biznesowa potrzebuje stabilnego klucza idempotencji. Wymuś unikalność w odpowiednim zakresie, np. jednego sprzedawcy lub konta, a następnie utwórz rekord płatności i wpisy księgi w jednej transakcji bazy.
CREATE TABLE payment_attempts (
account_id UUID NOT NULL,
idempotency_key TEXT NOT NULL,
provider_reference TEXT,
status TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (account_id, idempotency_key)
);
Gdy dwóch workerów zgłasza tę samą operację, unikalne ograniczenie rozstrzyga, który insert się powiedzie. Przegrany worker powinien odczytać istniejący rekord i zwrócić jego ustalony wynik. Nie może tworzyć drugiego obciążenia u dostawcy tylko dlatego, że transakcja bazy została ponowiona.
Wywołania zewnętrzne wymagają granicy transakcji
Baza nie może atomowo zatwierdzić zmian z niezależnym dostawcą płatności, chyba że oba systemy uczestniczą w wyspecjalizowanym protokole koordynacji, czego większość publicznych API nie robi. Trzymaj wywołanie sieciowe poza transakcją bazy i modeluj przepływ jako jawne stany: pending, authorized, captured, failed i reversed.
Transactional outbox może publikować zatwierdzone zmiany do workerów downstream. Konsumenci powinni usuwać duplikaty według identyfikatora zdarzenia, ponieważ wiadomość może zostać dostarczona więcej niż raz. Zapewnia to odzyskiwalne przetwarzanie bez udawania niemożliwej pojedynczej transakcji obejmującej wszystkie usługi.
Gorące konta potrzebują projektu pod konkretne obciążenie
Wypłaty, rozliczenia marketplace i duzi sprzedawcy mogą skupiać zapisy na jednym koncie. Dodanie węzłów bazy nie dzieli jednego konfliktowego wiersza między nie. Możliwe rozwiązania to niezmienne partycje wpisów, akumulatory okresowe, kolejkowane księgowanie dla jednego konta albo starannie określona hierarchia subkont.
Testuj rzeczywisty rozkład nierównomierności. Jednolity ruch syntetyczny może sugerować gotowość klastra, podczas gdy jeden sprzedawca produkcyjny tworzy powtarzalne konflikty serializowalne. Poprawność jest najważniejsza, lecz model danych powinien ujawniać bezpieczną współbieżność tam, gdzie pozwalają zasady księgowe.
Zapasy, rezerwacje i booking
Systemy zapasów i rezerwacji potrzebują autorytatywnej transakcji przydziału, gdy kilka osób może rościć sobie prawa do tego samego rzadkiego zasobu. Szybkie odczyty dostępności poprawiają przeglądanie, ale tylko ścieżka commitu może zdecydować, kto otrzyma ostatnią jednostkę.
Zapisy warunkowe zapobiegają nadmiernej sprzedaży
Aktualizacja warunkowa może zarezerwować zapas tylko wtedy, gdy wystarczająca ilość pozostała. Liczba zmienionych wierszy mówi aplikacji, czy przydział się powiódł.
UPDATE inventory
SET available = available - 1
WHERE sku = $1
AND available > 0;
Ta instrukcja powinna dzielić transakcję z rekordem rezerwacji. Odczyt dostępności, a później zmniejszenie wartości tworzy wyścig, chyba że poziom izolacji i obsługa predykatów chronią decyzję. Ograniczenia bazy powinny dodatkowo odrzucać wartości ujemne.
Dla przypisanych miejsc unikalne ograniczenie na identyfikator wydarzenia i miejsca daje jedną wygrywającą rezerwację. Zapasy hotelowe często modeluje się na poziomie pokoju i nocy albo puli zapasów z datą, aby nakładające się pobyty nie zajmowały tej samej pojemności. Właściwa jednostka konfliktu wynika z reguły biznesowej.
Blokady rozdzielają przydział od płatności
Tymczasowa blokada rezerwuje zapas, gdy trwa płatność lub potwierdzenie użytkownika. Zapisz czas wygaśnięcia i status, a następnie zamień ją w potwierdzoną rezerwację przez transakcję warunkową. Worker wygasający powinien zwalniać wyłącznie nadal aktywne blokady, ponieważ potwierdzenie i wygaśnięcie mogą się ścigać.
Upływ czasu zegarowego nie wystarcza, by zagwarantować zwolnienie. Workery mogą się zatrzymać, kolejki mogą mieć opóźnienia, a regiony mogą zawieść. Zapytania wyliczające sprzedażowy zapas powinny konsekwentnie uwzględniać wygasły status, a zadania naprawcze odzyskiwać pominięte blokady.
Czas blokady to decyzja produktowa i pojemnościowa. Dziesięć minut może być rozsądne dla checkoutu, ale podczas wzmożonego ruchu może zablokować znaczną część rzadkiego zapasu. Zmierz porzucenia i czas ukończenia płatności przed jego ustaleniem.
Skrajne konflikty nie skalują się liniowo
Tysięcy kupujących walczących o jeden wiersz nie da się obsłużyć równolegle przez dodanie replik. Każde udane zmniejszenie musi zostać uporządkowane względem pozostałych. Kontrola przyjęć, kolejka, koszyki zapasów lub wcześniej przydzielone regionalne limity mogą chronić bazę podczas premiery.
Regionalne limity ograniczają koordynację, ale zmieniają semantykę. Jeśli Europa ma niewykorzystane jednostki, a inny region wyprzeda się, system potrzebuje bezpiecznego sposobu przeniesienia limitu lub zaakceptowania tymczasowej nierównowagi. Stosuj ten wzorzec tylko wtedy, gdy biznes potrafi określić sposób uzgadniania regionalnej pojemności.
Wysoka dostępność i odtwarzanie po awarii
Distributed SQL może utrzymywać usługę mimo wybranych awarii infrastruktury, jeśli rozmieszczenie replik, zapas pojemności i zachowanie aplikacji odpowiadają zdefiniowanemu celowi usługi. Sama replikacja tego nie zapewnia.
SLO powinny określać domeny awarii
Cel dostępności potrzebuje obciążenia i scenariusza awarii. Określ, czy usługa ma przetrwać awarię jednego węzła, strefy dostępności czy całego regionu. Podaj akceptowalny poziom błędów i opóźnienia podczas zdarzenia, a nie tylko po odzyskaniu.
Klaster trzech replik w jednym budynku ma inny profil ryzyka niż trzy repliki w niezależnych strefach. Topologia wieloregionowa chroni przed szerszym zdarzeniem, ale wydłuża ścieżki kworum i wymaga wystarczającej pozostałej pojemności, by przejąć ruch po utracie lokalizacji.
RTO określa, jak szybko usługa musi wrócić. RPO określa, ile zatwierdzonych danych można utracić. Synchroniczna replikacja do kworum może wspierać cel zerowej utraty zatwierdzonych danych dla objętych awarii, ale tylko gdy wymagane repliki i ścieżka aplikacji działają zgodnie z projektem.
Failover tworzy widoczne zdarzenia dla aplikacji
Zmiany leadership mogą przerwać transakcje w toku, zamknąć połączenia i zwiększyć opóźnienia. Aplikacje muszą odróżniać wyniki bazy możliwe do ponowienia od trwałych błędów biznesowych. Nieudaną transakcję trzeba uruchomić ponownie jako całość, zamiast odtwarzać tylko jej ostatnią instrukcję.
Pule połączeń mogą po awarii przechowywać martwe endpointy. Kontrole zdrowia, zachowanie DNS, load balancery, walidacja certyfikatów i wykrywanie topologii przez sterownik należą do planu testów. Baza może być zdrowa, gdy aplikacja nadal nie potrafi jej znaleźć.
Pojemność po awarii wymaga jawnych obliczeń. Jeśli trzy regiony zwykle działają przy 70% wykorzystania, utrata jednego nie zostawia miejsca na jego część pracy. Rezerwa kosztuje, ale topologia bez pojemności na failover nie spełnia deklarowanego celu.
Game days sprawdzają projekt
Ćwiczenia awaryjne powinny wyłączać węzeł, izolować strefę, przerywać łączność regionalną i usuwać endpoint aplikacji. Mierz czas trwania błędów, wskaźnik ponowień transakcji, percentyle opóźnienia, wzrost kolejki i odpowiedź operatora.
Wykonuj je po istotnych zmianach topologii, sterownika lub schematu. Procedura sprawdzona przy zeszłorocznym ruchu może zawieść po podwojeniu danych lub dominacji jednego tenanta. Automatyzuj bezpieczne części ćwiczenia, aby dowody nie zależały od corocznego ręcznego zdarzenia.
Replikacja to nie backup
Repliki wiernie kopiują przypadkowe usunięcia, wadliwe migracje i szkodliwe zapisy aplikacji. Backupy i point-in-time recovery chronią przed logicznym uszkodzeniem, którego replikacja nie wykrywa.
Testy odtworzenia powinny budować osobne czyste środowisko, sprawdzać sumy kontrolne lub reguły aplikacji i mierzyć całkowity czas odzyskania. Uwzględnij klucze szyfrowania, polityki dostępu, wersje schematu i zależną konfigurację. Backup istniejący, lecz niemożliwy do odtworzenia w wymaganym czasie, nie jest odpowiednim systemem odzyskiwania.
Rezydencja danych i architektura wynikająca ze zgodności
Distributed SQL może umieszczać tenantów lub grupy rekordów w zatwierdzonych regionach, ale zgodność zależy od każdej kopii, ścieżki dostępu i procesu operacyjnego. Lokalność bazy jest jedną kontrolą w szerszym programie.
Reguły rezydencji potrzebują precyzyjnych definicji
Wymóg pozostawienia danych w kraju może dotyczyć przechowywania, przetwarzania, dostępu wsparcia, backupów, kluczy szyfrowania albo wszystkich tych elementów. Różne interpretacje tworzą różne topologie. Prawnicy i audytorzy powinni przełożyć regulacje oraz umowy na testowalne kontrole techniczne.
Zespoły potrzebują inwentaryzacji pól regulowanych i danych pochodnych. Logi, trace, indeksy wyszukiwania, eksporty analityczne, załączniki wsparcia i kolejki wiadomości mogą zawierać te same dane osobowe co tabela główna. Ograniczenie bazy przy globalnym eksporcie surowych payloadów nie spełnia zamierzonej polityki.
Minimalizacja danych może uprościć projekt. Jeśli globalna usługa potrzebuje wyłącznie identyfikatora konta i zbiorczego statusu, przechowuj dane wrażliwe w zatwierdzonym regionie, a gdzie indziej udostępniaj najmniejszą dozwoloną reprezentację.
Polityki rozmieszczenia muszą obejmować cykl życia
Polityki powinny określać, gdzie mogą istnieć żywe repliki, repliki tymczasowe, backupy, snapshoty, rekordy zmian i środowiska odtworzeniowe. Równoważenie i utrzymanie muszą przestrzegać tej samej granicy. Procedura awaryjna nie powinna dla wygody kopiować regulowanych danych do niezatwierdzonego regionu.
Kontrola dostępu potrzebuje ograniczeń geograficznych i organizacyjnych. Tożsamości usług powinny otrzymywać tylko potrzebne tabele i operacje. Dostęp ludzi do produkcji należy logować, ograniczać czasowo tam, gdzie to praktyczne, i przeglądać. Klucze szyfrowania przypisane do regionu mogą dodać kontroli, lecz ich dostępność i odtwarzanie po awarii wymagają osobnego projektu.
Relokacja tenanta zasługuje na udokumentowany przepływ. Zmiany umowy, migracja klienta lub restrukturyzacja firmy mogą wymagać przeniesienia rekordów między jurysdykcjami. Proces powinien wskazywać, kiedy znikają stare kopie, jak wygasają backupy i jakie dowody potwierdzają zakończenie.
Globalne raportowanie może wymagać danych pochodnych
Globalny dashboard może kolidować z rygorystycznym rozmieszczeniem, jeśli skanuje surowe dane klientów między regionami. Przetwarzanie regionalne może lokalnie wyliczać zatwierdzone agregaty, a następnie publikować niewrażliwe wyniki w centralnym magazynie raportowym.
Reguły agregacji powinny uniemożliwiać odtworzenie ograniczonych rekordów. Małe grupy, pola swobodnego tekstu i szczegółowe wymiary mogą ujawnić dane osobowe nawet po usunięciu bezpośrednich identyfikatorów. Ład analityczny należy więc do przeglądu architektury, a nie do późniejszego projektu raportowego.
Obciążenia operacyjne i analityczne często zasługują na osobne systemy. Baza transakcyjna chroni bieżący stan produktu, a pipeline'y ograniczone do regionu tworzą zarządzane zbiory danych dla raportów. Taki podział trzyma długie skany analityczne z dala od transakcji wrażliwych na opóźnienia.
Planowanie kosztów i wydajności
Distributed SQL kosztuje więcej niż podstawowa baza jednego regionu, ponieważ utrzymuje nadmiarową pojemność i koordynuje pracę przez sieć. Inwestycja nadal może mieć sens, jeśli zastępuje kosztowną pracę przy shardingu lub zapobiega stratom większym niż premia operacyjna.
Compute i storage obejmują narzut replikacji
Logiczny zbiór 2 TB z trzema pełnymi replikami zaczyna się od około 6 TB zreplikowanych danych, przed indeksami wtórnymi, tymczasowym miejscem na kompakcję, backupami i metadanymi. Rzeczywiste rozliczenia i kompresja zależą od produktu, więc szacunki powinny używać zmierzonego storage fizycznego, nie tylko logicznego rozmiaru tabel.
Compute musi pokryć zwykłą pracę, przetwarzanie konsensusu, równoważenie, backupy i rezerwę na awarie. Węzły nie są wymiennymi jednostkami przepustowości, gdy jedna partycja jest gorąca. Dodanie pojemności pomaga tylko wtedy, gdy obciążenie może się po niej rozłożyć.
Indeksy mnożą pracę zapisu i storage. Przeglądaj każdy indeks wtórny pod kątem wartości dla zapytań, częstotliwości aktualizacji i położenia geograficznego. Nieużywany indeks w klastrze rozproszonym marnuje dysk i podnosi koszt każdego dotkniętego nim zapisu.
Opłaty sieciowe mogą stać się istotne
Replikacja wysyła zapisy między lokalizacjami replik. Zapytania między regionami, change feeds, backupy i ruch aplikacji dodają kolejne transfery. Aktywny ruch w kilku regionach może wygenerować rachunek, którego benchmark w jednym regionie nigdy nie pokaże.
Oszacuj bajty na transakcję, współczynnik replikacji, liczbę zapisów, zwielokrotnienie przez indeksy i kierunek transferów. Następnie testuj z danymi rozliczeniowymi dostawcy podczas reprezentatywnego obciążenia. Sama liczba żądań pomija duże payloady i ruch w tle.
Błędy lokalności zwiększają koszt i opóźnienia. Usługa wdrożona w jednym regionie może wielokrotnie pytać koordynator w innym z powodu wyboru endpointu lub rozmieszczenia tenanta. Tracing rozproszony i regionalne zestawienia kosztów pozwalają wykryć ten wzorzec.
Ścieżki użytkownika pokazują skumulowane opóźnienie
Modeluj kompletne działania użytkownika, a nie izolowane instrukcje. Dla checkoutu policz każdy sekwencyjny commit bazy, silny odczyt, wywołanie zewnętrznego API i przekazanie do kolejki. Zastosuj zmierzone regionalne czasy round-trip i percentyle wykonania zapytania do ścieżki krytycznej.
Załóżmy, że jedna ścieżka zawiera dwa sekwencyjne zapisy do kworum, z których każdy dodaje 90 milisekund koordynacji sieciowej. To daje około 180 milisekund przed przetwarzaniem aplikacji. Połączenie zmian mających jedną decyzję atomową może usunąć commit, a równoległe niezależne odczyty mogą skrócić ścieżkę.
Testy obciążeniowe powinny obejmować realistyczne konflikty i rozmiary payloadów. Benchmark z losowymi identyfikatorami może rozkładać pracę idealnie, choć zapisy produkcyjne trafiają do kilku popularnych tenantów. Uwzględnij zmiany leadera i równoważenie, aby opóźnienie ogonowe odzwierciedlało zwykłą pracę klastra.
Porównaj całkowity koszt posiadania z realnymi alternatywami
Właściwe porównanie nie zestawia distributed SQL z wyimaginowaną bazą bez kosztów operacyjnych. Porównaj go z konkretną alternatywą: zarządzanym PostgreSQL, replikami, usługami shardingu, odzyskiwaniem regionalnym, routingiem aplikacji i inżynierami potrzebnymi do utrzymania.
Uwzględnij pracę migracyjną, szkolenia, obserwowalność, reagowanie na incydenty, plany wsparcia i koszty wyjścia. Usługa zarządzana może ograniczyć pracę infrastrukturalną, a self-management może spełnić wymagania kontroli kosztem większych potrzeb kadrowych.
Prosty model finansowy może porównać roczną premię platformy z oczekiwaną stratą z awarii, opóźnioną pracą inżynieryjną, ekspozycją na problemy ze zgodnością i przychodami dotkniętymi regionalnymi opóźnieniami. Dla niepewnych danych używaj zakresów i wskaż założenie zmieniające decyzję. Jeśli wynik zależy od nieprawdopodobnie wysokiego szacunku awarii, prostszy system prawdopodobnie nadal jest odpowiedni.
Wzorce projektowania schematu i aplikacji
Schemat distributed SQL działa dobrze, gdy ścieżki dostępu rozkładają niezależną pracę, utrzymując powiązane transakcje blisko siebie. Przeniesienie niezmienionego schematu z jednego węzła może zachować poprawność, lecz dać słabe opóźnienia lub poważne konflikty.
Klucze główne wpływają na dystrybucję
Monotonicznie rosnący klucz główny może kierować nowe wiersze na koniec jednego range. Losowe identyfikatory rozkładają inserty, ale całkowicie losowy rozkład może utrudniać skany tenantów i regionalne rozmieszczenie. Klucze złożone często równoważą te cele, zaczynając od identyfikatora tenanta lub koszyka i zachowując sortowalną wartość wewnątrz grupy.
Wybieraj prefiks zgodnie z granicami transakcji. Jeśli prawie każda operacja dotyczy tenanta, grupowanie według tenanta może ograniczyć pracę rozproszoną. Bardzo duży tenant może potrzebować koszyków w swojej przestrzeni nazw, aby kilka partycji mogło przyjmować zapisy równolegle.
Zmiana klucza głównego po rozrośnięciu tabeli może wymagać dużego przepisania danych. Przed migracją testuj kandydatów przy realistycznej nierównomierności. Badaj temperaturę partycji, fan-out transakcji, lokalność indeksów i zachowanie skanów, zamiast oceniać tylko całkowitą przepustowość.
Konflikty wymagają przeprojektowania przed zwiększaniem pojemności
Globalny licznik, pojedynczy wiersz konfiguracji lub jedno saldo sprzedawcy mogą serializować niezależne skądinąd żądania. Więcej węzłów nie usunie logicznego wymogu, by każda transakcja aktualizowała tę samą wartość.
Zastępuj dokładne liczniki globalne licznikami partycjonowanymi, gdy chwilowa agregacja jest akceptowalna. Wersjonuj konfigurację, zamiast często aktualizować jeden wiersz. Dla stanu pieniężnego zachowaj regułę księgową i znajdź współbieżność w wpisach append-only lub niezależnych subkontach, zamiast osłabiać poprawność.
Długie transakcje read-modify-write nasilają konflikty. Odczytuj najmniejszy konieczny zbiór, unikaj interakcji użytkownika wewnątrz transakcji i szybko zatwierdzaj. Jeśli praca biznesowa trwa minuty, przedstaw ją jako maszynę stanów obejmującą kilka krótkich transakcji.
Zachowanie przy ponowieniach należy do kontraktu aplikacji
Sterowniki mogą ponawiać pojedyncze instrukcje albo zwracać błąd możliwy do ponowienia do kodu aplikacji. Trzeba rozumieć, która warstwa odpowiada za pełne odtworzenie transakcji. Częściowe odtworzenie może użyć nieaktualnych decyzji lub pominąć wcześniejsze odczyty.
Pętla ponawiania powinna mieć maksymalną liczbę prób, losowy backoff i instrumentację. Zapisuj typ konfliktu, operację, liczbę prób i końcowy wynik. Nieograniczone ponowienia zamieniają konflikt w ukryte opóźnienie i mogą przeciążyć klaster.
Żądania biznesowe potrzebują stabilnych identyfikatorów, aby niepewną odpowiedź klienta dało się bezpiecznie sprawdzić. Gdy baza zatwierdzi zmianę, lecz odpowiedź zginie, klient powinien odpytać ustaloną operację zamiast wysyłać semantycznie nowe żądanie.
Zmiany schematu wymagają próby w skali produkcyjnej
Rozproszone zmiany schematu mogą szybko aktualizować metadane, gdy backfille i tworzenie indeksów działają w tle. Takie zadania zużywają storage, sieć i CPU oraz mogą wpływać na bieżące zapisy.
Stosuj migracje expand-and-contract. Najpierw dodaj zgodne pola lub tabele, wdroż kod obsługujący obie formy, wykonaj backfill w kontrolowanych partiach, przełącz odczyty, a potem usuń starą formę po weryfikacji. Plan wycofania powinien uwzględniać dane zapisane przez nową wersję.
Testuj duże migracje z wolumenem i topologią podobnymi do produkcji. Zmiana kończąca się szybko w małym klastrze staging może trwać w produkcji wiele godzin i konkurować z ruchem klientów. Przed rozpoczęciem monitoruj postęp, kontrolę pauzy, zapas dysku i zachowanie ponowień.
Lista wdrożeniowa i proof of concept
Użyteczny proof of concept testuje jedno reprezentatywne obciążenie wobec jawnych celów poprawności, opóźnień, odporności i kosztu. Ogólne benchmarki nie określą, czy konkretny schemat i aplikacja będą działać dobrze.
Wybierz obciążenie z rzeczywistymi ograniczeniami
Wybierz przepływ, np. rezerwację rzadkiego zasobu, księgowanie transferu lub provisionowanie tenanta w wymaganym regionie. Użyj schematu, zapytań, granic transakcji, rozmiarów payloadów i nierównomierności ruchu podobnych do produkcyjnych.
Zdefiniuj sukces przed testem:
- Poprawne wyniki przy współbieżności i ponowieniach
- Opóźnienia p50, p95 i p99 według regionu
- Utrzymana szczytowa przepustowość z rezerwą na awarie
- Zachowanie podczas awarii węzła i regionu
- Zmierzony koszt compute, storage i sieci
Margines bezpieczeństwa powinien wynikać z oczekiwanego wzrostu i pojemności na awarie, nie z arbitralnego mnożnika. Jeśli utrata jednego regionu mieści się w zakresie, pozostałe lokalizacje muszą obsłużyć przekierowane obciążenie podczas testu.
Zbuduj realistyczną powierzchnię aplikacji
API i mały interfejs użytkownika pokazują kolejność transakcji, zachowanie sterownika i opóźnienie odczuwane przez użytkownika, którego nie wyłapie narzędzie testujące wyłącznie bazę. Koder.ai może przez czat utworzyć interfejs React, backend Go i bazę PostgreSQL. Tryb planowania pomaga zdefiniować przepływ przed generowaniem, a eksport kodu źródłowego pozwala inżynierom dostosować warstwę danych do rozważanej bazy.
Traktuj wygenerowaną aplikację jako rusztowanie testowe, a nie dowód zgodności bazy. Uruchamiaj migracje, przeglądaj wygenerowany SQL, konfiguruj oficjalny sterownik i świadomie implementuj ponowienia transakcji. Snapshoty i rollback Koder.ai mogą chronić iteracje aplikacji, ale nie zastępują backupów bazy ani testów odtworzenia.
Koder.ai wspiera też wdrażanie i hosting, dzięki czemu można umieścić instancje testowej aplikacji blisko regionów bazy. Pozwala to mierzyć całą ścieżkę żądania zamiast uruchamiać każdy benchmark z jednej lokalizacji. Używaj danych syntetycznych, chyba że środowisko ma kontrole wymagane dla rekordów produkcyjnych.
Sprawdź normalną pracę i awarie
Test powinien obejmować stabilny ruch, skoki, gorące partycje, długie zapytania, zmiany schematu, pracę backupów i wymianę węzłów. Następnie przerwij łączność i usuń domenę awarii w zatwierdzonym środowisku testowym.
Zbieraj informacje o przerwanych transakcjach, próbach ponowienia, odpowiedziach o niedostępności, zmianach leadera, głębokości kolejki, użyciu dysku i transferze regionalnym. Zapisuj, co musiał zrobić operator. Automatyczne odzyskanie wymagające nieudokumentowanego kroku ręcznego nie jest jeszcze gotowe do produkcji.
Odtwórz backup do osobnego środowiska i zweryfikuj reguły aplikacji. Dla zapasów potwierdź, że przydziały nie przekraczają stanu. Dla księgi przelicz salda i sprawdź zbilansowane księgowania. Dla tenancy SaaS potwierdź, że rozmieszczenie i polityki dostępu przetrwały odtworzenie.
Zweryfikuj zgodność przed migracją
Zinwentaryzuj rozszerzenia bazy, procedury składowane, triggery, typy danych, założenia izolacji, funkcje ORM, zapytania raportowe, narzędzia backupu i skrypty administracyjne. Sklasyfikuj każdy element jako zgodny, zastępowalny lub blokujący.
Uruchom reprezentatywne migracje na kopii pełnego rozmiaru albo wygenerowanym zbiorze danych. Zmierz czas backfillu, opóźnienie change-data-capture, koszt równoległej pracy i czas przełączenia. Jeśli migracja używa podwójnych zapisów, określ sposób wykrywania rozbieżności i system autorytatywny na każdym etapie.
Shadow reads mogą porównywać wyniki bez zmiany stanu produkcji. Uwzględnij różnice czasowe i celowo nieaktualne zapytania, aby porównanie nie oznaczało oczekiwanej różnicy jako uszkodzenia. Każda niewyjaśniona rozbieżność w danych transakcyjnych wymaga rozwiązania przed przełączeniem.
Oceń gotowość produkcyjną
Przegląd produkcyjny powinien przypisać właścicieli dla działania bazy, ponowień aplikacji, bezpieczeństwa, polityki rezydencji, kosztów i reakcji na incydenty. Powinien obejmować dashboardy, alerty, runbooki, progi pojemności, dowody odtworzenia i punkt decyzji o wycofaniu.
Ostateczna decyzja nadal może brzmieć: pozostać przy PostgreSQL lub MySQL. Proof of concept odnosi sukces, gdy dostarcza wiarygodnych dowodów, nawet jeśli pokazują one, że opcja rozproszona kosztuje więcej, niż uzasadniają obecne wymagania. Gdy wymagania wspierają wdrożenie, migruj stopniowo, mierz każdy etap i zachowaj przetestowaną drogę powrotu, aż nowy system sprawdzi się przy rzeczywistym obciążeniu.
Często zadawane pytania
Czym w prostych słowach jest baza „distributed SQL”?
Baza distributed SQL udostępnia relacyjny interfejs SQL, czyli tabele, joiny, ograniczenia i transakcje, ale działa jako klaster na wielu maszynach, często w kilku regionach, zachowując się jak jedna logiczna baza danych.
W praktyce łączy:
- Znane zachowanie SQL/ACID
- Skalowanie poziome przez dodawanie węzłów
- Wysoką dostępność i odporność na awarie bez ręcznego shardingu
Czym distributed SQL różni się od tradycyjnego PostgreSQL lub MySQL?
Jednowęzłowy RDBMS albo układ primary/replica zwykle jest prostszy, tańszy i szybszy dla OLTP w jednym regionie.
Distributed SQL staje się atrakcyjny, gdy alternatywą jest:
- Sharding obsługiwany przez aplikację
- Złożony failover między regionami
- Wymóg silnej spójności między strefami lub regionami
- Rezydencja danych przy jednym modelu operacyjnym
Dlaczego systemy distributed SQL używają protokołów konsensusu, takich jak Raft lub Paxos?
Większość takich systemów opiera się na dwóch ideach:
- Replikacja: każdy shard lub partycja danych jest przechowywana na wielu węzłach.
- Konsensus: na przykład Raft lub Paxos, dzięki któremu repliki uzgadniają kolejność zapisów, a commit zwykle wymaga potwierdzenia większości.
To zapewnia silną spójność nawet przy awarii węzłów, ale wymaga dodatkowej komunikacji sieciowej.
Jak dane są dzielone i rozmieszczane między węzłami oraz regionami?
Tabele są dzielone na mniejsze fragmenty, często nazywane partycjami/shardami albo nazwami specyficznymi dla dostawcy, takimi jak ranges, tablets czy splits. Każda partycja:
- Ma własną grupę replik
- Może trafić do wybranych węzłów lub regionów
- Może zostać przeniesiona podczas równoważenia klastra
Zwykle wpływasz na rozmieszczenie za pomocą polityk, aby gorące dane i główni zapisujący byli blisko siebie, co ogranicza przejścia przez sieć.
Dlaczego transakcje w distributed SQL mogą być wolniejsze, zwłaszcza między regionami?
Transakcje rozproszone często dotykają wielu partycji, potencjalnie na różnych węzłach lub w innych regionach. Bezpieczny commit może wymagać:
- Blokad lub walidacji u uczestników
- Potwierdzeń replikacji przez kworum
- Skoordynowanej decyzji o commicie
Te dodatkowe rundy komunikacji sieciowej są głównym powodem wzrostu opóźnień zapisu, szczególnie gdy konsensus obejmuje regiony.
Jakie są najwyraźniejsze sygnały, że naprawdę potrzebuję distributed SQL?
Rozważ distributed SQL, gdy spełnione są co najmniej dwa warunki:
- Masz ważnych użytkowników w wielu regionach i potrzebujesz spójnych danych
- Potrzebujesz automatycznego failover między strefami lub regionami, przy rygorystycznym RTO/RPO
- Skalowanie pionowe przestało wystarczać dla zapisów
- Potrzebujesz silnej spójności dla kluczowych transakcji, takich jak pieniądze, zapasy czy rezerwacje
- Zgodność wymaga geograficznego rozmieszczenia danych
Jeśli obciążenie mieści się w jednym regionie z replikami i cache, klasyczny RDBMS często pozostaje lepszym wyborem.
Co daje silna spójność i jaki jest jej koszt?
Silna spójność oznacza, że po commicie transakcji odczyty nie zobaczą starszych danych.
W produkcie pomaga zapobiec:
- Podwójnemu wydaniu pieniędzy lub błędnym saldom
- Sprzedaży ostatniej sztuki więcej niż raz
- Rezerwacji tego samego miejsca przez dwie osoby
Kosztem jest to, że podczas podziału sieci system o silnej spójności może blokować lub odrzucać część operacji zamiast zaakceptować rozbieżne wersje prawdy.
Jak bezpiecznie obsługiwać ponowienia (idempotencję) w distributed SQL?
Polegać na ograniczeniach bazy i transakcjach:
- Zapisywać
idempotency_keylub podobny klucz dla każdego żądania albo próby - Dodać unikalne ograniczenie, np.
(account_id, idempotency_key) - W jednej transakcji zapisać rekord biznesowy oraz wpisy księgi lub outboxa
Dzięki temu ponowienia nie tworzą duplikatów. To ważne dla płatności, provisioningu i ponownego przetwarzania zadań w tle.
Jak wybrać między Spanner, CockroachDB i YugabyteDB?
Praktyczny podział wygląda tak:
- Spanner: zwykle usługa zarządzana w GCP, dojrzałe podejście do wielu regionów; wybór dialektu SQL wpływa na przenośność.
- CockroachDB: doświadczenie zbliżone do Postgresa i jego protokół sieciowy; wersja zarządzana lub self-hosted; nie jest w 100% zgodny z PostgreSQL.
- YugabyteDB: zgodne z PostgreSQL API SQL, YSQL, oraz opcjonalne API w stylu Cassandra, YCQL; wersja zarządzana lub self-hosted.
Przed wyborem przetestuj faktyczne ORM-y, migracje i używane rozszerzenia Postgresa. Nie zakładaj, że migracja będzie bezpośrednia.
Jaki plan proof of concept warto przygotować przed decyzją o distributed SQL?
Zacznij od skoncentrowanego PoC dla jednego krytycznego przepływu, np. checkoutu, rezerwacji lub księgowania. Sprawdź:
- Poprawność, bez podwójnych rezerwacji i utraconych aktualizacji
- Opóźnienia p50/p95 najważniejszych zapytań, również między regionami
- Zachowanie przy awariach węzła, strefy i, jeśli dotyczy, regionu
- Podstawy operacyjne: monitoring, kopie zapasowe i testy odtwarzania
Jeśli potrzebujesz pomocy z zakresem kosztów lub planów, sprawdź cennik. Powiązane wskazówki wdrożeniowe znajdziesz na blogu.