Verteilte Sequenzgenerierung ersetzt Datenbanksequenzen in großem Maßstab. Sie beseitigt zentrale Engpässe und bleibt dabei mit bestehenden Systemen kompatibel.
Das Problem zeigt sich nicht sofort — bis zu dem Zeitpunkt, an dem die Organisation versucht, von einer relationalen Datenbank zu einem cloud-nativen Speicher zu wechseln. In diesem Fall hingen über hundert Dienste von Datenbanksequenzen zur Generierung von Primärschlüsseln ab. Diese Zähler waren tief in die Logik eingebettet: von der Sortierung bis zur Rückwärtskompatibilität der API. In einer NoSQL-Umgebung wie DynamoDB gibt es keine nativen Sequenzen. Ein einfacher Austausch bricht Verträge, die Reihenfolge der Daten und die Leistung. Der Maßstab erhöht den Druck: Tausende von Zählern und hoher Durchsatz machen jeden Netzwerk-Round-Trip teuer in Bezug auf Latenz.
Standardalternativen wurden in Betracht gezogen, aber jede scheiterte an den Einschränkungen. UUIDs beseitigen Kollisionen, zerstören jedoch die Reihenfolge und verschlechtern die Leistung von Indizes. Der Snowflake-Ansatz bewahrt BIGINT und partielle Reihenfolge, erfordert jedoch die Verwaltung von Worker-IDs und die Synchronisierung von Uhren, was den Betrieb kompliziert. Ein zentraler Koordinator wird zum Engpass und zum Single Point of Failure. Timestamp-Ansätze garantieren keine Einzigartigkeit bei hoher Konkurrenz. Eine wichtige Beobachtung: Die meisten Systeme benötigen keine strikte globale Reihenfolge und keine „Lücken“. Dies ermöglichte es, die Anforderungen zu vereinfachen und auf schwere Koordination zugunsten von Lokalität und Caching zu verzichten.
Die Lösung ist ein spezialisierter Sequenzdienst mit mehrstufigem Caching. Die Architektur basiert auf DynamoDB als Quelle der Wahrheit, einem serverseitigen Cache und „dicken“ Clients innerhalb der Anwendungen. Anstelle der Generierung eines Wertes pro Anfrage weist das System Blöcke (Batch) von 500–1000 Werten über atomare Inkremente zu. Dies verringert die Belastung des Speichers und entfernt ihn aus dem kritischen Pfad. Die meisten Anfragen werden lokal bedient, ohne Netzwerkaufrufe. Der Kompromiss ist offensichtlich: Bei Ausfällen gehen einige Werte verloren, wodurch Lücken entstehen, und die globale Reihenfolge wird nicht garantiert.
Die Implementierung basiert auf atomaren Operationen von DynamoDB mit bedingtem Update. Jeder Zähler wird als separates Objekt gespeichert. Bei der Zuweisung eines Blocks wird eine Compare-and-Set-Logik verwendet: Wenn sich der Wert geändert hat, erfolgt ein Retry. Dies gewährleistet Einzigartigkeit ohne verteilte Sperren. Auf der Dienstebene hält jede Instanz ihren eigenen In-Memory-Cache mit nicht überlappenden Bereichen. Ein externer Cache (z. B. Redis) wird absichtlich ausgeschlossen, um keinen zusätzlichen Netzwerk-Hop und neue Ausfallpunkte hinzuzufügen.
Die zentrale Herausforderung besteht nicht in der Generierung von Werten, sondern im Management der Nachfülllogik. Wenn der Cache zu früh aufgefüllt wird, steigen die Kosten und Verluste. Zu spät — es treten Cache-Misses und Latenzspitzen auf. Hier wird ein Sliding-Window-Algorithmus verwendet, der die aktuelle Verbrauchsrate bewertet und dynamisch den Nachfüllschwellenwert berechnet. Die Formel ist einfach: die aktuelle Geschwindigkeit multipliziert mit einem Zeitpuffer. Das Nachfüllen wird asynchron gestartet, bevor der Cache erschöpft ist, damit Benutzeranfragen nicht blockiert werden. Dieser Mechanismus lebt innerhalb des Clients, was die Belastung des Netzwerks weiter verringert.
Das Ergebnis ist ein System, in dem DynamoDB weniger als 0,1 % der Sequenzanfragen bedient. Der Hauptstrom wird durch lokale Caches geschlossen, was die Generierung von Identifikatoren in Bezug auf Latenz nahe an eine gewöhnliche Operation im Speicher bringt. Die Migration der Dienste erfolgt ohne Änderungen an den Schemata und mit minimalen Codeanpassungen. Dabei bleibt die Kompatibilität mit bestehenden Verträgen erhalten. Die Metriken zur Gesamtleistung sind nicht offengelegt, aber architektonisch ist eine Verringerung der Latenz und die Beseitigung des zentralen Engpasses sichtbar.
Diese Lösung ist ein pragmatischer Kompromiss. Sie verzichtet auf strenge Garantien zugunsten von Skalierbarkeit und einfacher Bedienung. In Hochlastsystemen erweist sich dies oft als die richtige Wahl: nicht das ideale Modell, sondern das, das unter Last nicht bricht.