Jak firma Roblox wykorzystała Theta Sketches do skalowania analiz twórców

Analizy są niezbędne w dzisiejszych grach wieloosobowych działających w czasie rzeczywistym. W Roblox skupiamy się na tworzeniu narzędzi pomiarowych, które pomagają naszym twórcom osiągać sukcesy. Nasze bezpłatne, gotowe do użycia narzędzia analityczne dają twórcom natychmiastowy wgląd w rozwój ich doświadczeń, pozyskiwanie użytkowników i ich utrzymanie, pomagając im osiągnąć maksymalny sukces.
Stworzenie aktualnych systemów analitycznych, na których polegają miliony twórców Roblox, to spore wyzwanie. Aby mu sprostać, zoptymalizowaliśmy nasz silnik zapytań analitycznych tak, aby klaster przetwarzający o 120 rdzeniach mógł obsłużyć ponad 6 milionów zapytań dziennie od około 300 000 odwiedzających, którzy mają dostęp do 86 TB danych. Sercem naszego rozwiązania jest baza danych do przetwarzania analitycznego online (OLAP), którą wybraliśmy ze względu na jej skalowalność i integrację z algorytmami aproksymacji. Wykorzystując połączenie technik agregacji danych oraz algorytmów HyperLogLog i Theta Sketch, zapewniamy analitykę dla milionów doświadczeń Roblox1.
Podstawy analizy OLAP
Im więcej danych jest wyszukiwanych, tym dłużej trwa generowanie wyników. Gdy uda nam się zmniejszyć ilość potrzebnych danych i przyspieszyć proces analizy, twórcy mogą uzyskać wgląd w swoje działania niemal w czasie rzeczywistym. Niektóre z technik, które wykorzystujemy, to:
- Przechowywanie kolumnowe: OLAP, Druid, odczytuje tylko niezbędne kolumny.
- Filtry partycjonowania i sortowania: OLAP odczytuje tylko te pliki, które indeksują bezpośrednio potrzebne bloki danych.
- Rollup: OLAP częściowo agreguje zdarzenia przy użyciu wspólnych grupowań.
W szczególności rollupy umożliwiają systemom OLAP działanie pomiędzy największymi silnikami zapytań SQL, takimi jak Spark lub Presto (o opóźnieniach rzędu kilkudziesięciu sekund), a zapytaniami punktowymi lub ograniczonymi zapytaniami SQL, które zazwyczaj dostarczają w pełni zagregowane dane. Dzięki rollupom zapytania są indeksowane według grup wymiarów, co skutkuje znacznym zmniejszeniem całkowitej kardynalności wierszy. W przypadku analizowania miliardów, a nawet bilionów surowych zdarzeń, znacznie bardziej efektywne może być zebranie ich w miliony grup, które można agregować z opóźnieniem poniżej sekundy. Na przykład:

Chociaż agregacje oferują wspomniane powyżej korzyści związane z redukcją danych, niektóre metryki są na nie odporne, w tym zapytania wymagające pełnego sortowania tabeli surowych danych, takie jak zliczenia unikalnych wartości, percentyle i zapytania o częstotliwość.
Na szczęście możemy obejść te ograniczenia za pomocą technik, które zwracają statystycznie ograniczony wynik przybliżony w oparciu o złożone struktury danych zawierające próbkę pełnego zbioru danych. Struktury te są zaprojektowane do wykorzystania w technikach rollup i łączą dwa odrębne zliczenia za pomocą operacji sumowania, podobnie jak w przypadku dodawania dwóch liczb.
Analiza obciążeń związanych z analizą danych Roblox
W Roblox udostępniamy twórcom scentralizowany pulpit nawigacyjny, na którym mogą znaleźć najważniejsze dane analityczne. Obejmują one:
- Zaangażowanie: dzienni aktywni użytkownicy (DAU), miesięczni aktywni użytkownicy (MAU), retencja i lejki
- Monetyzacja: przychody, średni przychód na użytkownika, sprzedaż i ekonomia
- Dane dotyczące pozyskiwania użytkowników
- Personalizacja miniatur i wyniki eksperymentów
- Analizy rekomendacji na stronie głównej
- Wkrótce więcej.

Wybór i optymalizacja silnika zapytań
Pokonywanie wyzwań związanych z wydajnością
Podczas ostatniej rundy testów produkcyjnych w środowisku shadow odkryliśmy istotne wyzwanie: wydajność zapytań MAU wymagała poprawy po przejściu z pojedynczych dużych zapytań na wzorce agregacji dziennej. Są one kluczowe dla naszych wizualizacji analitycznych twórców.
Stwierdziliśmy, że struktura zapytania miała ogromny wpływ na podstawową wydajność naszego rozwiązania OLAP. Standardowe zapytania z wieloma etapami wykonywania (takie jak zagnieżdżone instrukcje „GROUP BY”2) często przenoszą dużą część pracy na lekkie węzły brokerskie.
Jest to klasyczny problem związany z dużymi zbiorami danych, w którym część zapytania jest ostatecznie wykonywana na ważnych, małych węzłach obsługujących. Spodziewaliśmy się, że nasze przybliżone struktury danych będą działać jak proste zliczenia lub sumy, ale odkryliśmy, że w rzeczywistości zachowują się one zupełnie inaczej.
Poniższy rysunek ilustruje ten problem. Pokazuje on, w jaki sposób nasze węzły historyczne przeprowadzałyby częściową agregację, tworząc Theta Sketch dla każdego dnia, a następnie przesyłając dane z powrotem do brokera. Następnie broker próbowałby scalić każdy duży dzienny szkic w jedną wartość miesięczną na dzień. W przypadku 30 dni MAU oznaczało to scalanie 1800 szkiców Theta o maksymalnym rozmiarze na brokerze, co skutkowało wolniejszym, podatnym na awarie zapytaniem, które monopolizowało procesor brokera.

Naszym rozwiązaniem było uruchomienie OLAP z mniejszą liczbą dużych modułów historycznych, aby zmaksymalizować lokalność danych dla źródeł danych opartych na zapytaniach przybliżonych. W praktyce spowodowało to przeniesienie operacji scalania, która mogła wymagać przetworzenia ponad 100 MB danych, z powrotem na nasze węzły historyczne.
Aby osiągnąć to w SQL, użyliśmy połączenia wbudowanego, aby zapytania propagowały niezbędne informacje do węzłów historycznych, i przygotowaliśmy zapytanie z listą dat wyników wbudowanych. Każda data wyniku może następnie zebrać odpowiednie dane z segmentów węzłów historycznych. Dane są następnie przekazywane z powrotem do brokera, gdzie wyniki są szybko scalane w jedną mapę daty wyniku do danych metrycznych, jak widać poniżej.

Ta optymalizacja miała ogromny wpływ na wydajność w przypadku zapytań na dużą skalę. W przypadku podziału MAU dla dużego serwisu według krajów średnia wydajność zapytań poprawiła się 5-krotnie (z 17,53 sekundy do 3,23), jak pokazano na poniższym wykresie. Odnotowaliśmy również 50-krotne zmniejszenie czasu pracy procesora na brokerze (z 16,83 sekundy do 0,34).
Chociaż wyniki mogą się różnić, podkreśla to znaczenie ostrożnego traktowania złożonych operacji (takich jak scalanie milionów szkiców). Zakładanie, że operacje te są równoważne z prostymi agregacjami, może prowadzić do poważnych problemów z wydajnością, zwłaszcza w systemach, w których powszechne są agregacje klientów ostatniej mili.
Podsumowania i teoretyczna kostka theta

Zbadaliśmy również kostkę theta, czyli uogólnione podejście mające na celu wypełnienie luki między podstawowymi tabelami zbiorczymi a tabelami zawierającymi wyłącznie surowe dane poprzez przybliżone przecięcia zbiorów. Podejście to rozwiązuje fundamentalne ograniczenie: tabele zbiorcze tracą swoją przewagę, gdy zapytania muszą dotyczyć wielu warstw wymiarów o wysokiej kardynalności. Dzieje się tak, ponieważ każdy wymiar powoduje, że kardynalność zbiorcza skaluje się jako ∏dim (iloczyn wymiarów).
Zaprojektowaliśmy system, który agregowałby według wspólnych grup wymiarów z ograniczeniem kardynalności, umożliwiając zapytania o wydajności rollupowej dotyczące dowolnego elementu w grupie. Następnie, szukając kombinacji wymiarów w różnych grupach, próbowalibyśmy wykonać przybliżone połączenie join4 między zbiorami i zwrócić wyniki metryczne wraz z oszacowaniami błędów. Zapytanie o wysokim szacowanym błędzie byłoby przekazywane do tabeli surowej, gdzie wiele filtrów powinno umożliwić duże optymalizacje typu pushdown.


Ponieważ możemy szybko obliczyć ten wskaźnik błędu, służy on również jako wyraźny sygnał, że odczytanie surowej tabeli będzie prawdopodobnie wydajne. W przypadkach, gdy nakładające się dane są niewielkie w stosunku do sumy (na przykład osoby mówiące po japońsku w Niemczech), duża liczba wierszy surowej tabeli zostanie odfiltrowana. Skutkuje to wydajnymi optymalizacjami typu pushdown. System wykorzystujący grupy wymiarów, przybliżone połączenia i odczyty surowej tabeli oparte na błędach naprawdę zmaksymalizowałby wydajność rollupu w przypadku zapytań sprzyjających przybliżeniom.
W przypadku Roblox rozwiązanie to będzie bardziej przydatne na kolejnym poziomie skalowania — potencjalnie do analizy dynamicznych lejków lub niestandardowych zdarzeń — podczas gdy nasza obecna prosta replika rollup zaspokaja dzisiejsze potrzeby.
Tworzenie platformy samoobsługowej
Po zoptymalizowaniu naszego brokera zajęliśmy się tworzeniem narzędzi do wdrażania i wyszukiwania zbiorów danych dodanych do naszego rozwiązania OLAP. Stworzyliśmy bibliotekę open source Spark i Trino UDAF dla naszych funkcji datasketch, umożliwiając Sparkowi korzystanie z tego samego binarnego formatu datasketch, co nasz OLAP6. Dzięki temu większość obciążenia obliczeniowego pozostała w Sparku, co pomogło ujednolicić aproksymację w całym Robloxie, potencjalnie zmniejszając koszty obliczeniowe nawet o 80% dla niektórych zbiorów danych.
Uprościliśmy wdrażanie dzięki wewnętrznemu rozszerzeniu naszego harmonogramu zadań wsadowych i zdefiniowaliśmy API w stylu dataframe, które pomaga programistom w podejmowaniu decyzji dotyczących ostatecznych miar i wymiarów, zmniejszając wpływ otwartych zapytań. Udostępniliśmy również na licencji open source kilka przykładowych przepływów pracy pokazujących, w jaki sposób ładujemy i przeszukujemy te dane w naszym OLAP.
Nasze zoptymalizowane zbiory danych analitycznych zapewniają teraz twórcom dogłębny wgląd w dane. Dzięki optymalizacji średnia wydajność wzrosła czterokrotnie, a wydajność w najgorszym przypadku – 50-krotnie. Platforma samoobsługowa umożliwia naszemu zespołowi Creator Analytics ciągłe udoskonalanie nowych zbiorów danych dla programistów. Cieszymy się, że programiści różnej wielkości korzystają z tych narzędzi, aby tworzyć niesamowite doświadczenia w Roblox.
1 Obliczone na podstawie unikalnych użytkowników z dowolnym dostępem
w ciągu ostatnich 60 dni 2 Tak jak w tym prostym zapytaniu
o MAU 3 Wyniki dotyczą okresu od 21 do 28 marca 2025 r.
4 Wykonane w następujący sposób: SELECT c.experience_id, c.country, p.platform, THETA_INTERSECT(c.user_theta, p.user_theta) from (select experience_id, country, user_theta from theta_cube where agg_level = country) c union (select experience_id, platform, user_theta from theta_cube where agg_level = platform) p
5 https://datasketches.apache.org/docs/Theta/ThetaSketchSetOpsAccuracy.html
6 Za pomocą funkcji SQL w Druidzie COMPLEX_DECODE_BASE64('HLLSketch', sketch_col_name ).


