Lesen und Schreiben im großen Maßstab verstehen

Lesen Sie dieses Dokument, um fundierte Entscheidungen zur Architektur Ihrer Anwendungen für hohe Leistung und Zuverlässigkeit zu treffen. Dieses Dokument enthält fortgeschrittene Cloud Firestore-Themen. Wenn Sie gerade erst mit Cloud Firestore beginnen, sehen Sie sich stattdessen die Schnellstartanleitung an.

Cloud Firestore ist eine flexible, skalierbare Datenbank für die Entwicklung mobiler Geräte, Web- und Server von Firebase und Google Cloud. Der Einstieg in Cloud Firestore und das Schreiben umfangreicher und leistungsstarker Anwendungen ist ganz einfach.

Um sicherzustellen, dass Ihre Anwendungen auch bei steigender Datenbankgröße und zunehmendem Datenverkehr weiterhin eine gute Leistung erbringen, ist es hilfreich, die Mechanismen der Lese- und Schreibvorgänge im Cloud Firestore-Backend zu verstehen. Sie müssen auch die Interaktion Ihrer Lese- und Schreibvorgänge mit der Speicherschicht und die zugrunde liegenden Einschränkungen verstehen, die sich auf die Leistung auswirken können.

In den folgenden Abschnitten finden Sie Best Practices vor der Architektur Ihrer Anwendung.

Verstehen Sie die hochrangigen Komponenten

Das folgende Diagramm zeigt die übergeordneten Komponenten, die an einer Cloud Firestore-API-Anfrage beteiligt sind.

Hochwertige Komponenten

Cloud Firestore SDK und Client-Bibliotheken

Cloud Firestore unterstützt SDKs und Client-Bibliotheken für verschiedene Plattformen. Während eine App direkte HTTP- und RPC-Aufrufe an die Cloud Firestore API durchführen kann, bieten die Clientbibliotheken eine Abstraktionsebene, um die API-Nutzung zu vereinfachen und Best Practices zu implementieren. Sie bieten möglicherweise auch zusätzliche Funktionen wie Offline-Zugriff, Caches usw.

Google Frontend (GFE)

Dies ist ein Infrastrukturdienst, der allen Cloud-Diensten von Google gemeinsam ist. Das GFE nimmt eingehende Anfragen entgegen und leitet sie an den entsprechenden Google-Dienst (hier Firestore-Dienst) weiter. Darüber hinaus bietet es weitere wichtige Funktionalitäten, darunter den Schutz vor Denial-of-Service-Angriffen.

Cloud Firestore-Dienst

Der Cloud Firestore-Dienst führt Prüfungen der API-Anfrage durch, einschließlich Authentifizierung, Autorisierung, Kontingentprüfungen und Sicherheitsregeln, und verwaltet auch Transaktionen. Dieser Cloud Firestore-Dienst umfasst einen Speicherclient , der mit der Speicherschicht für das Lesen und Schreiben von Daten interagiert.

Cloud Firestore-Speicherschicht

Die Cloud Firestore-Speicherschicht ist für die Speicherung der Daten und Metadaten sowie der zugehörigen Datenbankfunktionen verantwortlich, die von Cloud Firestore bereitgestellt werden. In den folgenden Abschnitten wird beschrieben, wie Daten in der Cloud Firestore-Speicherebene organisiert werden und wie das System skaliert. Wenn Sie wissen, wie Daten organisiert sind, können Sie ein skalierbares Datenmodell entwerfen und die Best Practices in Cloud Firestore besser verstehen.

Schlüsselbereiche und Splits

Cloud Firestore ist eine dokumentenorientierte NoSQL-Datenbank. Sie speichern Daten in Dokumenten , die in Sammlungshierarchien organisiert sind. Die Sammlungshierarchie und die Dokument-ID werden für jedes Dokument in einen einzigen Schlüssel übersetzt. Dokumente werden nach diesem einzigen Schlüssel logisch gespeichert und lexikografisch geordnet. Wir verwenden den Begriff Schlüsselbereich, um einen lexikographisch zusammenhängenden Bereich von Schlüsseln zu bezeichnen.

Eine typische Cloud Firestore-Datenbank ist zu groß, um auf eine einzelne physische Maschine zu passen. Es gibt auch Szenarien, in denen die Arbeitslast der Daten zu groß ist, als dass ein Computer sie bewältigen könnte. Um große Arbeitslasten zu bewältigen, partitioniert Cloud Firestore die Daten in separate Teile, die auf mehreren Maschinen oder Speicherservern gespeichert und von diesen bereitgestellt werden können. Diese Partitionen werden in den Datenbanktabellen in Blöcken von Schlüsselbereichen vorgenommen, die als Splits bezeichnet werden.

Synchrone Replikation

Es ist wichtig zu beachten, dass die Datenbank immer automatisch und synchron repliziert wird. Die Datenaufteilungen verfügen über Replikate in verschiedenen Zonen , um sie auch dann verfügbar zu halten, wenn auf eine Zone nicht mehr zugegriffen werden kann. Die konsistente Replikation auf die verschiedenen Kopien des Splits wird vom Paxos- Algorithmus zur Konsensfindung verwaltet. Ein Replikat jedes Splits wird zum Paxos-Anführer gewählt, der für die Verarbeitung von Schreibvorgängen in diesen Split verantwortlich ist. Durch die synchrone Replikation haben Sie die Möglichkeit, immer die neueste Version der Daten aus Cloud Firestore lesen zu können.

Das Gesamtergebnis ist ein skalierbares und hochverfügbares System, das sowohl bei Lese- als auch bei Schreibvorgängen niedrige Latenzen bietet, unabhängig von hoher Arbeitslast und in sehr großem Maßstab.

Datenlayout

Cloud Firestore ist eine schemalose Dokumentendatenbank. Intern werden die Daten jedoch hauptsächlich in zwei relationalen Datenbanktabellen in der Speicherschicht wie folgt angeordnet:

  • Dokumententabelle : In dieser Tabelle werden Dokumente gespeichert.
  • Indextabelle : In dieser Tabelle werden Indexeinträge gespeichert, die es ermöglichen, Ergebnisse effizient und nach Indexwert sortiert zu erhalten.

Das folgende Diagramm zeigt, wie die Tabellen für eine Cloud Firestore-Datenbank mit den Aufteilungen aussehen könnten. Die Splits werden in drei verschiedenen Zonen repliziert und jedem Split ist ein Paxos-Anführer zugewiesen.

Datenlayout

Einzelne Region versus Multiregion

Wenn Sie eine Datenbank erstellen, müssen Sie eine Region oder mehrere Regionen auswählen.

Ein einzelner regionaler Standort ist ein bestimmter geografischer Standort, z. B. us-west1 . Die Datenaufteilungen einer Cloud Firestore-Datenbank verfügen über Replikate in verschiedenen Zonen innerhalb der ausgewählten Region, wie bereits erläutert.

Ein Standort mit mehreren Regionen besteht aus einer definierten Gruppe von Regionen, in denen Replikate der Datenbank gespeichert werden. Bei einer Bereitstellung von Cloud Firestore in mehreren Regionen verfügen zwei der Regionen über vollständige Replikate der gesamten Daten in der Datenbank. Eine dritte Region verfügt über ein Zeugenreplikat , das keinen vollständigen Datensatz verwaltet, aber an der Replikation teilnimmt. Durch die Replikation der Daten zwischen mehreren Regionen stehen die Daten auch beim Verlust einer gesamten Region zum Schreiben und Lesen zur Verfügung.

Weitere Informationen zu den Standorten einer Region finden Sie unter Cloud Firestore-Standorte .

Einzelne Region versus mehrere Regionen

Verstehen Sie das Leben eines Schreibers in Cloud Firestore

Ein Cloud Firestore-Client kann Daten schreiben, indem er ein einzelnes Dokument erstellt, aktualisiert oder löscht. Beim Schreiben in ein einzelnes Dokument müssen sowohl das Dokument als auch die zugehörigen Indexeinträge atomar in der Speicherschicht aktualisiert werden. Cloud Firestore unterstützt auch atomare Vorgänge, die aus mehreren Lese- und/oder Schreibvorgängen für ein oder mehrere Dokumente bestehen.

Für alle Arten von Schreibvorgängen stellt Cloud Firestore die ACID-Eigenschaften (Atomizität, Konsistenz, Isolation und Haltbarkeit) relationaler Datenbanken bereit. Cloud Firestore bietet außerdem Serialisierbarkeit , was bedeutet, dass alle Transaktionen so aussehen, als ob sie in einer seriellen Reihenfolge ausgeführt würden.

Schritte auf hoher Ebene in einer Schreibtransaktion

Wenn der Cloud Firestore-Client mithilfe einer der zuvor genannten Methoden einen Schreibvorgang ausgibt oder eine Transaktion festschreibt, wird dies intern als Datenbank-Lese-/Schreibtransaktion in der Speicherschicht ausgeführt. Die Transaktion ermöglicht es Cloud Firestore, die zuvor erwähnten ACID-Eigenschaften bereitzustellen.

Als ersten Schritt einer Transaktion liest Cloud Firestore das vorhandene Dokument und bestimmt die Änderungen, die an den Daten in der Tabelle „Dokumente“ vorgenommen werden sollen.

Dazu gehört auch die Durchführung notwendiger Aktualisierungen der Indextabelle wie folgt:

  • Felder, die den Dokumenten hinzugefügt werden, benötigen entsprechende Einfügungen in der Indextabelle.
  • Felder, die aus den Dokumenten entfernt werden, müssen in der Indextabelle entsprechend gelöscht werden.
  • Felder, die in den Dokumenten geändert werden, müssen in der Indextabelle sowohl gelöscht (für alte Werte) als auch eingefügt (für neue Werte) werden.

Um die zuvor erwähnten Mutationen zu berechnen, liest Cloud Firestore die Indexierungskonfiguration für das Projekt. Die Indizierungskonfiguration speichert Informationen über die Indizes für ein Projekt. Cloud Firestore verwendet zwei Arten von Indizes: Einzelfeld- und zusammengesetzte Indizes. Ausführliche Informationen zu den in Cloud Firestore erstellten Indizes finden Sie unter Indextypen in Cloud Firestore .

Sobald die Mutationen berechnet sind, sammelt Cloud Firestore sie innerhalb einer Transaktion und schreibt sie dann fest.

Verstehen Sie eine Schreibtransaktion in der Speicherschicht

Wie bereits erwähnt, beinhaltet ein Schreibvorgang in Cloud Firestore eine Lese-/Schreibtransaktion in der Speicherschicht. Abhängig vom Datenlayout kann ein Schreibvorgang eine oder mehrere Aufteilungen umfassen, wie im Datenlayout zu sehen ist.

Im folgenden Diagramm verfügt die Cloud Firestore-Datenbank über acht Splits (mit 1–8 gekennzeichnet), die auf drei verschiedenen Speicherservern in einer einzelnen Zone gehostet werden, und jeder Split wird in drei (oder mehr) verschiedenen Zonen repliziert. Jeder Split hat einen Paxos-Anführer, der sich bei verschiedenen Splits in einer anderen Zone befinden kann.

Aufteilung der Cloud Firestore-Datenbank

Betrachten Sie eine Cloud Firestore-Datenbank mit der Restaurants Sammlung wie folgt:

Restaurantsammlung

Der Cloud Firestore-Client fordert die folgende Änderung an einem Dokument in der Restaurant Sammlung an, indem er den Wert des Feldes priceCategory aktualisiert.

Zu einem Dokument in der Sammlung wechseln

Die folgenden allgemeinen Schritte beschreiben, was im Rahmen des Schreibvorgangs geschieht:

  1. Erstellen Sie eine Lese-/Schreibtransaktion.
  2. Lesen Sie das Dokument restaurant1 in der Sammlung Restaurants aus der Tabelle „ Dokumente“ der Speicherschicht.
  3. Lesen Sie die Indizes für das Dokument aus der Tabelle „Indizes“ .
  4. Berechnen Sie die an den Daten vorzunehmenden Änderungen. In diesem Fall gibt es fünf Mutationen:
    • M1: Aktualisieren Sie die Zeile für „ restaurant1 in der Tabelle „ Dokumente “, um die Wertänderung des Felds priceCategory widerzuspiegeln.
    • M2 und M3: Löschen Sie die Zeilen für den alten Wert von priceCategory in der Indextabelle für absteigende und aufsteigende Indizes.
    • M4 und M5: Fügen Sie die Zeilen für den neuen Wert von priceCategory in die Indextabelle für absteigende und aufsteigende Indizes ein.
  5. Begehen Sie diese Mutationen.

Der Speicherclient im Cloud Firestore-Dienst sucht nach den Splits, die Eigentümer der Schlüssel der zu ändernden Zeilen sind. Betrachten wir einen Fall, in dem Split 3 M1 und Split 6 M2-M5 bedient. Es handelt sich um eine verteilte Transaktion, an der alle diese Splits als Teilnehmer beteiligt sind. Die Teilnehmersplits können auch alle anderen Splits umfassen, aus denen zuvor im Rahmen der Lese-/Schreibtransaktion Daten gelesen wurden.

Die folgenden Schritte beschreiben, was im Rahmen des Commits geschieht:

  1. Der Speicherclient gibt einen Commit aus. Der Commit enthält die Mutationen M1-M5.
  2. Die Splits 3 und 6 sind die Teilnehmer dieser Transaktion. Einer der Teilnehmer wird als Koordinator ausgewählt, z. B. Split 3. Die Aufgabe des Koordinators besteht darin, sicherzustellen, dass die Transaktion für alle Teilnehmer atomar entweder festgeschrieben oder abgebrochen wird.
    • Die Leiternachbildungen dieser Splits sind für die von den Teilnehmern und Koordinatoren geleistete Arbeit verantwortlich.
  3. Jeder Teilnehmer und Koordinator führt einen Paxos-Algorithmus mit seinen jeweiligen Replikaten aus.
    • Der Anführer führt einen Paxos-Algorithmus mit den Replikaten aus. Das Quorum wird erreicht, wenn die meisten Replikate mit einer ok to commit -Antwort an den Leiter antworten.
    • Jeder Teilnehmer benachrichtigt dann den Koordinator, wenn er vorbereitet ist (erste Phase des zweiphasigen Commits). Wenn ein Teilnehmer die Transaktion nicht festschreiben kann, wird die gesamte Transaktion aborts .
  4. Sobald der Koordinator weiß, dass alle Teilnehmer, einschließlich ihm selbst, vorbereitet sind, teilt er allen Teilnehmern das Ergebnis der accept mit (zweite Phase des zweiphasigen Commits). In dieser Phase zeichnet jeder Teilnehmer die Festschreibungsentscheidung im stabilen Speicher auf und die Transaktion wird festgeschrieben.
  5. Der Koordinator antwortet dem Speicherclient in Cloud Firestore, dass die Transaktion festgeschrieben wurde. Parallel dazu wenden der Koordinator und alle Teilnehmer die Mutationen auf die Daten an.

Commit-Lebenszyklus

Wenn die Cloud Firestore-Datenbank klein ist, kann es vorkommen, dass ein einzelner Split alle Schlüssel in den Mutationen M1–M5 besitzt. In einem solchen Fall gibt es nur einen Teilnehmer an der Transaktion und das zuvor erwähnte zweiphasige Commit ist nicht erforderlich, wodurch die Schreibvorgänge schneller werden.

Schreibt in mehreren Regionen

Bei einer Bereitstellung in mehreren Regionen erhöht die Verteilung von Replikaten über Regionen die Verfügbarkeit, geht jedoch mit Leistungseinbußen einher. Die Kommunikation zwischen Replikaten in verschiedenen Regionen erfordert längere Umlaufzeiten. Daher ist die Grundlatenz für Cloud Firestore-Vorgänge im Vergleich zu Bereitstellungen in einer einzelnen Region etwas höher.

Wir konfigurieren die Replikate so, dass die Führung bei Splits immer in der primären Region bleibt. Die primäre Region ist diejenige, aus der Datenverkehr zum Cloud Firestore-Server eingeht. Diese Führungsentscheidung reduziert die Hin- und Rückverzögerung bei der Kommunikation zwischen dem Speicherclient in Cloud Firestore und dem Replikatleiter (oder Koordinator für Multi-Split-Transaktionen).

Jeder Schreibvorgang in Cloud Firestore beinhaltet auch eine gewisse Interaktion mit der Echtzeit-Engine in Cloud Firestore. Weitere Informationen zu Echtzeitabfragen finden Sie unter Grundlegendes zu Echtzeitabfragen im großen Maßstab .

Verstehen Sie die Lebensdauer einer Lektüre im Cloud Firestore

Dieser Abschnitt befasst sich mit eigenständigen, nicht in Echtzeit erfolgenden Lesevorgängen in Cloud Firestore. Intern verarbeitet der Cloud Firestore-Server die meisten dieser Abfragen in zwei Hauptphasen:

  1. Ein einzelner Bereichsscan über die Indextabelle
  2. Punktsuche in der Dokumententabelle basierend auf dem Ergebnis des früheren Scans
Möglicherweise gibt es bestimmte Abfragen, die in Cloud Firestore weniger oder mehr Verarbeitung erfordern (z. B. IN-Abfragen).

Das Lesen der Daten aus der Speicherschicht erfolgt intern mithilfe einer Datenbanktransaktion, um konsistente Lesevorgänge sicherzustellen. Im Gegensatz zu den für Schreibvorgänge verwendeten Transaktionen erfordern diese Transaktionen jedoch keine Sperren. Stattdessen wählen sie einen Zeitstempel aus und führen dann alle Lesevorgänge zu diesem Zeitstempel aus. Da sie keine Sperren erwerben, blockieren sie keine gleichzeitigen Lese-/Schreibtransaktionen. Um diese Transaktion auszuführen, gibt der Speicherclient in Cloud Firestore eine Zeitstempelgrenze an, die der Speicherebene mitteilt, wie sie einen Lesezeitstempel auswählen soll. Der vom Speicherclient in Cloud Firestore ausgewählte Typ der Zeitstempelbindung wird durch die Leseoptionen für die Leseanforderung bestimmt.

Verstehen Sie eine Lesetransaktion in der Speicherschicht

In diesem Abschnitt werden die Arten von Lesevorgängen und deren Verarbeitung in der Speicherschicht in Cloud Firestore beschrieben.

Starke Lektüre

Standardmäßig sind Cloud Firestore-Lesevorgänge stark konsistent . Diese starke Konsistenz bedeutet, dass ein Cloud Firestore-Lesevorgang die neueste Version der Daten zurückgibt, die alle bis zum Start des Lesevorgangs festgeschriebenen Schreibvorgänge widerspiegelt.

Einzel-Split-Lesung

Der Speicherclient in Cloud Firestore sucht nach den Splits, die die Schlüssel der zu lesenden Zeilen besitzen. Nehmen wir an, dass ein Lesevorgang aus Split 3 aus dem vorherigen Abschnitt durchgeführt werden muss. Der Client sendet die Leseanforderung an das nächstgelegene Replikat, um die Roundtrip-Latenz zu reduzieren.

Zu diesem Zeitpunkt können je nach gewähltem Replikat folgende Fälle auftreten:

  • Die Leseanforderung geht an ein Leader-Replikat (Zone A).
    • Da der Vorspann immer auf dem neuesten Stand ist, kann die Auslesung direkt erfolgen.
  • Die Leseanforderung geht an ein Nicht-Leader-Replikat (z. B. Zone B).
    • Split 3 weiß möglicherweise anhand seines internen Status, dass er über genügend Informationen verfügt, um den Lesevorgang durchzuführen, und der Split tut dies.
    • Split 3 ist sich nicht sicher, ob es die neuesten Daten gesehen hat. Es sendet eine Nachricht an den Leiter und fragt nach dem Zeitstempel der letzten Transaktion, die zur Bereitstellung des Lesevorgangs angewendet werden muss. Sobald diese Transaktion angewendet wurde, kann der Lesevorgang fortgesetzt werden.

Cloud Firestore gibt die Antwort dann an seinen Client zurück.

Multi-Split-Lesung

In der Situation, in der die Lesevorgänge aus mehreren Splits erfolgen müssen, erfolgt derselbe Mechanismus über alle Splits hinweg. Sobald die Daten von allen Aufteilungen zurückgegeben wurden, kombiniert der Speicherclient in Cloud Firestore die Ergebnisse. Cloud Firestore antwortet seinem Client dann mit diesen Daten.

Abgestandene Lesungen

Starke Lesevorgänge sind der Standardmodus in Cloud Firestore. Dies geht jedoch mit einer möglicherweise höheren Latenz aufgrund der möglicherweise erforderlichen Kommunikation mit dem Leiter einher. Oft muss Ihre Cloud Firestore-Anwendung nicht die neueste Version der Daten lesen und die Funktionalität funktioniert gut mit Daten, die möglicherweise einige Sekunden veraltet sind.

In einem solchen Fall kann sich der Client mithilfe der read_time -Leseoptionen dafür entscheiden, veraltete Lesevorgänge zu empfangen. In diesem Fall werden Lesevorgänge durchgeführt, während die Daten zum read_time vorlagen, und es ist sehr wahrscheinlich, dass das nächstgelegene Replikat bereits überprüft hat, dass es zum angegebenen read_time über Daten verfügt. Für eine deutlich bessere Leistung sind 15 Sekunden ein angemessener Wert. Selbst bei veralteten Lesevorgängen sind die ausgegebenen Zeilen miteinander konsistent.

Vermeiden Sie Hotspots

Die Aufteilungen in Cloud Firestore werden automatisch in kleinere Teile zerlegt, um die Arbeit zur Bereitstellung des Datenverkehrs bei Bedarf oder bei Erweiterung des Schlüsselraums auf mehr Speicherserver zu verteilen. Zur Bewältigung von überschüssigem Datenverkehr erstellte Aufteilungen bleiben etwa 24 Stunden lang erhalten, auch wenn der Datenverkehr wegfällt. Wenn also wiederkehrende Verkehrsspitzen auftreten, werden die Aufteilungen beibehalten und bei Bedarf weitere Aufteilungen eingeführt. Diese Mechanismen helfen Cloud Firestore-Datenbanken bei der automatischen Skalierung bei zunehmender Verkehrslast oder Datenbankgröße. Es gibt jedoch einige Einschränkungen, die Sie beachten sollten, wie unten erläutert.

Die Aufteilung von Speicher und Last nimmt Zeit in Anspruch, und ein zu schnelles Hochfahren des Datenverkehrs kann zu hohen Latenzzeiten oder Fehlern aufgrund von Fristüberschreitungen führen, die üblicherweise als Hotspots bezeichnet werden, während sich der Dienst anpasst. Die beste Vorgehensweise besteht darin, Vorgänge über den Schlüsselbereich zu verteilen und gleichzeitig den Datenverkehr für eine Sammlung in einer Datenbank mit 500 Vorgängen pro Sekunde zu erhöhen. Nach diesem allmählichen Anstieg erhöhen Sie den Verkehr alle fünf Minuten um bis zu 50 %. Dieser Prozess wird als 500/50/5- Regel bezeichnet und positioniert die Datenbank so, dass sie optimal an Ihre Arbeitslast angepasst ist.

Obwohl Aufteilungen automatisch mit zunehmender Auslastung erstellt werden, kann Cloud Firestore einen Schlüsselbereich nur so lange aufteilen, bis ein einzelnes Dokument über einen dedizierten Satz replizierter Speicherserver bereitgestellt wird. Daher kann ein hohes und anhaltendes Volumen gleichzeitiger Vorgänge an einem einzelnen Dokument zu einem Hotspot in diesem Dokument führen. Wenn bei einem einzelnen Dokument anhaltend hohe Latenzen auftreten, sollten Sie eine Änderung Ihres Datenmodells in Betracht ziehen, um die Daten auf mehrere Dokumente aufzuteilen oder zu replizieren.

Konfliktfehler treten auf, wenn mehrere Vorgänge versuchen, dasselbe Dokument gleichzeitig zu lesen und/oder zu schreiben.

Ein weiterer Sonderfall von Hotspotting tritt auf, wenn als Dokument-ID in Cloud Firestore ein sequentiell aufsteigender/absteigender Schlüssel verwendet wird und eine beträchtlich hohe Anzahl von Vorgängen pro Sekunde erfolgt. Das Erstellen weiterer Splits hilft hier nicht, da sich der Traffic-Anstieg einfach auf den neu erstellten Split verlagert. Da Cloud Firestore standardmäßig alle Felder im Dokument automatisch indiziert, können solche beweglichen Hotspots auch im Indexbereich für ein Dokumentfeld erstellt werden, das einen sequentiell steigenden/abfallenden Wert wie einen Zeitstempel enthält.

Beachten Sie, dass Firestore durch Befolgen der oben beschriebenen Vorgehensweisen skaliert werden kann, um beliebig große Arbeitslasten zu bedienen, ohne dass Sie eine Konfiguration anpassen müssen.

Fehlerbehebung

Firestore stellt den Key Visualizer als Diagnosetool bereit, das speziell zur Analyse von Nutzungsmustern und zur Behebung von Hotspotting-Problemen entwickelt wurde.

Was kommt als nächstes