18 Punkte von xguru 2024-02-05 | 1 Kommentare | Auf WhatsApp teilen
  • Apple verwendet Cassandra und FoundationDB für iCloud und CloudKit
  • Diese Datenbanken speichern in einer extremen Multi-Tenancy-Architektur Milliarden von Datenbanken

Zeitlose Lehren aus der realen Welt

  • Meta und Apple nutzen beide asynchrone Verarbeitung, um Nutzerfunktionen reibungslos bereitzustellen
  • Beide Unternehmen lösen Skalierungsprobleme mit einer zustandslosen (stateless) Architektur
  • Ressourcen werden logisch isoliert, um Zuverlässigkeit und Verfügbarkeit sicherzustellen
  • Unterschiedliche Anforderungen werden auf einfache Weise verarbeitet
  • Es werden Abstraktionsschichten aufgebaut, um die Developer Experience zu verbessern
  • Kenne die Nutzer und entscheide darauf basierend über jede Schicht, API und jedes Design

Cassandra

  • Cassandra ist ein weit verbreitetes spaltenorientiertes NoSQL-Datenbankmanagementsystem
  • Ursprünglich wurde es bei Facebook für die Suchfunktion des Facebook-Posteingangs entwickelt
    • Interessanterweise hat Meta selbst den Großteil seiner Cassandra-Nutzung durch ZippyDB ersetzt
  • iCloud nutzt teilweise Cassandra, und Apple betreibt eine der weltweit größten Cassandra-Deployments
    • mehr als 300.000 Instanzen/Knoten
    • Hunderte Petabyte an Daten
    • mehr als 2 Petabyte pro Cluster
    • Millionen von Abfragen pro Sekunde
    • Tausende Anwendungen
  • Cassandra wird bei Apple weiterhin aktiv verbessert
  • Doch CloudKit + Cassandra stießen an Skalierungsgrenzen, weshalb FoundationDB eingeführt wurde

FoundationDB

  • Apple nutzt FoundationDB öffentlich und hat das Unternehmen 2015 übernommen
  • FoundationDB ist ein Open-Source-Distributed-Transactional-Key-Value-Store, der für die Verarbeitung großer Datenmengen ausgelegt ist
    • geeignet sowohl für Lese-/Schreib-Workloads als auch für schreibintensive Workloads
  • Apple nutzt die FoundationDB Record Layer umfassend in CloudKit
  • Die FoundationDB Record Layer bietet eine Java-API zur Speicherung strukturierter Daten
  • Die Record Layer unterstützt extreme Multi-Tenancy

Warum die Record Layer auf FoundationDB verwenden

  • FoundationDB übernimmt die Aufgaben verteilter Systeme und der Nebenläufigkeitskontrolle.
  • Die Record Layer fungiert als relationale Datenbank, die FoundationDB einfacher nutzbar macht.
  • CloudKit bildet die oberste Schicht und stellt Funktionen und APIs für App-Entwickler bereit.
  • Über die Record Layer unterstützt Apple Multi-Tenancy in großem Maßstab
    • sie wird für extreme Multi-Tenancy verwendet, bei der jeder Nutzer jeder Anwendung einen unabhängigen Record Store erhält
    • sie hostet Milliarden unabhängiger Datenbanken, die Tausende Schemas gemeinsam nutzen

Wie CloudKit FoundationDB und die Record Layer verwendet

  • In CloudKit werden Anwendungen als „logische Container“ dargestellt, die einem definierten Schema folgen
    • dieses Schema beschreibt kurz die Record-Typen, Felder und Indizes, die für effiziente Datenabfrage und Suche erforderlich sind
    • Anwendungen können Daten innerhalb von CloudKit in „Bereichen“ organisieren, um Records logisch zu gruppieren und sie optional mit Client-Geräten zu synchronisieren
  • Jedem Nutzer wird innerhalb von FoundationDB ein eindeutiger Subspace zugewiesen, und für jede Anwendung, mit der der Nutzer interagiert, wird ein Record Store erzeugt
    • im Kern verwaltet CloudKit damit eine enorme Zahl logischer Datenbanken in der Größenordnung Nutzerzahl mal Anwendungszahl
    • jede Datenbank enthält ihre eigenen Records, Indizes und Metadaten und summiert sich so auf Milliarden von Datenbanken
  • Erhält CloudKit eine Anfrage von einem Client-Gerät, wird diese per Load Balancing an einen verfügbaren CloudKit-Service-Prozess weitergeleitet
    • der Prozess bearbeitet die Anfrage durch Interaktion mit dem jeweiligen Record Store der Record Layer
  • CloudKit übersetzt das definierte Anwendungsschema in Metadatendefinitionen innerhalb der Record Layer, die in einem separaten Metadata Store gespeichert werden
    • diese Metadaten werden durch CloudKit-spezifische Systemfelder ergänzt, die die Erstellung von Records, Änderungszeiten und den Bereich verfolgen, in dem der Record gespeichert ist
    • um effizient auf Records innerhalb jedes Bereichs zuzugreifen, wird dem Primärschlüssel der Bereichsname als Präfix vorangestellt
    • zusätzlich zu benutzerdefinierten Indizes verwaltet CloudKit auch „Systemindizes“ für interne Zwecke wie die Verwaltung von Speicherquoten, indem es die Größe von Records nach Typ nachverfolgt

Durch die gemeinsame Nutzung von FoundationDB und der Record Layer konnte Apple vier zentrale Probleme lösen, die mit Cassandra allein nicht lösbar waren

1. Lösung des Problems der personalisierten Volltextsuche

  • FoundationDB unterstützt personalisierte Volltextsuche, damit Nutzer schnell auf ihre eigenen Daten zugreifen können
  • Durch die Nutzung der Schlüsselsortierung von FoundationDB lässt sich nicht nur der Anfang von Texten schnell durchsuchen (Präfix-Matching), sondern es sind auch komplexere Suchanfragen möglich, etwa Proximity Search und Phrasensuche, also das Finden nah beieinanderliegender oder in bestimmter Reihenfolge stehender Wörter, ohne zusätzlichen Overhead
  • In herkömmlichen Suchsystemen müssen oft zusätzliche Hintergrundprozesse laufen, um Suchindizes aktuell zu halten. Apples System verarbeitet alles in Echtzeit, sodass der Suchindex sofort aktualisiert wird, sobald sich Daten ändern, und kein zusätzlicher Schritt erforderlich ist

2. Lösung des Problems hochgradig gleichzeitiger Bereiche

  • CloudKit verarbeitet mit FoundationDB viele gleichzeitig auftretende Updates reibungslos
  • Zuvor nutzte CloudKit mit Cassandra einen speziellen Index, der Änderungen in jedem Bereich verfolgte, um Daten über mehrere Geräte hinweg zu synchronisieren
    • wenn ein Gerät Daten aktualisieren musste, prüfte es diesen Index, um zu sehen, was neu war
    • traten jedoch mehrere Updates gleichzeitig auf, konnten Konflikte entstehen
  • Mit FoundationDB verwendet CloudKit einen speziellen Indextyp, der die exakte Reihenfolge jeder Änderung nachverfolgt, ohne Konflikte zu verursachen
    • dies geschieht durch die Vergabe einer eindeutigen „Version“ für jede Änderung, und CloudKit prüft diese Versionen bei der Synchronisierung, um festzustellen, welche Updates einem Gerät entgangen sind
  • Muss CloudKit Daten zwischen mehreren Storage-Clustern verschieben, um die Last gleichmäßiger zu verteilen, wird die Situation schwieriger, da die Versionsnummern der Cluster nicht übereinstimmen
    • zur Lösung dieses Problems weist CloudKit den Daten jedes Nutzers eine „Anzahl von Umzügen“ zu, „incarnation“ genannt, die bei jeder Übertragung der Daten in einen neuen Cluster erhöht wird
    • jedes Record-Update enthält die aktuelle „incarnation“-Nummer des Nutzers, sodass CloudKit auch nach einer Verschiebung durch Prüfung von incarnation und Versionsnummer die korrekte Reihenfolge der Updates bestimmen kann
  • Beim Übergang zum neuen System stand CloudKit zudem vor dem Problem, ältere Daten ohne diese Versionsnummern verarbeiten zu müssen
    • dieses Problem wurde jedoch elegant gelöst, indem eine spezielle Funktion verwendet wurde, die ältere Updates aus dem vorherigen System vor denen des neuen Systems einsortiert
    • dadurch waren keine komplexen Änderungen an Apps und kein Altcode nötig
    • um die richtige Reihenfolge der Historie beizubehalten, werden incarnation, Version und ältere Update-Zählerwerte berücksichtigt

3. Lösung des Problems von High-Latency-Abfragen

  • FoundationDB wurde für hohe Nebenläufigkeit und nicht für niedrige Latenz entworfen. Das heißt, der Fokus liegt darauf, viele Operationen gleichzeitig verarbeiten zu können, statt einzelne Operationen maximal zu beschleunigen
  • Um dieses Design bestmöglich zu nutzen, führt die Record Layer viele Aufgaben „asynchron“ aus
    • dabei werden Aufgaben zur späteren Ausführung in eine Warteschlange gestellt, während in der Zwischenzeit andere Arbeiten erledigt werden können
    • dieser Ansatz hilft dabei, Verzögerungen zu überdecken, die während solcher Aufgaben auftreten können
  • Allerdings ist das Tool, das FoundationDB zur Kommunikation mit der Datenbank verwendet, so aufgebaut, dass es für das Networking einen einzelnen Thread nutzt und immer nur eine Aufgabe gleichzeitig erledigt
    • in früheren Versionen führte dieses Setup zu Staus im System, weil alle Aufgaben auf diesen Netzwerk-Thread warten mussten
    • da die Record Layer diesen Single-Thread-Ansatz nutzte, entstand ein Flaschenhals
  • Um dies zu verbessern, reduzierte Apple die Arbeitslast dieses Netzwerk-Threads
    • komplexe Aufgaben wurden dadurch schneller, weil das System nun an mehreren Stellen gleichzeitig mit der Datenbank arbeiten kann, ohne Warteschlangen zu bilden
    • so wird Latenz bzw. die gefühlte Langsamkeit verdeckt, da das System nicht warten muss, bis eine Aufgabe abgeschlossen ist, bevor es die nächste startet

4. Lösung des Problems kollidierender Transaktionen

  • In FoundationDB tritt ein „Transaktionskonflikt“ auf, wenn eine Transaktion einen bestimmten Schlüssel liest, während eine andere denselben Schlüssel verändert
    • FoundationDB bietet Kontrollmöglichkeiten über die Menge der Schlüssel, die beim Lesen oder Schreiben solche Konflikte auslösen können, sodass sich diese Konflikte präzise steuern lassen
  • Eine gängige Methode zur Vermeidung unnötiger Konflikte besteht darin, eine spezielle Art von Lesezugriff ohne Konfliktauslösung durchzuführen, sogenannte „Snapshot“-Reads, über verschiedene Schlüssel hinweg
    • wird bei diesem Lesen ein wichtiger Schlüssel gefunden, markiert die Transaktion nicht den gesamten Bereich, sondern nur die konkreten Schlüssel mit potenziellem Konflikt
    • so wird sichergestellt, dass eine Transaktion nur von Änderungen beeinflusst wird, die für ihr Ergebnis tatsächlich relevant sind
  • Die Record Layer nutzt diese Strategie, um effizient eine Struktur namens Skip List zu verwalten, die Teil eines Ranking-Index-Systems ist
    • das manuelle Festlegen solcher Konfliktbereiche kann jedoch schwierig sein und, insbesondere wenn es mit der Kernlogik einer Anwendung vermischt ist, zu schwer erkennbaren Bugs führen
    • deshalb ist es in auf FoundationDB aufgebauten Systemen ratsam, höherwertige Werkzeuge wie benutzerdefinierte Indizes zu schaffen, um solche Muster zu behandeln
    • dieser Ansatz hilft, Situationen zu vermeiden, in denen die Verantwortung für das Abschwächen von Konfliktregeln bei einzelnen Client-Anwendungen liegt und dadurch Fehler und Inkonsistenzen entstehen können

1 Kommentare

 
xguru 2024-02-05

Hacker-News-Kommentare

  • Ein Hacker-News-Nutzer teilte Einblicke aus seiner Zeit bei Apple über die Unterschiede zwischen Datenbanken und Dateisystemen. Er erwähnte, dass Datenbanken und Dateisysteme im Grunde dieselbe Funktion erfüllen und Optimierungen zur Lösung bestimmter Probleme sind. Als Beispiel zeige iCloud, wie sich ein Dateisystem auf Grundlage einer Datenbank definieren lasse. Der Nutzer berichtete außerdem von seiner Erfahrung mit Cassandra zur Speicherung von Videos.

  • Ein anderer Nutzer erwähnte seine Erfahrung aus einem früheren Unternehmen, wo mit FoundationDB und RecordLayer ein transaktionales Katalogsystem aufgebaut wurde. Dieses System sei sehr effektiv gewesen, und die Nutzung von gRPC und Protobuf habe sich natürlich angefühlt. Als Nachteil wurde jedoch die hohe Einstiegshürde für den Betrieb von FoundationDB im großen Maßstab genannt.

  • Ein Nutzer bewertete die Synchronisierungsfunktion von Apple Notes als besser im Umgang mit Konflikten als Markdown-basierte Notizanwendungen. Deshalb sei er letztlich zu Apple Notes gewechselt.

  • Frühere Beiträge zu FoundationDB wurden erwähnt. Dazu gehörten Links zum verteilten Key-Value-Store von FoundationDB, zur Record Layer, zur Übernahme durch Apple sowie zur Funktionsweise und zu den Eigenschaften von FoundationDB.

  • Erwähnt wurde außerdem ein interessanter Aspekt der Architektur nativer Desktop-Software, die sich schrittweise in Richtung Cloud-basierter Speicherung und Zusammenarbeit bewegt. Wichtig sei dabei, Schemaänderungen und Versionsmigrationen gut zu handhaben, da dies in großem Maßstab ohne Eingriffe von Administratoren geschehe.

  • Ein Nutzer wünschte sich, dass iCloud Time-Machine-Backups speichern könnte.

  • Da FoundationDB auf SQLite basiert, wurde die Frage aufgeworfen, ob die HCTree-Engine auf FoundationDB angewendet werden könnte. HCTree habe das Potenzial, die Lese-/Schreibleistung von SQLite um das Zehnfache zu steigern.

  • Es wurde Unzufriedenheit darüber geäußert, wie iCloud die Dateien der Nutzer verwaltet. Mitunter sei es problematisch, dass iCloud zuletzt verwendete Dateien, Apps und Fotos automatisch in die Cloud verschiebt, um Speicherplatz freizugeben.

  • Ein Nutzer erinnerte sich an ein Berichtssystem namens Hyperion, das er früher bei einer Bank verwendet hatte. Dieses System erzeugte für jeden Bericht eine neue Datenbank; damals habe ihm das seltsam erschienen, rückblickend sei es seiner Zeit aber voraus gewesen.