Die Inhalte dieser Website wurden mithilfe künstlicher Intelligenz (KI) oder maschineller Übersetzungstechnologie übersetzt und können Fehler enthalten.

Skip to content

Wie der Cache von Roblox 1,38 Milliarden QPS über die Grenzen von Redis hinaus aufrechterhielt

Bewältigung eines 10-fachen Traffic-Anstiegs mit Blick auf Zuverlässigkeit und Effizienz

In der Massive-Multiplayer-Spielumgebung von Roblox lesen und schreiben Nutzer Daten in einem Umfang und mit einer Geschwindigkeit, die herkömmliche Caching-Muster überfordern würden. Ein Nutzer bemerkt vielleicht keine Verzögerung von 200 ms auf einer Standard-Webseite, die statische Daten zwischenspeichert, aber eine Verzögerung von 200 ms beim Abrufen von Interaktionsdaten oder Server-Metadaten auf Roblox könnte das Gameplay zum Erliegen bringen und die simulierte Spielwelt stören. Roblox basiert auf einem Ökosystem aus unvorhersehbaren, hochgeschwindigkeitsfähigen und kurzlebigen Daten, die innerhalb von Millisekunden abgerufen werden müssen, um das Eintauchen in die Spielwelt aufrechtzuerhalten. 

Unsere Caching-Schicht ist so konzipiert, dass sie als massiver Stoßdämpfer zwischen Millionen gleichzeitiger Nutzer und unserem persistenten Speicher fungiert. Sie bewältigt eine Spitzenlast von 1,38 Milliarden Abfragen pro Sekunde (QPS), wobei einzelne logische Cluster 100 Millionen QPS erreichen. In diesem Beitrag geben wir einen Einblick, wie wir einen föderierten Caching-Dienst entwickelt haben, der über die Grenzen von Redis®-Knoten hinaus skaliert, um einen enormen Durchsatz zu erzielen – und das bei einem beispiellosen 10-fachen Anstieg des Datenverkehrs auf unserem größten Cluster.

Gesamtzahl der Lese-/Schreibvorgänge im Cache über alle Caching-Knoten hinweg.

Skalierung über die Branchengrenzen hinaus, um den ständig wachsenden Anforderungen gerecht zu werden

Bei Roblox basiert unser Caching-Dienst auf der Redis®-Technologie, und ein Redis®-Cluster kann potenziell bis zu 1.000 Knoten unterstützen. Die empirisch ermittelte und in der Branche akzeptierte Grenze liegt jedoch deutlich niedriger – bei etwa 400–500 Knoten.1 Dies liegt am dezentralen Gossip-Protokoll von Redis®, das die Kommunikation zwischen den Knoten nutzt, um Informationen zu Zustand, Status und Shard-Zuordnung auszutauschen. In Clustern mit mehr als einigen hundert Knoten beginnt der Datenverkehr des Gossip-Protokolls, erhebliche CPU- und Netzwerkressourcen zu beanspruchen. Jeder Knoten muss Statusaktualisierungen von einer ständig wachsenden Anzahl von Peers verarbeiten, was Ressourcen von der primären Aufgabe abzieht, Daten mit geringer Latenz bereitzustellen. Dieser Overhead der Steuerungsebene kann zu Instabilität und unvorhersehbarer Leistung führen, weshalb Managed-Cloud-Anbieter Knotenbeschränkungen durchsetzen. Es handelt sich um eine praktische Obergrenze, die nicht überschritten werden kann, ohne die Zuverlässigkeit zu beeinträchtigen.

Diese praktische Begrenzung auf einen einzelnen Cluster reicht nicht aus, um eine Plattform im Umfang von Roblox zu unterstützen, wo der größte Backend-Dienst in Spitzenzeiten über 100 Millionen QPS von einem unserer logischen Redis®-Cluster forderte – ein Anstieg von 10 Millionen innerhalb von drei Monaten.

Um diese architektonische Einschränkung zu überwinden, haben wir eine föderierte Architektur eingeführt und einen Client-Reverse-Proxy vor mehreren kleineren, unabhängigen Redis®-Clustern positioniert. Wir verwenden eine Obergrenze von 400 Knoten pro Cluster, um eine vorhersehbare Leistung zu gewährleisten. 

Aus der Perspektive unserer Tausenden von Anwendungen und Diensten präsentiert der Client-Reverse-Proxy diese unterschiedlichen Cluster als einen einzigen, einheitlichen und skalierbaren Caching-Dienst. Diese Föderation ermöglicht es uns, nahezu unbegrenzt horizontal zu skalieren, indem wir einfach weitere Cluster zur Föderation hinzufügen und so die Einschränkungen des Gossip-Protokolls vollständig umgehen. Heute besteht unsere größte einzelne Caching-Bereitstellung aus über 6.000 Redis®-Knoten, die von über 15 unabhängigen Redis®-Clustern unterstützt werden – eine Größenordnung, die nur durch dieses Verbundmodell möglich ist.

Ein schematisches Diagramm der Verbundarchitektur.

Der dreistufige Migrationsprozess

Der Übergang von einer traditionellen Konfiguration zu einer föderierten Architektur bei gleichzeitiger Verwaltung von Hunderten aktiver Produktionscluster war ein bedeutendes Unterfangen. Ende 2024 näherten sich viele dieser Cluster bereits unserer Grenze von 400 Knoten, was einen Übergang zu einem Modell mit höherer Kapazität dringend erforderlich machte. Die größte Herausforderung bestand darin, dass Standard-Redis®-Instanzen isoliert arbeiten und keine inhärente Kenntnis von anderen Clustern haben, was eine maßgeschneiderte Methode zur Datenreplikation während der Migration erforderte.

Um dies zu lösen, entwickelten wir eine transparente, dreistufige Migrationspipeline, die darauf ausgelegt war, Dienste ohne jegliche Beteiligung der Kunden oder Änderungen auf Anwendungsseite zu migrieren.

Der Migrations-Workflow

  1. Doppelte Schreibvorgänge: Wir konfigurierten den Client-Reverse-Proxy so, dass er doppelte Schreibvorgänge durchführte und eingehende Daten gleichzeitig sowohl an den ursprünglichen Cluster als auch an das neue Verbundziel sendete.
  2. Parität und Leseumstellung: Sobald der Dual-Write-Prozess den neuen Cluster mit identischen Daten gefüllt hatte, haben wir – da alle Daten an eine Time-to-Live (TTL) gebunden sind – den Lesedatenverkehr vom alten Cluster auf die neue föderierte Architektur umgeleitet.
  3. Stilllegung: Nachdem wir die Stabilität des neuen Clusters bestätigt hatten, stellten wir alle Schreibvorgänge auf den ursprünglichen Cluster ein und nahmen ihn außer Betrieb.

Wir haben unsere wichtigsten Dienste erfolgreich in ein Verbundmodell migriert, das über 6.000 Knoten unterstützt und damit die Grenzen eines einzelnen Redis®-Clusters bei weitem übertrifft. 

Bewältigung des explosiven Wachstums von Grow a Garden

Ab April 2025 standen wir vor einer Kapazitätsherausforderung von einem Ausmaß, wie es nur wenige Dienste jemals erleben. Auslöser war der phänomenale Erfolg von „Grow a Garden“. Innerhalb weniger Wochen nach dem Start brach „Grow a Garden“ nicht nur Roblox-Rekorde, sondern auch alle bisherigen Rekorde der Gaming-Branche für gleichzeitige Nutzer (CCU). In nur drei Monaten stieg die Spielerzahl des Spiels über unseren bisherigen CCU-Rekord für ein einzelnes Spiel von 2,8 Millionen Spielern auf einen Spitzenwert von über 21 Millionen. Dieses explosive Wachstum führte zu einem zehnfachen Anstieg des Datenverkehrs in unserem größten Redis®-Cluster und einem dreifachen Anstieg des Datenverkehrs im gesamten Caching.

Roblox betreibt seine Caching-Systeme auf physischen Maschinen in eigenen Rechenzentren, und dieses beispiellose Wachstum war nicht Teil unseres langfristigen Kapazitätsplans. Um diese Flutwelle zu überstehen und die Plattform stabil zu halten, mussten wir die Effizienz unserer bestehenden Infrastruktur radikal verbessern und dabei der Zuverlässigkeit Priorität einräumen. 

Effizienzsteigerung während des Ansturms

Zunächst mussten wir die kritischsten Engpässe in unserem System identifizieren, um die Optimierungsmaßnahmen priorisieren zu können. Roblox Caching läuft auf zwei verschiedenen Hardwarepools: einem für Redis®-Backend-Knoten und einem für Envoy. Unsere Redis®-Flotte ist in erster Linie speichergebunden, während der Client-Proxy rechenintensiv ist. 

Für unsere speichergebundene Redis®-Flotte haben wir eine neue Scheduling-Richtlinie implementiert, um die Speicherauslastung über unsere physischen Maschinengruppen hinweg besser auszugleichen. Außerdem haben wir die Containergröße feinabgestimmt, um die Speicherfragmentierung zu reduzieren. 

Um die CPU-Auslastung zu verbessern, haben wir zwei wichtige Optimierungen an unserem Cache-System vorgenommen:

  1. Autoskalierung des Client-Proxys: Wir haben die Autoskalierungsrichtlinie für unsere rechenintensiven Client-Proxy-Flotte angepasst. Durch die Verringerung der zulässigen Abweichung vom Ziel-CPU-Wert haben wir eine höhere durchschnittliche CPU-Auslastung für Jobs erreicht, ohne den Scale-up-Schwellenwert zu ändern.
  2. Interne Cluster-Zustandsprüfungen: Wir haben die internen Zustandsprüfungen optimiert, um unnötige Heartbeats zu eliminieren, wobei das ursprüngliche Erkennungsintervall beibehalten und gleichzeitig Rechenressourcen eingespart wurden.

Diese Änderungen führten zu einer Verbesserung der CPU-Auslastung um fast 10 %. Schließlich haben wir diese beiden sehr unterschiedlichen Workloads mithilfe von cgroup-Isolation auf Containerebene im selben Maschinenpool zusammengeführt, wodurch sich unser Gesamtkapazitätsbedarf um 25 % reduzierte.

Technik für unerschütterliche Zuverlässigkeit

Zuverlässigkeit blieb unsere oberste Priorität, selbst als wir uns beeilten, die rekordverdächtige Nachfrage zu befriedigen. 

End-to-End-Beobachtbarkeit

Wir konzentrierten uns zunächst darauf, potenzielle Ausfälle zu verstehen, indem wir massiv in unseren Observability-Stack investierten, um eine umfassende, durchgängige Zustandsüberwachung unseres Caching-Systems zu ermöglichen. Durch die Verfolgung von Servererfolgen und clientseitigen Ausfällen über den gesamten Lebenszyklus einer Anfrage hinweg konnten wir Probleme schnell erkennen und diagnostizieren und so oft Auswirkungen auf die Nutzer verhindern. So haben wir beispielsweise eine Hot-Key-Erkennung hinzugefügt, die den Live-Datenverkehr analysiert, um Daten zu identifizieren, auf die übermäßig oft zugegriffen wird und die die Caching-Ebene belasten. Wenn das System einen Hot Key meldet, können wir den Datenverkehr zu diesen Keys sowohl auf Server- als auch auf Client-Seite proaktiv drosseln, um eine Überlastung des Systems zu verhindern.

Fehlerbehandlung

In jedem verteilten System dieser Größenordnung kommt es gelegentlich zu Teilausfällen. Wir haben unser System so konzipiert, dass es diese Ausfälle elegant bewältigt und ihre Auswirkungen minimiert.

  • Teilausfälle bei gebündelten Anfragen: Wir haben das System neu strukturiert, um Teilausfälle bei gebündelten Anfragen zu bewältigen. Früher führte ein einziger Ausfall einer Anfrage in einer fragmentierten gebündelten Anfrage zum Scheitern des gesamten Stapels. Jetzt können Anfragen erfolgreich sein, selbst wenn einige Schlüssel nicht abgerufen werden können.
  • Minderung von Head-of-Line-Blocking: Von Caching-Systemen wird erwartet, dass sie schnell sind. Selbst eine geringfügige Netzwerkverlangsamung kann dazu führen, dass sich Anfragen stauen und eine Zeitüberschreitung auftritt – ein Phänomen, das als Head-of-Line-Blocking bekannt ist. Wir haben intelligente Wiederholungsmechanismen und Verbindungspooling-Strategien sowohl in der Client- als auch in der Proxy-Schicht implementiert, um zu verhindern, dass eine einzelne langsame Anfrage andere beeinträchtigt.
  • Fehler- und Latenzinjektion: Um die Ausfallsicherheit zu testen, injizieren wir mithilfe unseres Fehlerinjektions-Frameworks regelmäßig kontrollierte Fehler (Knotenausfälle, Rack-Ausfälle, Netzwerklatenz, API-Fehler) in die Produktion. Durch die Analyse des Systemverhaltens unter diesen Bedingungen identifizieren und beheben wir proaktiv Schwachstellen und gewährleisten so kontinuierliche Zuverlässigkeit.

Clientseitige Ausfallsicherheit

Um zu verhindern, dass unser Caching-System durch plötzliche Traffic-Spitzen überlastet wird, haben wir eine robuste clientseitige Ausfallsicherheit implementiert. Dieser Mechanismus arbeitet auf Anwendungsebene und stellt sicher, dass kein einzelner Dienst die Cache-Ressourcen monopolisieren kann.

  • Ratenbegrenzung zur Flusssteuerung: Unser Client verfügt über eine Begrenzung des gesamten ausgehenden Datenverkehrs, wodurch sichergestellt wird, dass er nicht mehr Anfragen sendet, als der Backend-Dienst verarbeiten kann. Bei Überschreitung dieses Limits werden Anfragen gedrosselt, wodurch eine Überlastung des Dienstes verhindert wird.
  • Wiederholungs- und Timeout-Mechanismen: Unsere Client-Bibliotheken nutzen sorgfältig abgestimmte Wiederholungsmechanismen, einschließlich Timeouts und eines Wiederholungsbudgets, um „Thundering-Herd“-Probleme zu verhindern. Dies begrenzt den Auswirkungsbereich, gibt Ressourcen frei und verbessert die allgemeine Reaktionsfähigkeit des Systems, indem es verhindert, dass unbestimmte Hänger und gleichzeitige Wiederholungsversuche das System überlasten.

Die Zukunft des Caching bei Roblox

Die Caching-Entwicklung bei Roblox begann mit Bare-Metal-Redis®-Clustern, die direkt von den jeweiligen Anwendungsbesitzern verwaltet wurden, ging dann zu Memcached-Clustern über, die von unserem Caching-Team verwaltet wurden, und wurde schließlich auf ein Redis®-as-a-Service-Modell umgestellt, bei dem ein Redis®-Cluster als Managed Service bereitgestellt wird. Obwohl sich dieses Modell bei unserer derzeitigen Größe als äußerst effektiv erwiesen hat, erkennen wir seine Grenzen hinsichtlich der Serviceeffizienz und der Ressourcenisolierung für eine so dynamische Plattform wie Roblox.

Da die Plattform weiter wächst, planen wir erhebliche Investitionen in einen Multitenant-Caching-Dienst der nächsten Generation, der auf ValKey aufbaut. Diese Architektur ist darauf ausgelegt, die Kosten durch die gemeinsame Nutzung von Ressourcen im Rahmen der Multitenancy drastisch zu senken und gleichzeitig die Isolation von Daten, Ressourcen und Ausfällen für jeden Tenant zu gewährleisten. Unser Ziel ist es, die Leistung, Skalierbarkeit, Zuverlässigkeit und Kosteneffizienz unserer Caching-Infrastruktur erheblich zu verbessern, um den Anforderungen der immer umfangreicheren Erlebnisse auf Roblox gerecht zu werden.

Danksagung: 

Diese Leistung ist das Ergebnis einer gemeinsamen Anstrengung. Vielen Dank an die anderen Teammitglieder, die einen wesentlichen Beitrag geleistet haben: Utkarsh Singh, Karthick Ramachandran, Vineesha Kasireddy, Weiji Hu und Leon Gao.

1AWS begrenzt ElasticCache auf 500 Knoten.

*Redis ist eine Marke von Redis Labs Ltd. Alle Rechte daran liegen bei Redis Labs Ltd.