Treści na tej stronie zostały przetłumaczone przy użyciu sztucznej inteligencji (AI) lub technologii tłumaczenia maszynowego i mogą zawierać błędy.

Skip to content

Jak pamięć podręczna Roblox utrzymała 1,38 mld QPS, przekraczając limity Redis

Radzenie sobie z dziesięciokrotnym wzrostem ruchu z myślą o niezawodności i wydajności

W środowisku gier wieloosobowych na ogromną skalę, jakim jest Roblox, użytkownicy odczytują i zapisują dane w ilościach i z prędkością, które przerosłyby standardowe wzorce buforowania. Użytkownik może nie zauważyć 200-milisekundowego opóźnienia na standardowej stronie internetowej, która buforuje dane statyczne, ale 200-milisekundowe opóźnienie w pobieraniu danych interakcji lub metadanych serwera w Roblox może zatrzymać rozgrywkę i zakłócić symulowany świat gry. Roblox działa w ekosystemie nieprzewidywalnych, szybkich i efemerycznych danych, które muszą być pobierane w ciągu milisekund, aby zachować immersję. 

Nasza warstwa buforowania została zaprojektowana tak, aby działać jak ogromny amortyzator między milionami jednoczesnych użytkowników a naszą trwałą pamięcią masową. Obsługuje ona szczytowe obciążenie wynoszące 1,38 miliarda zapytań na sekundę (QPS), przy czym pojedyncze klastry logiczne osiągają nawet 100 milionów QPS. W tym poście ujawnimy, w jaki sposób zaprojektowaliśmy federacyjną usługę buforowania, aby skalować się poza ograniczenia węzłów Redis® w celu uzyskania ogromnej przepustowości, jednocześnie radząc sobie z bezprecedensowym 10-krotnym wzrostem ruchu w naszym największym klastrze.

Łączna liczba operacji odczytu/zapisu w pamięci podręcznej we wszystkich węzłach buforujących.

Skalowanie wykraczające poza granice branżowe w celu zaspokojenia stale rosnących potrzeb

W Roblox nasza usługa buforowania opiera się na technologii Redis®, a jeden klaster Redis® może obsługiwać nawet 1000 węzłów. Jednak empiryczny i przyjęty w branży limit jest znacznie niższy — wynosi około 400–500 węzłów.1 Wynika to z zdecentralizowanego protokołu Gossip w Redis®, który wykorzystuje komunikację między węzłami do udostępniania informacji o kondycji, statusie i mapowaniu fragmentów. W klastrach większych niż kilkaset węzłów komunikacja w ramach protokołu Gossip zaczyna zużywać znaczne zasoby procesora i sieci. Każdy węzeł musi przetwarzać aktualizacje statusu od coraz większej liczby węzłów równorzędnych, co odbiera zasoby przeznaczone na podstawowe zadanie, jakim jest dostarczanie danych z niskim opóźnieniem. To obciążenie płaszczyzny sterowania może prowadzić do niestabilności i nieprzewidywalnej wydajności, dlatego dostawcy zarządzanych usług w chmurze nakładają ograniczenia dotyczące liczby węzłów. Jest to praktyczny pułap, którego nie można przekroczyć bez narażenia niezawodności.

Ten praktyczny limit pojedynczego klastra nie wystarcza do obsługi platformy działającej na skalę Roblox, gdzie największa usługa backendowa wymagała w szczytowym momencie ponad 100 milionów QPS od jednego z naszych logicznych klastrów Redis®, co stanowi wzrost z 10 milionów w ciągu trzech miesięcy.

Aby przezwyciężyć to ograniczenie architektoniczne, wdrożyliśmy architekturę federacyjną, umieszczając odwrotny serwer proxy klienta przed wieloma mniejszymi, niezależnymi klastrami Redis®. Stosujemy limit 400 węzłów na klaster, aby zapewnić przewidywalną wydajność. 

Z perspektywy naszych tysięcy aplikacji i usług klientowski serwer proxy odwrotny przedstawia te rozbieżne klastry jako pojedynczą, ujednoliconą i skalowalną usługę buforowania. Ta federacja pozwala nam skalować się horyzontalnie niemal bez ograniczeń poprzez proste dodawanie kolejnych klastrów do federacji, całkowicie omijając ograniczenia protokołu Gossip. Obecnie nasze największe pojedyncze wdrożenie buforowania składa się z ponad 6000 węzłów Redis®, które są wspierane przez ponad 15 niezależnych klastrów Redis® — skala ta jest możliwa wyłącznie dzięki temu modelowi federacyjnemu.

Schemat ogólny architektury federacyjnej.

Trzyetapowy proces migracji

Przejście z tradycyjnej konfiguracji na architekturę federacyjną przy jednoczesnym zarządzaniu setkami aktywnych klastrów produkcyjnych było poważnym przedsięwzięciem. Pod koniec 2024 r. wiele z tych klastrów zbliżało się już do naszego limitu 400 węzłów, co sprawiało, że przejście na model o większej pojemności stało się pilne. Głównym wyzwaniem było to, że standardowe instancje Redis® działają w izolacji i nie mają wbudowanej świadomości istnienia innych klastrów, co wymagało niestandardowej metody replikacji danych podczas przenoszenia.

Aby rozwiązać ten problem, opracowaliśmy przejrzysty, trzyetapowy proces migracji, zaprojektowany tak, aby umożliwić przeniesienie usług bez udziału klienta i bez zmian po stronie aplikacji.

Przebieg migracji

  1. Podwójne zapisywanie: Skonfigurowaliśmy odwrotny serwer proxy klienta tak, aby wykonywał podwójne zapisy, wysyłając przychodzące dane jednocześnie zarówno do oryginalnego klastra, jak i do nowego, zfederowanego miejsca docelowego.
  2. Równowaga i przełączanie odczytu: Gdy proces podwójnego zapisu zapełnił nowy klaster identycznymi danymi, a ponieważ wszystkie dane są ograniczone czasem życia (TTL), przenieśliśmy ruch odczytu ze starego klastra do nowej architektury federacyjnej.
  3. Wycofanie z eksploatacji: Po potwierdzeniu stabilności nowego klastra wstrzymaliśmy wszystkie operacje zapisu do pierwotnego klastra i wycofaliśmy go z eksploatacji.

Z powodzeniem przenieśliśmy nasze najbardziej krytyczne usługi do modelu federacyjnego, który obsługuje ponad 6000 węzłów, znacznie przekraczając ograniczenia pojedynczego klastra Redis®. 

Radzenie sobie z gwałtownym wzrostem popularności serwisu Grow a Garden

Począwszy od kwietnia 2025 r. stanęliśmy przed wyzwaniem związanym z wydajnością na skalę, z jaką rzadko spotykają się inne usługi. Katalizatorem był fenomenalny sukces Grow a Garden. W ciągu zaledwie kilku tygodni od premiery Grow a Garden zaczęło pobijać nie tylko rekordy Roblox, ale także rekordy całej branży gier pod względem liczby użytkowników jednocześnie (CCU). W ciągu zaledwie trzech miesięcy liczba graczy przekroczyła nasz poprzedni rekord CCU dla pojedynczej gry, wynoszący 2,8 miliona graczy, osiągając szczytową wartość ponad 21 milionów. Ten gwałtowny wzrost spowodował 10-krotny wzrost ruchu w naszym największym klastrze Redis® i 3-krotny wzrost ruchu w całym systemie buforowania.

Roblox obsługuje nasze systemy buforowania na fizycznych maszynach w naszych własnych centrach danych, a ten bezprecedensowy wzrost nie był częścią naszego długoterminowego planu wydajności. Aby przetrwać tę falę i utrzymać stabilność platformy, musieliśmy radykalnie poprawić wydajność naszej istniejącej floty, jednocześnie stawiając na pierwszym miejscu niezawodność. 

Poprawa wydajności podczas gwałtownego wzrostu

Po pierwsze, musieliśmy zidentyfikować najbardziej znaczące wąskie gardła w naszym systemie, abyśmy mogli ustalić priorytety prac optymalizacyjnych. Roblox Caching działa na dwóch różnych pulach sprzętowych: jednej dla węzłów zaplecza Redis® i jednej dla Envoy. Nasza flota Redis® jest głównie ograniczona pamięcią, podczas gdy proxy klienta jest ograniczone mocą obliczeniową. 

W przypadku naszej floty Redis® ograniczonej pamięcią wdrożyliśmy nową politykę planowania, aby lepiej zrównoważyć wykorzystanie pamięci w naszych grupach maszyn fizycznych. Dostosowaliśmy również rozmiar kontenerów, aby zmniejszyć fragmentację pamięci. 

Aby poprawić wykorzystanie procesora, wdrożyliśmy dwie kluczowe optymalizacje w naszym systemie pamięci podręcznej:

  1. Automatyczne skalowanie proxy klienta: Dostosowaliśmy politykę automatycznego skalowania dla naszej floty proxy klienta, która jest ograniczona mocą obliczeniową. Obniżając dopuszczalne odchylenie od docelowego obciążenia procesora, osiągnęliśmy wyższe średnie wykorzystanie procesora dla zadań bez zmiany progu skalowania w górę.
  2. Wewnętrzne kontrole stanu klastra: Zoptymalizowaliśmy wewnętrzne kontrole stanu, aby wyeliminować niepotrzebne sygnały kontrolne, zachowując pierwotny interwał wykrywania i oszczędzając jednocześnie zasoby obliczeniowe.

Zmiany te spowodowały prawie 10-procentową poprawę wykorzystania procesora. Na koniec umieściliśmy te dwa bardzo różne obciążenia w tej samej puli maszyn z izolacją na poziomie kontenerów cgroup, co zmniejszyło nasze ogólne zapotrzebowanie na moc obliczeniową o 25%.

Inżynieria na rzecz niezachwianej niezawodności

Niezawodność pozostała naszym priorytetem, nawet gdy staraliśmy się sprostać rekordowemu zapotrzebowaniu. 

Kompleksowa obserwowalność

Najpierw skupiliśmy się na zrozumieniu potencjalnych awarii, inwestując znaczne środki w nasz stos obserwowalności w celu kompleksowego, kompleksowego monitorowania stanu naszego systemu buforowania. Śledząc sukcesy serwerów i awarie po stronie klienta w całym cyklu życia żądania, szybko wykryliśmy i zdiagnozowaliśmy problemy, często zapobiegając ich wpływowi na użytkowników. Na przykład dodaliśmy funkcję wykrywania kluczy gorących, która pobiera próbki ruchu na żywo w celu zidentyfikowania danych, do których dostęp jest nadmierny i które obciążają warstwę buforowania. Gdy system zidentyfikuje klucz gorący, możemy proaktywnie ograniczyć ruch do tych kluczy zarówno po stronie serwera, jak i klienta, aby zapobiec przeciążeniu systemu.

Obsługa awarii

W każdym systemie rozproszonym tej skali zdarzają się czasami częściowe awarie. Zaprojektowaliśmy nasz system tak, aby płynnie radził sobie z tymi awariami i minimalizował ich wpływ.

  • Częściowe awarie w żądaniach wsadowych: Przeprojektowaliśmy system, aby radził sobie z częściowymi awariami w żądaniach wsadowych. Wcześniej pojedyncza awaria żądania w podzielonym na fragmenty żądaniu wsadowym powodowała niepowodzenie całej partii. Teraz żądania mogą zakończyć się powodzeniem, nawet jeśli niektóre klucze nie zostaną pobrane.
  • Ograniczanie blokowania na początku kolejki: Od systemów buforujących oczekuje się szybkości. Nawet niewielkie spowolnienie sieci może spowodować gromadzenie się żądań i przekroczenie limitu czasu, co jest zjawiskiem znanym jako blokowanie na początku kolejki. Wdrożyliśmy inteligentne mechanizmy ponawiania prób oraz strategie puli połączeń zarówno w warstwie klienckiej, jak i proxy, aby zapobiec sytuacji, w której pojedyncze spowolnienie żądania wpływa na pozostałe.
  • Wprowadzanie błędów i opóźnień: Aby przetestować odporność, regularnie wprowadzamy kontrolowane awarie (awarie węzłów, awarie szaf, opóźnienia sieciowe, błędy API) do środowiska produkcyjnego za pomocą naszej platformy do wprowadzania błędów. Analizując zachowanie systemu w tych warunkach, proaktywnie identyfikujemy i naprawiamy słabe punkty, zapewniając ciągłą niezawodność.

Odporność po stronie klienta

Aby zapobiec przeciążeniu naszego systemu buforowania przez nagłe wzrosty ruchu, wdrożyliśmy solidną odporność na awarie po stronie klienta. Mechanizm ten działa na poziomie aplikacji, zapewniając, że żadna pojedyncza usługa nie może zmonopolizować zasobów pamięci podręcznej.

  • Ograniczanie przepustowości w celu kontroli przepływu: Nasz klient ma limit całkowitego ruchu wychodzącego, co gwarantuje, że nie wysyła więcej żądań, niż usługa backendowa jest w stanie obsłużyć. Żądania są ograniczane, gdy limit ten zostanie przekroczony, co zapobiega przeciążeniu usługi.
  • Mechanizmy ponownych prób i limitów czasu: Nasze biblioteki klienckie wykorzystują starannie dostrojone mechanizmy ponownych prób, w tym limity czasu i limit ponownych prób, aby zapobiec problemom typu „thundering herd”. Ogranicza to zasięg awarii, zwalnia zasoby i poprawia ogólną responsywność systemu, zapobiegając przeciążeniu systemu przez nieograniczone zawieszanie się i jednoczesne ponowne próby.

Przyszłość buforowania w Roblox

Droga Roblox do buforowania zaczęła się od klastrów Redis® typu bare-metal, zarządzanych bezpośrednio przez właścicieli aplikacji, potem przeszliśmy do klastrów Memcached, zarządzanych przez nasz zespół ds. buforowania, a na końcu przeszliśmy na model Redis®-as-a-service, gdzie klaster Redis® jest dostarczany jako usługa zarządzana. Chociaż model ten okazał się bardzo skuteczny w naszej obecnej skali, dostrzegamy jego ograniczenia pod względem wydajności usługi i izolacji zasobów dla platformy tak dynamicznej jak Roblox.

W miarę rozwoju platformy planujemy zainwestować znaczne środki w usługę buforowania nowej generacji opartą na ValKey. Architektura ta została zaprojektowana tak, aby radykalnie obniżyć koszty poprzez współdzielenie wspólnych zasobów w ramach wielodostępności, zapewniając jednocześnie izolację danych, zasobów i awarii dla każdego użytkownika. Naszym celem jest znaczne zwiększenie wydajności, skalowalności, niezawodności i opłacalności naszej infrastruktury buforowania, aby sprostać wymaganiom coraz bardziej rozbudowanych doświadczeń na Roblox.

Podziękowania: 

To osiągnięcie jest wynikiem wspólnych wysiłków. Dziękujemy innym członkom zespołu, którzy wnieśli znaczący wkład: Utkarsh Singh, Karthick Ramachandran, Vineesha Kasireddy, Weiji Hu i Leon Gao.

1AWS ogranicza liczbę węzłów ElasticCache do 500.

*Redis jest znakiem towarowym firmy Redis Labs Ltd. Wszelkie prawa do niego są zastrzeżone dla firmy Redis Labs Ltd.