Kontakt

Nino Grossrieder (-Müller)

Schema-on-Read für unsaubere IoT-Payloads: JSONB speichern, typisierte Views generieren

Sensorwerte sind einfach: Zeitpunkt, Signal, Zahl. Das Schema dafür steht in fünf Minuten und hält zehn Jahre.

Der Rest ist es nicht. Zustandsmeldungen, Ereignisse, Typenschild-Informationen, Diagnosepakete – diese Nachrichten haben zwanzig Felder, von denen fünfzehn optional sind, und ihre Form ändert sich mit jedem Firmware-Update. Sie kommen über denselben MQTT-Broker herein wie die Messwerte, verhalten sich aber völlig anders. Wer sie beim ersten Auftreten in ein normalisiertes Schema presst, baut sich eine Migrations-Tretmühle: Jede neue Anlage bringt eine Schemaänderung mit, und bis die deployed ist, steht die Ingestion.

Dieser Artikel zeigt den Ausweg: die Payloads als jsonb speichern und die typisierte, relationale Sicht daraus generieren – aus denselben Modelldefinitionen, die Ihre Anwendung ohnehin schon hat. Die Beispiele stammen aus einer Plattform mit HiveMQ als Broker und TigerData als Zeitreihendatenbank, das Muster funktioniert aber mit jedem PostgreSQL.

Zwei Sorten Nachrichten, zwei Speicherstrategien

Der Fehler ist nicht, JSON zu speichern. Der Fehler ist, alles gleich zu behandeln.

MerkmalSkalare MesswerteSemi-strukturierte Nachrichten
Volumensehr hochniedrig bis mittel
Formstabilvariabel, entwickelt sich
Zugriffaggregieren, chartierennachschlagen, filtern, verbinden
Speicherformschmale, typisierte Hypertablejsonb mit Diskriminator
Rollupsja, hierarchischselten nötig
SchemaänderungMigration nötignur View neu generieren

Die hochfrequenten Messwerte gehören in ein striktes Time-Series-Schema – dort verdient Struktur ihr Geld. Alles andere bekommt die flexible Fahrspur. Dieses Schema-on-Read ist kein Freibrief für die ganze Plattform, sondern ein chirurgisch eingesetzter Notausgang.

JSONB: Flexibilität beim Schreiben

Eine Tabelle mit Zeitpunkt, Quelle, einem Diskriminatorfeld für den Nachrichtentyp und dem vollständigen Payload als jsonb nimmt alles an, was hereinkommt. Ein neues Feld im Firmware-Update? Es ist gespeichert, bevor jemand davon wusste. Ein Feld verschwindet? Historische Nachrichten behalten es.

Damit verschieben Sie eine Entscheidung, die Sie am Anfang eines Projekts ohnehin nicht gut treffen können: wie diese Daten am Ende abgefragt werden. Die Modellierung erfolgt, wenn Sie die Zugriffsmuster kennen – und nicht auf Verdacht.

Der Kern: typisierte Views aus dem Modell generieren

JSON speichern heisst nicht, auf typisierten Zugriff zu verzichten. Denn die Payload-Verträge existieren bereits irgendwo als typisierte Modelle in Ihrem Anwendungscode. Ein kleiner Generator liest so ein Modell und erzeugt daraus eine CREATE VIEW-Anweisung, die das jsonb in typisierte Spalten projiziert – eine View pro Nachrichtentyp, gefiltert über den Diskriminator.

Aus einem Modell wie diesem:

class MachineState(BaseModel):
    machine_id: str
    state: str
    cycle_count: int
    temperature_c: float | None = None

wird automatisch:

CREATE OR REPLACE VIEW v_machine_state AS
SELECT
  ts,
  payload ->> 'machine_id'              AS machine_id,
  payload ->> 'state'                   AS state,
  (payload ->> 'cycle_count')::integer  AS cycle_count,
  (payload ->> 'temperature_c')::real   AS temperature_c
FROM message_raw
WHERE message_type = 'MachineState';

Von aussen sieht das aus wie eine ganz normale Tabelle. Grafana, Power BI und jedes SQL-Werkzeug können damit arbeiten, ohne je ein JSON-Konstrukt zu sehen.

Warum das Modell die einzige Wahrheit bleibt

Der entscheidende Punkt ist die Richtung der Abhängigkeit. Das typisierte Modell ist die Quelle, die View ist Ableitung. Ändert sich der Vertrag, regenerieren Sie die View – und checken das erzeugte SQL zusammen mit Ihren übrigen Migrationen in die Versionsverwaltung ein.

Damit ist ausgeschlossen, was in gewachsenen Systemen der Normalfall ist: dass Anwendungscode und Datenbanksicht auseinanderdriften und niemand mehr sagen kann, welche Definition gilt. Sie bekommen die Flexibilität von JSON beim Schreiben und eine relationale, typisierte Oberfläche beim Lesen – ohne die übliche Doppelpflege.

Ehrlich bleiben: JSONB ist nicht gratis

Drei Einschränkungen, die Sie kennen sollten, bevor Sie das grossflächig einsetzen:

  • Speicher. Feldnamen werden in jeder Zeile mitgespeichert. Bei Millionen Zeilen pro Tag ist das der Grund, warum Messwerte eben nicht in JSON gehören.
  • Abfrageplanung. Der Planer schätzt Selektivität auf JSON-Ausdrücken schlechter als auf echten Spalten. Bei häufig gefilterten Feldern helfen ein GIN-Index auf dem Payload oder eine generierte Spalte, die das Feld herauszieht und indiziert.
  • Keine Typsicherheit beim Schreiben. Was als Text hereinkommt, wird beim Casten in der View zum Problem. Der Cast kann fehlschlagen – fangen Sie das ab, sonst kippt Ihnen die View, sobald ein Gerät «n/a» statt einer Zahl sendet.

Wann Sie doch normalisieren sollten

Sobald ein Nachrichtentyp drei Bedingungen erfüllt, gehört er in echte Spalten: Die Form ist seit Monaten stabil, das Volumen ist relevant, und es wird regelmässig darauf aggregiert oder gejoint. Der Weg dorthin ist dann unkompliziert, weil die Historie im Archiv liegt und sich neu verarbeiten lässt.

Schema-on-Read ist ein Zwischenzustand mit Berechtigung – kein Endzustand. Wer alles dort belässt, hat am Ende einen Data Swamp statt einer Plattform.

Praxis: wo das den Unterschied macht

Industrie und Logistik

Ein Anlagenpark mit Maschinen aus vier Generationen liefert Statusmeldungen in vier Dialekten – in Schweizer Industriebetrieben eher die Regel als die Ausnahme. JSONB nimmt alle an, generierte Views pro Generation machen sie vergleichbar, ohne dass eine neue Maschine die Ingestion blockiert.

Öffentlicher Verkehr

Fahrzeugdiagnosen und Störungsmeldungen enthalten je nach Hersteller unterschiedliche Felder. Die flexible Fahrspur erlaubt es, alles aufzuzeichnen und die Auswertung nachzuziehen, wenn klar ist, welche Felder betrieblich zählen.

Öffentliche Verwaltung

Gebäudeleittechnik unterschiedlicher Baujahre spricht selten dieselbe Sprache. Statt eines Integrationsprojekts pro Liegenschaft entsteht eine gemeinsame Rohschicht mit liegenschaftsspezifischen Views.

Wie wir Sie unterstützen

Der Umgang mit unsauberen Datenverträgen ist die Arbeit, die in Architekturdiagrammen nie auftaucht und trotzdem die halbe Projektzeit frisst. Wir lösen sie als Teil unserer Arbeit an operativen Datensystemen, Datenplattformen und Data Applications – für Industrie und Logistik, Verkehrsbetriebe und Verwaltungen in der ganzen Schweiz.

Ihre Payloads ändern sich schneller, als Ihr Schema mitkommt? → Kontakt aufnehmen – wir zeigen Ihnen, welche Nachrichtentypen strikt und welche flexibel gespeichert gehören.

Weiterführende Glossar-Artikel

kontakt

Wir freuen uns, von Ihnen zu hören!
Nino Grossrieder (-Müller)