Najlepsze podejście do shardowania tabel MySQL, aby tego nie robić, chyba że jest to całkowicie nieuniknione.
Kiedy piszesz aplikację, zwykle chcesz to robić w sposób, który maksymalizuje szybkość i szybkość programisty. Optymalizujesz opóźnienia (czas do uzyskania odpowiedzi) lub przepustowość (liczba odpowiedzi na jednostkę czasu) tylko wtedy, gdy jest to konieczne.
Partycjonujesz, a następnie przypisujesz partycje do różnych hostów (=odłamek) tylko wtedy, gdy suma wszystkich tych partycji nie mieści się już w pojedynczej instancji serwera bazy danych - powodem tego jest albo zapis, albo odczyt.
Przypadek zapisu to albo a) częstotliwość zapisów powoduje trwałe przeciążenie dysków tego serwera lub b) dzieje się zbyt wiele zapisów, więc replikacja trwale pozostaje w tej hierarchii replikacji.
Przypadek odczytu dla shardingu występuje wtedy, gdy rozmiar danych jest tak duży, że ich zestaw roboczy nie mieści się już w pamięci, a odczyty danych zaczynają trafiać na dysk zamiast być przez większość czasu obsługiwane z pamięci.
Tylko wtedy, gdy masz aby to zrobić.
W momencie shardowania płacisz za to na wiele sposobów:
Większość twojego SQL nie jest już deklaratywna.
Normalnie w SQL mówisz bazie danych, jakich danych potrzebujesz, i pozostawiasz optymalizatorowi przekształcenie tej specyfikacji w program dostępu do danych. To dobrze, ponieważ jest elastyczny, a pisanie tych programów dostępu do danych jest nudną pracą, która szkodzi szybkości.
W środowisku podzielonym prawdopodobnie łączysz tabelę w węźle A z danymi w węźle B lub masz tabelę większą niż węzeł w węzłach A i B i łączysz z niej dane z danymi znajdującymi się w węźle B i C. Zaczynasz ręcznie pisać rozwiązania łączenia oparte na hashowaniu po stronie aplikacji, aby rozwiązać ten problem (lub wymyślasz na nowo klaster MySQL), co oznacza, że otrzymujesz dużo SQL, które nie są już deklaratywne, ale wyrażają funkcjonalność SQL w sposób proceduralny (np. używasz instrukcji SELECT w pętlach).
Narażasz się na duże opóźnienia w sieci.
Zwykle zapytanie SQL można rozwiązać lokalnie, a optymalizator wie o kosztach związanych z dostępem do dysku lokalnego i rozwiązuje zapytanie w sposób minimalizujący koszty.
W środowisku podzielonym na fragmenty zapytania są rozwiązywane przez uruchamianie dostępu typu klucz-wartość w sieci do wielu węzłów (miejmy nadzieję, że z dostępem do kluczy wsadowych, a nie wyszukiwaniem poszczególnych kluczy w obie strony) lub przez wypchnięcie części WHERE
dalej do węzłów, w których można je zastosować (jest to nazywane „przesunięciem warunkowym”) lub obu.
Ale nawet w najlepszych przypadkach wiąże się to z dużo większą liczbą podróży w obie strony niż sytuacja lokalna i jest to bardziej skomplikowane. Zwłaszcza, że optymalizator MySQL nie wie w ogóle nic o opóźnieniach sieci (OK, klaster MySQL powoli staje się w tym lepszy, ale w przypadku waniliowego MySQL poza klastrem jest to nadal prawdą).
Tracisz dużo ekspresji języka SQL.
Ok, to prawdopodobnie mniej ważne, ale ograniczenia klucza obcego i inne mechanizmy SQL zapewniające integralność danych nie są w stanie objąć wielu fragmentów.
MySQL nie ma API, które pozwala na asynchroniczne zapytania, które są sprawne.
Gdy dane tego samego typu znajdują się w wielu węzłach (np. dane użytkowników w węzłach A, B i C), zapytania horyzontalne często muszą być rozwiązane na wszystkich tych węzłach ("Znajdź wszystkie konta użytkowników, które nie były zalogowane przez 90 dni albo więcej"). Czas dostępu do danych rośnie liniowo wraz z liczbą węzłów, chyba że można pytać o wiele węzłów równolegle, a wyniki są agregowane w miarę ich pojawiania się („Map-Reduce”).
Warunkiem tego jest asynchroniczne API komunikacyjne, które nie istnieje dla MySQL w dobrym stanie. Alternatywą jest wiele rozwidlenia i połączeń w procesach potomnych, czyli odwiedzanie świata ssania na przepustce sezonowej.
Po rozpoczęciu shardingu struktura danych i topologia sieci stają się widoczne jako wskaźniki wydajności aplikacji. Aby działać w miarę dobrze, Twoja aplikacja musi być tego świadoma, a to oznacza, że tak naprawdę tylko sharding na poziomie aplikacji ma sens.
Pytanie brzmi bardziej jeśli chcesz automatycznie podzielić (określenie który wiersz przechodzi do którego węzła przez hashowanie kluczy podstawowych na przykład) lub jeśli chcesz podzielić funkcjonalnie w sposób ręczny ("Tabele związane z historyjką użytkownika xyz idądo tego master, podczas gdy tabele powiązane z abc i def trafiają do tego mastera”).
Sharding funkcjonalny ma tę zaletę, że jeśli zostanie wykonany prawidłowo, przez większość czasu jest niewidoczny dla większości programistów, ponieważ wszystkie tabele związane z ich historyjką użytkownika będą dostępne lokalnie. To pozwala im nadal korzystać z deklaratywnego SQL tak długo, jak to możliwe, a także spowoduje mniejsze opóźnienia w sieci, ponieważ liczba transferów międzysieciowych jest minimalna.
Fragmentacja funkcjonalna ma tę wadę, że nie pozwala na to, aby pojedyncza tabela była większa niż jedna instancja i wymaga ręcznej uwagi projektanta.
Sharding funkcjonalny ma tę zaletę, że można go stosunkowo łatwo wykonać na istniejącej bazie kodu z wieloma zmianami, które nie są zbyt duże. http://Booking.com zrobił to wiele razy w ciągu ostatnich lat i zadziałało to dla nich dobrze.
Powiedziawszy to wszystko, patrząc na twoje pytanie, uważam, że zadajesz niewłaściwe pytania lub całkowicie nie rozumiem twojego opisu problemu.