|
7 Min. Lesezeit
|

So laden Sie 1 Milliarde FHIR-Ressourcen in 5 Stunden in Aidbox

Diesen Artikel zusammenfassen mit:
ChatGPTPerplexityClaudeGrok

Große Datenmengen bedeuten in der Regel große Probleme und viel Aufwand. Eine dieser Herausforderungen ist das Laden großer Datenmengen. Üblicherweise gibt es eine Quelle für Big Data, und das Problem stellt sich als eine Art Legacy-System-Migration oder Migration historischer Daten auf einen FHIR-Server dar.

Die typische Lösung für solche Probleme besteht darin, eine ETL-Pipeline (Extract, Transform, Load) aufzubauen:

Wesentliche Punkte dieser Architektur:

  • Daten transformieren und in den Speicher hochladen. Dadurch lässt sich der gesamte Prozess in zwei Schritte aufteilen: Datenvorbereitung und Daten-Upload.
  • Gespeicherte Daten komprimieren: FHIR-Daten enthalten viele ähnliche Strukturen, z. B. {:resourceType "Patient" … }, sodass das Komprimieren solcher Daten sehr effizient ist und die Netzlast dadurch um das Zehnfache reduziert werden kann.

Die Aufgabe der Transformation und Speicherung der eingehenden Daten ist stark vom Zustand des Systems abhängig, für das der Import durchgeführt wird; daher wird dieser Aspekt in diesem Artikel nicht weiter behandelt. Dennoch benötigten wir für unseren Test eine Datenquelle. Daher wurde das Tool Synthea verwendet, das 40 Batches erzeugte, von denen jeder rund 25.000 Patient-Ressourcen sowie deren zugehörige Ressourcen enthielt. Das Skript zur Generierung ist über folgenden Link zugänglich: gen_synthea.sh.

Best Practices für das FHIR Bulk Load

Die Aufgabe, große Datenmengen zu laden, ist nicht neu, und es gibt eine Reihe bewährter Verfahren, die sich für diese Aufgabe als effizient erwiesen haben.

Aus der Perspektive der HTTP-Datenübertragung über das Netzwerk ist das Laden von Daten Stück für Stück offensichtlich nicht der beste Ansatz, da dabei ein erheblicher Overhead durch den Verbindungsaufbau, den Empfang der Antwort usw. entsteht. Die Datenübertragung in Batches kann den Overhead-Aufwand erheblich reduzieren und die Datenübertragung deutlich beschleunigen. Weitere Verbesserungen ergeben sich durch Streaming, bei dem der gesamte Overhead entfällt.

Verwenden Sie den PostgreSQL COPY-Befehl, um alle Zeilen mit einem einzigen Befehl zu laden, anstatt eine Reihe von INSERT-Befehlen zu verwenden. Der COPY-Befehl ist für das Laden großer Zeilenmengen optimiert. Er ist weniger flexibel als INSERT, verursacht jedoch bei großen Datenladeoperationen deutlich weniger Overhead.

Datenbankindizes können die Einfügegeschwindigkeit verlangsamen, da jedes Tabellen-Insert eine Aktualisierung aller Indizes der Tabelle erfordert, um die Transaktionalität zu gewährleisten. Das Erstellen von Indizes auf vorhandenen Daten ist wesentlich schneller als inkrementelle Aktualisierungen bei jedem einzelnen Insert. Daher empfiehlt es sich, alle vorhandenen Tabellenindizes vor dem Laden der Daten zu deaktivieren (oder zu löschen), einen Insert mittels des COPY-Befehls durchzuführen und die Tabellenindizes anschließend wieder zu aktivieren (oder neu zu erstellen).

Die Besonderheit der Datenspeicherung in Aidbox in Form von JSONB sowie sehr große FHIR-Ressourcen können zu Schwierigkeiten mit TOAST führen. Im Wesentlichen entsteht das Problem, wenn die Daten nicht in eine PostgreSQL-Seite passen und die Datenbank-Engine beginnt, einen Teil der Daten in die TOAST-zugewiesene Tabelle auszulagern. Bei nachfolgenden Lese- und Schreibvorgängen wird intern auf zwei Tabellen zugegriffen: die TOAST-zugewiesene und die Standardtabelle.

Es gibt verschiedene Methoden, um TOAST zu reduzieren:

  • explizite Speicherkonfiguration
  • Erhöhung des Komprimierungslimits

ALTER TABLE <target_table> ALTER COLUMN resource SET STORAGE MAIN; ALTER TABLE <target_table> SET (toast_tuple_target = 6000);

Die load-from-bucket-Operation

Aidbox verfügt über eine load-from-bucket-Operation, die einen asynchronen Upload von FHIR-Ressourcen aus einem S3-Bucket ermöglicht. Diese Operation wurde speziell für das parallele Batch-Laden großer Mengen an FHIR-Daten entwickelt.

Funktionsweise:

  • Die Liste der Dateien aus dem Bucket abrufen
  • Alle Indizes für die Ziel-Ressourcentabellen deaktivieren und temporär speichern
  • n parallele Lade-Threads starten
  • Für jeden Thread einen Stream aus einem der S3-Buckets für die zu ladenden Dateien öffnen und auf der anderen Seite einen PostgreSQL COPY-Stream öffnen
  • Ressourcen parallel im Stream konvertieren
  • Nach dem Laden aller Ressourcen alle Indizes wieder aktivieren

Hinweis: Das Deaktivieren von Indizes wird angewendet, wenn das Flag disable-idx? angegeben ist. Die load-from-bucket-Operation mit dem Flag disable-idx? wird für den Einsatz auf einer produktiven Live-Umgebung nicht empfohlen, da diese Operation alle Indizes für die Zieltabellen deaktiviert.

Umgebung

Infrastruktur für diesen Test:

  • Aidbox installiert auf einer AWS m5.4xlarge-Instanz mit 16 vCPU
  • Datenbank: AWS RDS Aurora auf dem Instanztyp db.r5.4xlarge mit 16 vCPU

Experiment

Zunächst versuchen wir, ca. 10 % der Daten zu laden und zu beobachten, wie Aidbox reagiert:

  • Gesamte Ladegröße (komprimiert): 17 GB
  • Gesamte Ressourcenanzahl: 93 Millionen
  • Resultierende Datenbankgröße: 171 GB

Beispielaufruf der Aidbox load-from-bucket RPC-Operation:

POST /rpc content-type: text/yaml accept: text/yaml

method: aidbox.bulk/load-from-bucket params: bucket: s3:// prefixes: ["fhir/1/", "fhir/2/", "fhir/3/", "fhir/4/"] thread-num: 16 disable-idx?: true account: access-key-id: secret-access-key: region:

Lassen Sie uns Importe mit verschiedenen Thread-Anzahlen (1, 2, 4, …, 16) ausführen und beobachten, wie sich die Ausführungszeit und die Durchschnittswerte verändern.

Wie aus dem Diagramm ersichtlich, wächst die allgemeine Datenladeperformance mit der Erhöhung der Threads von 1 auf 4 nahezu linear. Die weitere Erhöhung der Threads von 4 auf 12 ist kaum signifikant. Die zusätzliche Erhöhung von 12 auf 16 hat keinen Einfluss auf das Leistungswachstum. Die Leistungsgrenzen können durch die Netzwerkbandbreite, den Datenbankdurchsatz oder Ähnliches verursacht werden. In unserem Experiment gab es weder Einschränkungen seitens der Datenbank noch der Netzwerkbandbreite – die Java-Performance war der Hauptengpass.

Import-Durchsatz pro Sekunde nach Ressourcentyp bei 16 Threads für 10 % der Daten

Aus unseren Beobachtungen schließen wir, dass die Datenladeperformance durch die Verteilung der Daten nach Volumen beeinflusst wird. So ist aus dem obigen Diagramm ersichtlich, dass kleine Ressourcen wie Procedure oder Observation mit einer Geschwindigkeit von bis zu 40.000 Ressourcen pro Sekunde geladen werden können. Große Ressourcen wie ExplanationOfBenefit hingegen lassen sich nur mit bis zu 5.300 Ressourcen pro Sekunde laden, was 20 % des gesamten Datensatzvolumens ausmacht. Eine solche Datenungleichmäßigkeit beeinflusst letztlich den Durchsatz und die Gesamtdauer des Datenimportprozesses erheblich und kann daher von Projekt zu Projekt stark variieren.

Möchten Sie mehr über die Details erfahren? Tauchen Sie tiefer in unseren Prozess ein und erfahren Sie, wie Sie das Laden Ihrer FHIR-Ressourcen optimieren können. Hier mehr erfahren

1 Milliarde FHIR-Ressourcen laden

Basierend auf den obigen Experimenten haben wir uns entschieden, die Daten für den endgültigen Import mit 12 Threads zu laden. Die Gesamtdauer des Imports betrug 4 Stunden und 48 Minuten. Dabei wurden etwa 1 Milliarde Ressourcen erstellt, bei einer Gesamtdatenbankgröße von 1.649 GB.

Nachfolgend eine Zusammenfassung des Import-Ergebnisses:

  • 4 Std. 48 Min. Ausführungszeit
  • 183 GB gesamte komprimierte Datengröße
  • 1.649 GB resultierende Datenbankgröße
  • 1 Milliarde gesamte Ressourcenanzahl
  • 57.000 durchschnittliche Ressourcenanzahl pro Sekunde
  • 64 Patientendaten pro Sekunde

Import-Durchsatz pro Sekunde nach Ressourcentyp bei 12 Threads für 100 % der Daten

Dieses Diagramm veranschaulicht den allgemeinen Import-Durchsatz des Daten-Uploads. Es ist zu erkennen, dass die Geschwindigkeit des Prozesses nicht konstant ist und auf ihrem Höhepunkt zwischen 8.000 und 80.000 Ressourcen pro Sekunde schwanken kann. Der durchschnittliche Durchsatz betrug 57.000 Ressourcen pro Sekunde, was 10 % mehr war als bei den vorherigen Tests.

Die deutlichsten Geschwindigkeitseinbrüche wurden bei der Provenance-Ressource festgestellt. Die durchschnittliche Datensatzgröße für diese Ressource betrug 37 KB, was sehr groß ist. Datensätze dieser Größe werden langsam verarbeitet und verursachen Komplexitäten im Zusammenhang mit der TOAST-Technologie in Postgres. Die endgültigen Durchsatzstatistiken sind stark von der Struktur und dem Volumen der Daten abhängig: Kleine Ressourcen werden nahezu sofort geladen, während große sehr langsam geladen werden.

Ressourcenanzahl und Gesamtgrößenverteilung der 15 größten Tabellen

Das Verteilungsdiagramm ist besonders interessant. Es zeigt, dass es im Durchschnitt etwa 300 Observations pro Patient-Ressource gibt, und dass die Ressourcen, die den meisten Speicherplatz beanspruchen, Claim, DiagnosticReport, DocumentReference und ExplanationOfBenefit sind. Die Provenance-Ressource verdient besondere Aufmerksamkeit: Bei einer Gesamtanzahl von 1 Million Einträgen beansprucht sie 40 GB Speicherplatz, bei einer durchschnittlichen Ressourcengröße von 37 KB.

CPU-Auslastung von Aidbox und Aurora

Das Diagramm zur CPU-Auslastung von Aidbox und Aurora zeigt, dass während nahezu der gesamten Dauer des Datenimports alle 12 Aidbox-Kerne vollständig ausgelastet waren. Gleichzeitig betrug der durchschnittliche CPU-Verbrauch der Aurora-Datenbank 54 %. Im Diagramm ist ein starker Anstieg des CPU-Verbrauchs der Datenbank und ein gleichzeitiger Einbruch bei Aidbox zu erkennen. Dies ist auf die Übertragung der großen Provenance-Ressourcen zurückzuführen. Zu diesem Zeitpunkt begannen die Aidbox-Threads auf eine Antwort der Datenbank zu warten, während die Datenbank selbst damit beschäftigt war, die Daten zu komprimieren, um sie in TOAST zu speichern.

Um mit dem Laden großer Datensätze in Aidbox zu experimentieren und die Skalierbarkeit aus erster Hand zu erleben, probieren Sie die kostenlose Version von Aidbox aus. Sie bietet eine robuste Umgebung zum Testen dieser Funktionen mit allen erforderlichen Tools und ohne Funktionseinschränkungen.

Fazit

Die naheliegendste Lösung ist nicht immer die optimale. Manchmal muss man das Problem anders formulieren oder bestimmte technische Kompromisse eingehen, um eine effektive Lösung zu finden. In diesem Beitrag präsentieren wir unsere Sichtweise auf die Best Practices für das Hochladen großer Datensätze in Aidbox.

Autoren: Marat Surmashev, Georgii Ivannikov

Aidbox FHIR Platform Free Trial

Siehe auch: Aidbox Horizontal & Vertical Scaling und Health Samurai Lab: Game of Pools.

Diesen Artikel teilen
Comments
Comments
Sign in
Loading comments...
Subscribe to our blog

Get the latest articles on FHIR, interoperability, and healthcare IT.