Wszyscy słyszeliście o skalowaniu — Twoja architektura powinna być skalowalna, powinieneś być w stanie skalować się w górę, aby sprostać wymaganiom, i tak dalej. Co to znaczy, gdy mówimy o bazach danych? Jak wygląda skalowanie za kulisami? Ten temat jest obszerny i nie sposób omówić wszystkich aspektów. Ta seria wpisów na dwóch blogach jest próbą uzyskania wglądu w temat skalowalności bazy danych.
Dlaczego skalujemy?
Najpierw przyjrzyjmy się, na czym polega skalowalność. Krótko mówiąc, mówimy o możliwości obsługi większego obciążenia przez Twoje systemy bazodanowe. Może to być kwestia radzenia sobie z krótkotrwałymi skokami aktywności, może to być kwestia radzenia sobie ze stopniowo zwiększanym obciążeniem w środowisku bazy danych. Istnieje wiele powodów, dla których warto rozważyć skalowanie. Większość z nich przychodzi z własnymi wyzwaniami. Możemy poświęcić trochę czasu na zapoznanie się z przykładami sytuacji, w których możemy chcieć zwiększyć skalę.
Wzrost zużycia zasobów
Jest to najbardziej ogólny — obciążenie wzrosło do tego stopnia, że istniejące zasoby nie są już w stanie sobie z tym poradzić. To może być wszystko. Zwiększyło się obciążenie procesora i klaster bazy danych nie jest już w stanie dostarczać danych z rozsądnym i stabilnym czasem wykonywania zapytań. Wykorzystanie pamięci wzrosło do tego stopnia, że baza danych nie jest już powiązana z procesorem, ale stała się powiązana z we/wy, przez co wydajność węzłów bazy danych została znacznie zmniejszona. Sieć może równie dobrze być wąskim gardłem. Możesz być zaskoczony, widząc, jakie limity związane z siecią mają przypisane Twoje instancje w chmurze. W rzeczywistości może to stać się najczęstszym ograniczeniem, z którym musisz się zmierzyć, ponieważ sieć to wszystko w chmurze – nie tylko dane przesyłane między aplikacją a bazą danych, ale także pamięć masowa jest podłączona przez sieć. Może to być również użycie dysku - po prostu kończy Ci się miejsce na dysku lub, co bardziej prawdopodobne, biorąc pod uwagę, że obecnie możemy mieć dość duże dyski, rozmiar bazy danych przerósł rozmiar „możliwy do zarządzania”. Konserwacja, taka jak zmiana schematu, staje się wyzwaniem, wydajność spada ze względu na rozmiar danych, a tworzenie kopii zapasowych trwa wieki. Wszystkie te przypadki mogą być uzasadnionym przypadkiem potrzeby zwiększenia skali.
Nagły wzrost obciążenia
Innym przykładem, w którym wymagane jest skalowanie, jest nagły wzrost obciążenia. Z jakiegoś powodu (np. działania marketingowe, wirusowe treści, sytuacja awaryjna lub podobna) Twoja infrastruktura doświadcza znacznego wzrostu obciążenia klastra bazy danych. Obciążenie procesora przekracza dach, dyskowe operacje wejścia/wyjścia spowalniają zapytania itp. Prawie każdy zasób, o którym wspomnieliśmy w poprzedniej sekcji, może zostać przeciążony i zacząć powodować problemy.
Planowana operacja
Trzeci powód, który chcielibyśmy podkreślić, to bardziej ogólny – jakaś zaplanowana operacja. Może to być zaplanowane działanie marketingowe, które spodziewasz się zwiększyć ruch, Czarny piątek, testowanie obciążenia lub prawie wszystko, co znasz z góry.
Każdy z tych powodów ma swoją własną charakterystykę. Jeśli potrafisz zaplanować z wyprzedzeniem, możesz szczegółowo przygotować proces, przetestować go i wykonać, kiedy tylko masz na to ochotę. Najprawdopodobniej spodoba ci się to w okresie „małego ruchu”, o ile coś takiego istnieje w twoich obciążeniach (nie musi istnieć). Z drugiej strony, nagłe skoki obciążenia, zwłaszcza jeśli są na tyle znaczące, aby wpłynąć na produkcję, wymuszą natychmiastową reakcję, bez względu na to, jak jesteś przygotowany i jak jest bezpieczny - jeśli Twoje usługi są już naruszone, możesz równie dobrze po prostu idź po to zamiast czekać.
Typy skalowania bazy danych
Istnieją dwa główne typy skalowania:pionowe i poziome. Oba mają wady i zalety, oba są przydatne w różnych sytuacjach. Przyjrzyjmy się im i omówmy przypadki użycia dla obu scenariuszy.
Skalowanie w pionie
Ta metoda skalowania jest prawdopodobnie najstarsza:jeśli twój sprzęt nie jest wystarczająco wydajny, aby poradzić sobie z obciążeniem, wzmocnij go. Mówimy tutaj po prostu o dodawaniu zasobów do istniejących węzłów z zamiarem uczynienia ich wystarczająco zdolnymi do radzenia sobie z postawionymi zadaniami. Ma to pewne reperkusje, które chcielibyśmy omówić.
Zalety skalowania pionowego
Najważniejsze jest to, że wszystko pozostaje takie samo. Miałeś trzy węzły w klastrze bazy danych, nadal masz trzy węzły, tylko bardziej wydajne. Nie ma potrzeby przeprojektowywania środowiska, zmiany sposobu, w jaki aplikacja powinna uzyskiwać dostęp do bazy danych - wszystko pozostaje dokładnie takie samo, ponieważ pod względem konfiguracji nic się tak naprawdę nie zmieniło.
Kolejną istotną zaletą skalowania w pionie jest to, że może być bardzo szybki, zwłaszcza w środowiskach chmurowych. Cały proces polega w zasadzie na zatrzymaniu istniejącego węzła, dokonaniu zmiany w sprzęcie, ponownym uruchomieniu węzła. W przypadku klasycznych konfiguracji lokalnych, bez żadnej wirtualizacji, może to być trudne – możesz nie mieć szybszego procesora dostępnego do wymiany, aktualizacja dysków do większych lub szybszych może również być czasochłonna, ale w przypadku środowisk chmurowych, publicznych lub prywatnych, może to być tak proste, jak uruchomienie trzech poleceń:zatrzymaj instancję, uaktualnij instancję do większego rozmiaru, uruchom instancję. Wirtualne adresy IP i ponownie podłączane woluminy ułatwiają przenoszenie danych między instancjami.
Wady skalowania pionowego
Główną wadą skalowania pionowego jest to, że po prostu ma swoje ograniczenia. Jeśli używasz największego dostępnego rozmiaru instancji i najszybszych woluminów dyskowych, niewiele więcej możesz zrobić. Nie jest też tak łatwo znacząco zwiększyć wydajność klastra bazy danych. Zależy to głównie od początkowego rozmiaru instancji, ale jeśli masz już całkiem wydajne węzły, możesz nie być w stanie osiągnąć 10-krotnego skalowania w poziomie przy użyciu skalowania pionowego. Węzły, które byłyby 10 razy szybsze, mogą po prostu nie istnieć.
Skalowanie w poziomie
Skalowanie w poziomie to zupełnie inna bestia. Zamiast zwiększać rozmiar instancji, pozostajemy na tym samym poziomie, ale rozszerzamy się w poziomie, dodając kolejne węzły. Ponownie, istnieją zalety i wady tej metody.
Zalety skalowania w poziomie
Główną zaletą skalowania w poziomie jest to, że teoretycznie niebo jest granicą. Nie ma sztucznego sztywnego limitu skalowania w poziomie, mimo że ograniczenia istnieją, głównie ze względu na coraz większe obciążenie komunikacji wewnątrz klastra z każdym nowym węzłem dodanym do klastra.
Kolejną istotną zaletą jest możliwość skalowania klastra bez konieczności przestojów. Jeśli chcesz uaktualnić sprzęt, musisz zatrzymać instancję, zaktualizować ją, a następnie zacząć od nowa. Jeśli chcesz dodać więcej węzłów do klastra, wszystko, co musisz zrobić, to aprowizować te węzły, zainstalować potrzebne oprogramowanie, w tym bazę danych, i pozwolić mu dołączyć do klastra. Opcjonalnie (w zależności od tego, czy klaster ma wewnętrzne metody udostępniania danych nowym węzłom) może być konieczne samodzielne zaopatrzenie go w dane. Zazwyczaj jest to jednak proces zautomatyzowany.
Wady skalowania w poziomie
Głównym problemem, z którym musisz się zmierzyć, jest to, że dodawanie coraz większej liczby węzłów utrudnia zarządzanie całym środowiskiem. Musisz być w stanie stwierdzić, które węzły są dostępne, taka lista musi być utrzymywana i aktualizowana z każdym nowym utworzonym węzłem. Możesz potrzebować zewnętrznych rozwiązań, takich jak usługa katalogowa (Consul lub Etcd), aby śledzić węzły i ich stan. To oczywiście zwiększa złożoność całego środowiska.
Kolejnym potencjalnym problemem jest to, że proces skalowania w poziomie wymaga czasu. Dodawanie nowych węzłów i dostarczanie im oprogramowania, a zwłaszcza danych, wymaga czasu. Ile zależy od sprzętu (głównie I/O i przepustowości sieci) oraz od rozmiaru danych. W przypadku dużych konfiguracji może to zająć znaczną ilość czasu i może to blokować sytuacje, w których skalowanie musi nastąpić natychmiast. Godziny oczekiwania na dodanie nowych węzłów mogą być nie do zaakceptowania, jeśli klaster bazy danych jest uszkodzony w takim stopniu, że operacje nie są wykonywane prawidłowo.
Wymagania wstępne dotyczące skalowania
Replikacja danych
Zanim będzie można podjąć jakąkolwiek próbę skalowania, Twoje środowisko musi spełniać kilka wymagań. Na początek Twoja aplikacja musi być w stanie korzystać z więcej niż jednego węzła. Jeśli może używać tylko jednego węzła, twoje opcje są ograniczone do skalowania w pionie. Możesz zwiększyć rozmiar takiego węzła lub dodać trochę zasobów sprzętowych do serwera bare metal i zwiększyć jego wydajność, ale to najlepsze, co możesz zrobić:zawsze będziesz ograniczony dostępnością bardziej wydajnego sprzętu i ostatecznie znajdziesz siebie bez możliwości dalszego zwiększania skali.
Z drugiej strony, jeśli masz środki do wykorzystania wielu węzłów bazy danych przez swoją aplikację, możesz skorzystać ze skalowania poziomego. Zatrzymajmy się tutaj i porozmawiajmy o tym, czego potrzebujesz, aby w pełni wykorzystać wiele węzłów.
Na początek możliwość oddzielenia odczytów od zapisów. Tradycyjnie aplikacja łączy się tylko z jednym węzłem. Ten węzeł jest używany do obsługi wszystkich zapisów i odczytów wykonywanych przez aplikację.
Dodanie drugiego węzła do klastra, z punktu widzenia skalowania, niczego nie zmienia . Musisz pamiętać, że jeśli jeden węzeł ulegnie awarii, drugi będzie musiał obsłużyć ruch, więc w żadnym momencie suma obciążenia obu węzłów nie powinna być zbyt wysoka, aby mógł sobie z nią poradzić jeden węzeł.
Dzięki dostępnym trzem węzłom możesz w pełni wykorzystać dwa węzły. Pozwala nam to na skalowanie części ruchu odczytu:jeśli jeden węzeł ma 100% przepustowości (a wolimy działać najwyżej na 70%), to dwa węzły stanowią 200%. Trzy węzły:300%. Jeśli jeden węzeł nie działa i jeśli pozostałe węzły wypchniemy prawie do granic możliwości, możemy powiedzieć, że jesteśmy w stanie pracować z 170-180% pojemności pojedynczego węzła, jeśli klaster jest zdegradowany. To daje nam ładne 60% obciążenia każdego węzła, jeśli wszystkie trzy węzły są dostępne.
Pamiętaj, że w tej chwili mówimy tylko o skalowaniu odczytów . W żadnym momencie replikacja nie może poprawić pojemności zapisu. W replikacji asynchronicznej masz tylko jeden zapis (główny), a w przypadku replikacji synchronicznej, takiej jak Galera, gdzie zbiór danych jest współdzielony przez wszystkie węzły, każdy zapis, który ma miejsce w jednym węźle, będzie musiał zostać wykonany na pozostałych węzłach grupa.
W trzywęzłowym klastrze Galera, jeśli zapiszesz jeden wiersz, w rzeczywistości zapiszesz trzy wiersze, po jednym dla każdego węzła. Dodanie większej liczby węzłów lub replik nie zrobi różnicy. Zamiast pisać ten sam wiersz na trzech węzłach, napiszesz go na pięciu. Dlatego dzielenie zapisów w klastrze multi-master, gdzie zestaw danych jest współdzielony przez wszystkie węzły (istnieją klastry multi-master, w których dane są shardowane, na przykład MySQL NDB Cluster - tutaj historia skalowalności zapisu jest zupełnie inna), nie ma sensu. Zwiększa obciążenie związane z rozwiązywaniem potencjalnych konfliktów zapisu we wszystkich węzłach, podczas gdy tak naprawdę nie zmienia niczego w odniesieniu do całkowitej pojemności zapisu.
Równoważenie obciążenia i podział odczytu/zapisu
Możliwość dzielenia odczytów i zapisów jest koniecznością, jeśli chcesz skalować odczyty w konfiguracjach replikacji asynchronicznej. Musisz mieć możliwość wysyłania ruchu zapisu do jednego węzła, a następnie wysyłania odczytów do wszystkich węzłów w topologii replikacji. Jak wspomnieliśmy wcześniej, ta funkcja jest również bardzo przydatna w klastrach z wieloma wzorcami, ponieważ pozwala nam usunąć konflikty zapisu, które mogą wystąpić, jeśli spróbujesz rozdzielić zapisy na wiele węzłów w klastrze. Jak możemy wykonać podział odczytu/zapisu? Możesz to zrobić na kilka sposobów. Zagłębmy się trochę w ten temat.
Podział R/W na poziomie aplikacji
Najprostszy scenariusz, również najrzadszy:Twoja aplikacja jest w stanie skonfigurować, które węzły powinny otrzymywać zapisy, a które odczyty. Funkcjonalność tę można skonfigurować na kilka sposobów, z których najprostszym jest zakodowana na sztywno lista węzłów, ale może to być również coś w rodzaju dynamicznego spisu węzłów aktualizowanego przez wątki w tle. Główny problem z tym podejściem polega na tym, że cała logika musi być napisana jako część aplikacji. W przypadku zakodowanej na stałe listy węzłów, najprostszy scenariusz wymagałby zmian w kodzie aplikacji dla każdej zmiany w topologii replikacji. Z drugiej strony, bardziej zaawansowane rozwiązania, takie jak wdrażanie wykrywania usług, byłyby bardziej skomplikowane w utrzymaniu na dłuższą metę.
Rozdzielone złącze R/W
Inną opcją byłoby użycie łącznika do wykonania podziału odczytu/zapisu. Nie wszyscy mają tę opcję, ale niektórzy tak. Przykładem może być php-mysqlnd lub Connector/J. Sposób integracji z aplikacją może się różnić w zależności od samego złącza. W niektórych przypadkach konfiguracja musi być wykonana w aplikacji, w innych w osobnym pliku konfiguracyjnym dla konektora. Zaletą tego podejścia jest to, że nawet jeśli musisz rozszerzyć swoją aplikację, większość nowego kodu jest gotowa do użycia i utrzymywana przez zewnętrzne źródła. Ułatwia to radzenie sobie z taką konfiguracją i musisz pisać mniej kodu (jeśli w ogóle).
Podział R/W w równoważniku obciążenia
Wreszcie jedno z najlepszych rozwiązań:loadbalancery. Pomysł jest prosty – przekaż swoje dane przez loadbalancer, który będzie w stanie odróżnić odczyty od zapisów i wysłać je w odpowiednie miejsce. Jest to duże ulepszenie z punktu widzenia użyteczności, ponieważ możemy oddzielić wykrywanie bazy danych i routing zapytań od aplikacji. Jedyne, co aplikacja musi zrobić, to wysłać ruch bazy danych do pojedynczego punktu końcowego, który składa się z nazwy hosta i portu. Reszta dzieje się w tle. Loadbalancery pracują nad kierowaniem zapytań do węzłów bazy danych zaplecza. Loadbalancery mogą również wykonywać wykrywanie topologii replikacji lub możesz zaimplementować odpowiednią inwentaryzację usług za pomocą etcd lub consul i zaktualizować ją za pomocą narzędzi do aranżacji infrastruktury, takich jak Ansible.
To kończy pierwszą część tego bloga. W drugim omówimy wyzwania, przed którymi stoimy podczas skalowania warstwy bazy danych. Omówimy również niektóre sposoby skalowania naszych klastrów baz danych.