It’s personal: gepflegt speichern

Fragen des Designs zur Speicherung persönlicher Daten
Kommentare

Die persönlichen Daten der Nutzer eines Softwaresystems sind zu speichern. Meist geschieht dies direkt auf dem Gerät, auf dem die Anwendung ebenfalls installiert ist. Da es sich dabei nur um einen sehr kleinen Teil der gesamten Datenmenge handelt, wird ihnen von Softwarearchitekten oft keine besondere Aufmerksamkeit geschenkt. Ein Fehler, bei dem Ärger oft vorprogrammiert ist, etwa wenn Support nötig wird oder zusätzliche Funktionen in neuen Versionen einer Anwendung umgesetzt werden sollen.

Entwickler Magazin

Der Artikel „It’s personal: gepflegt speichern“ von Dmitry Poloyko ist erstmalig erschienen im Entwickler Magazin 2.2012

Dieser Artikel schildert empfehlenswerte Vorgehensweisen, um solchen Ärger zu vermeiden. Er beschreibt, wie eine professionelle Datenspeicherung an dieser Stelle zu gestalten und umzusetzen ist. Dabei geht er auf die Architektur sowie die Vor- und Nachteile eigener Datenformate von XML (Extensible Markup Language) und der Arbeit mit einem DBMS (Datenbankmanagementsystem) wie SQLite ein.

Persönliche Daten: Worum geht es dabei?

Bei den fraglichen Daten handelt es sich um die unterschiedlichsten Typen. Es kann um persönliche Informationen zu Konfiguration und Nutzung, für die Identifikation oder zur Authentifizierung gehen, aber auch um Prozessdaten, die üblicherweise nicht mit anderen Anwendern geteilt werden, oder um die Daten im Cache eines Systems. Dieser kleine Teil der Daten wird, wie bereits erwähnt, zumeist nicht auf dem Server, sondern auf dem lokal genutzten Gerät gespeichert, auf dem sich auch die Applikation befindet. Auch wenn die allgemein akzeptierte Theorie und Praxis des Datenbankmanagements etwas anderes empfiehlt, sind Softwarearchitekten bei deren Einrichtung oft nicht sorgfältig genug und schlagen sich anschließend beispielsweise bei der Erweiterung einer Software mit Problemen herum, die vorher vermeidbar gewesen wären. Üblicherweise geht es hier um persönliche Informationen; also um Daten, die zur Konfiguration einer Anwendung dienen: Um Name und ID, alte Konfigurationseinstellungen, Informationen zur Historie oder Zugangsschlüssel für die Identifikation und Authentifizierung auf einem Remote Server. In einigen Fällen werden allerdings auch Prozessdaten lokal gespeichert. Außerdem werden oft auch Daten vom Server lokal im Cache gespeichert, um den Datenverkehr im Netz zu reduzieren. Diese ermöglichen dem Anwender aber auch die Arbeit im System, wenn der Zugriff auf den Server einmal nicht möglich ist. Daten zur Konfiguration einer Anwendung sind meist sehr klein. Die zu verarbeitenden Daten und die Daten im Cache können aber auch sehr umfangreich werden. Wird eine Applikation dann auch noch vom Administrator für mehrere Anwender eines Geräts eingerichtet, kommen noch zusätzliche Sicherheitsaspekte ins Spiel.

Leicht eingeführt und schwer gepflegt

Das Speichern der fraglichen Daten in einer binären Datei ist meist der erste Gedanke eines Entwicklers. Und dieser Zugang hat tatsächlich Vorteile. Der wichtigste ist, er ist leicht zu implementieren. Die meisten Programmiersprachen unterstützen den Entwickler beim Lesen und Schreiben dieser Daten sehr gut. Überdies gibt es hilfreiche Techniken, wie die Serialisation von Objekten, die auch das Speichern komplexer Datenstrukturen gestatten. Der zweite Vorteil besteht darin, dass ein derartiges Vorgehen üblicherweise nur wenig Speicherplatz benötigt. Es gibt keinen Grund, den Code von Dateien separat zu analysieren, also zu parsen. Daher sind sie zumeist schnell ausgelesen. Das typische Szenario sieht dann so aus, dass eine Anwendung ihre Daten bei der Startup-Prozedur lädt und alle Informationen speichert, wenn ein System geschlossen wird. Andererseits kann die Nutzung eines binären Formats bei der Entwicklung und Einführung einer Software auch Probleme bereiten. Die problematischste Frage ist die der Rückwärtskompatibilität. Schon eine geringfügige Änderung des Datenformats führt zu schwerwiegenden Inkompatibilitäten mit Vorgängerversionen. Die Datenkonversion während dieses Prozesses erfordert daher zusätzlichen Entwicklungsaufwand.

Glücklicherweise lässt sich dieser Herausforderung schon in der Designphase wirksam begegnen. Es empfiehlt sich, Dateien mit einer Nummer beginnen zu lassen, die der Identifikation der Formatversion dienen. Geschieht dies, ist es einfach, einen sauberen Weg zu deren Auslesen anzulegen. Ebenfalls eine gute Idee ist es, am Anfang einige „Magische Nummern“ einzufügen. Sie dienen der leichten Identifizierung einer Datei. Besonders hilfreich ist das, wenn die Versionsnummer fehlt, während grundlegende Formatspezifikationen geschrieben werden. Startet die Datei nicht mit einer solchen Magischen Nummer, liegt die erste Version des gewählten Formats vor. Alle anschließenden Versionen starten mit dieser Nummer, gefolgt von der ID der Datenversion. Beim Start lässt sich so eindeutig feststellen, aus welcher Version die Daten stammen. Sie werden dann entsprechend korrekt gelesen und im aktuellen Format gespeichert. Tabelle 1 zeigt das entsprechende Schema.

Tabelle 1: Schema Magischer Nummern und Versions-ID

Der zweite Grund, mit dem binären Format vorsichtig zu sein, besteht in der Datenintegrität. Eine goldene Regel besagt, dass alles so lange perfekt läuft, bis die Anwendung kollabiert. Am schlimmsten ist ein Crash in dem Moment, in dem Daten auf die Platte geschrieben werden. In diesem Fall verfügt eine Anwendung beim nächsten Start nur über unvollständige Informationen. Die Stelle des Abbruchs ist meist leicht zu identifizieren, die Wiederherstellung der Daten ist allerdings sehr schwierig. Gut ist es daher, wenn die vorherigen Daten als Kopie für ein Backup genutzt worden sind. Sie werden neu eingelesen und die Kopie wird nach der kompletten Speicherung wieder entfernt. So können die Daten nach einem Crash über die Backup-Kopie nach dem letzten regulären Ausstieg wieder hergestellt werden. Sind die Daten wichtig, können sie auch automatisch in bestimmten Zeitintervallen gesichert werden. Etwa dann, wenn die CPU-Kapazitäten dafür frei hat. Zuerst muss man natürlich prüfen, ob Daten wirklich geändert worden sind. Dieses Vorgehen empfiehlt sich auf mobilen Geräten, um zusätzliche belastende Operationen zu vermeiden, Energie zu sparen oder um weniger Schreibzyklen auf Flash-Speicher durchzuführen. Geht ein PC in die Stand-by- oder Ruhephase, ist es ebenfalls empfehlenswert, Daten zu sichern.

Die dritte Herausforderung, die zu hohen Ansprüchen an die Entwicklung führt, ist die Notwendigkeit eines speziellen Werkzeugs für die Generierung von Stichprobendaten, also die Analyse und das Editieren von gespeicherten, binären Daten. Der Bedarf an einem derartigen Werkzeug ist zu Beginn der Entwicklung nicht offensichtlich. Aber die Experten für Qualitätssicherung und Mitglieder des Supportteams werden sicher danach fragen. Daher sollte der Punkt in die Überlegungen aufgenommen werden.

Die vierte Herausforderung betrifft das Teilen von Daten zwischen verschiedenen, ausführbaren Modulen einer Applikation. Denn manche Anwendungen sind als Set von separaten, ausführbaren Einheiten gestaltet. Jede davon ist für einen speziellen Teil der Funktionalität verantwortlich. Eine derartige Konfiguration erfordert den verteilten Zugriff auf gemeinsame Daten. Und das bei gleichzeitig ordentlichen Schutzvorkehrzungen.

Es gibt nun allerdings zwei gewichtige Gründe, die den Einsatz des binären Formats verhindern: Zunächst einmal ist dieses Format sehr stark abhängig von der Technologie, mit der es entwickelt worden ist. Dies insbesondere dann, wenn Objekte mithilfe von Techniken wie der Serialisation gespeichert werden. Wird dann etwa die Programmiersprache verändert, kann das Lesen der Daten mit dem neuen Entwicklungswerkzeug und mit neuen Bibliotheken extrem kompliziert werden. Da Daten zwischen unterschiedlichen Geräten üblicherweise nicht ausgetauscht werden, ist die Frage der unterschiedlichen, binären Darstellung einiger Typen davon auf unterschiedlichen Geräten nicht wichtig. Wer den Einsatz unterschiedlicher Anwendungen auf unterschiedlichen Plattformen plant, sollte aber die Größe verschiedener Integer-Typen, ihre Darstellung mit Big/Little-Endian Byte-Reihenfolge oder verschiedene Real-Typen-Standarddarstellungen bedenken. Denn in einem plattformunabhängigen Format ist deren Pflege und Analyse weitaus komplizierter. Ein anderer Punkt auf der persönlichen Checkliste sollte der potenzielle Umfang einer Datei sein. Denn das Lesen und Schreiben großer Datenmengen kann zu erheblichen Verzögerungen beim Starten und Schließen einer Anwendung führen. Der Einsatz verschiedener, separater Dateien für jeden Objekttyp und ein zufälliger Zugriff statt des Lesens kompletter Datensätze kann die Geschwindigkeit verbessern. Die damit verbundene Architektur erschwert die Entwicklungsarbeit allerdings erheblich.

Sicherheitsprobleme beim Parsen im eigenen Textformat

Die Erstellung eines eigenen Textformats kann die Frage nach dem Einsatz eines geeigneten Analysewerkzeugs vermeiden. Auch die Abhängigkeit vom Entwicklungswerkzeug und Fragen der Plattformkompatibilität werden so minimiert. Nur die Code-Pages für ASCII-verschlüsselten Text sollten in diesem Fall besonders beachtet werden. Andererseits erfordert die Entwicklung eines eigenen Parsers zusätzliche Anstrengungen und kann zu Sicherheitsproblemen wie Buffer Overrun oder Statement Injection führen. Bedenken sollte der Entwickler auch, dass ein Textformat doppelt so viel Speicherplatz und Ladezeit benötigen kann, wie ein identischer, binärer Datensatz. Außerdem verursacht das Textparsing Verzögerungen beim Ladevorgang.

XML: gute Wahl für die Speicherung von Konfigurationsdaten

Der Einsatz von XML ist im Vergleich zur Nutzung eines „eigenen“ Textformats meist die bessere Alternative. In diesem Fall kann auch ein bewährter XML-Parser, statt eines selbst entwickelten, eingesetzt werden. Auch für das Lesen und Editieren können schon existierende Werkzeuge eingesetzt werden. Ein anderer Vorteil von XML liegt darin, dass ein hinzugefügter Tag von früheren Versionen eines Programms ignoriert wird. Es entstehen also keine Kompatibilitätsprobleme. Trotzdem ist es nicht zu empfehlen, sehr große XML-Dateien zu erstellen. XML ist gut geeignet für die Verwahrung von Konfigurationsschlüsseln, kleineren Datenmengen unterschiedlichen Typs oder anderen, damit vergleichbaren Aufgaben.

Der Einsatz eines DBMS

Wenn ein Programm mit großen Datenmengen arbeitet, auf die zufällig zugegriffen wird, ist ein DBMS die beste Lösung. Denn damit ist es nicht mehr notwendig beim Start und beim Beenden einer Anwendung mit den kompletten Daten zu arbeiten. Und nur noch die Änderungen werden auf die Platte geschrieben. Der Inhalt der Daten wird über die Konsole der DBMS bearbeitet und betrachtet. Eine Erweiterung des Datenformats durch neue Tables oder Columns berührt den alten Code nicht sehr stark. Über Transactions wird zudem die Datenintegrität geschützt. Drei Dinge verhindern möglicherweise den Einsatz eines DBMS:

  1. Es ist auf Anwenderseite nicht leicht aufzusetzen.
  2. Verglichen mit anderen Lösungen ist es relativ teuer.
  3. Der Ressourcenbedarf ist hoch.

DBMS-Lösungen müssen meist installiert und konfiguriert werden. DBMS-Module vergrößern Installationsdateien, können zu potenziellen Konflikten mit neuen oder früheren Versionen einer Anwendung führen und benötigen möglicherweise zusätzliche Lizenzen. Außerdem laufen die meisten DBMS Engines mit eigenen Prozessen, die CPU- und Speicherressourcen belasten. Auch ist die Leistungsfähigkeit eines DBMS für kleinere Datenvolumina vielleicht überdimensioniert.

Die Anforderungen an ein DBMS, das nur zur Speicherung von Applikationsdaten genutzt wird, unterscheiden sich von denen, die bei der Nutzung als Datenbankserver erforderlich sind. Es sollte ein kompaktes, verteilbares Modul sein, das weder Installation noch Konfiguration benötigt und keine Konflikte mit anderen Softwaresystemen verursacht. Und ein Minimum an Systemressourcen sollte dafür ebenfalls schon genügen. Allerhand also.

SQLite [1] ist ein gutes Beispiel für eine Datenbank, die diesen Anforderungen genügt. Als Open Source DBMS ist sie zudem frei verfügbar, also Public Domain. Die Größe der ausführbaren Datei liegt unter 0,5 Megabyte. Sie erlaubt die Integration von sqlite3.c in den Sourcecode und das gemeinsame Kompilieren, um den Einsatz separater Binaries zu vermeiden. Die Möglichkeit des Abschaltens nicht benötigter Optionen verringert zudem die Größe der Binaries. Sie ist sehr schnell und unterstützt die meisten SQL92-Eigenschaften. Die komplette Datenbank wird in einer einzigen Datei und in einem plattformunabhängigen Format gespeichert. Sehr vorteilhaft ist die Tatsache, dass SQLite so genannte Transactions unterstützt und die Datenbank so immer in einem korrekten Zustand vorhält.

Mit den geschilderten Eigenschaften kann die SQLite-Datenbank eine gute Alternative zur Entwicklung eines maßgeschneiderten, binären Formats sein. Das API des SQLite APIs ist zudem sehr praktisch, wenn es um den Zugriff einzelner Anwender auf die Datenbank geht. Es unterstützt Prepared Statements, die sehr effizient arbeiten, wenn Datenbankabfragen nur bezüglich gebundener Parameter variieren. Der gleichzeitige Zugriff mehrerer Anwender ist möglich. In diesem Fall muss eine Anwendung das reset/finalize von Prepared Statements dann allerdings sehr schnell realisieren. Ansonsten können andere Instanzen nur lesen, aber nicht schreiben. Das geschieht aufgrund eines speziellen Locking-Mechanismus, der in SQLite integriert ist. Daneben gibt es auch die Möglichkeit, mit verschiedenen Datenbankdateien eine einzige SQLite-Verbindung gemeinsam zu nutzen.

Restriktionen finden sich natürlich auch. So ist es nicht möglich, Dateien anzuhängen, während eine Transaktion aktiv ist. Das Entfernen einer Datei während eines aktiven Statements ist ebenfalls nicht möglich. Der Einsatz verschiedener Datenbanken, die durch eine SQLite-Verbindung anstelle einer heterogenen Transaktionsverwaltung gehandhabt werden, ist nicht zu empfehlen. Atomarität funktioniert in einigen Transaktionsmodi, wie etwa WAL, nicht. Und auch nicht in Situationen, in denen die Hauptverbindung zur Datenbank die In-Memory-Datenbank ist. Ein letzter Punkt: SQLite reduziert die Datenbank niemals, ehe nicht ein VACUUM-Statement ausgeführt worden ist. Dieses kann nach einer massiven DELETE-Operation durchaus hilfreich sein.

SQLite unterstützt wenn gewünscht auch Verschlüsselung (Data Encryption) und Compression. Diese Lösungen sind allerdings nicht Public Domain. Der Einsatz von SQLite in konkreten Projekten zeigt, dass damit Daten mit bis zu 100 Megabyte effizient verwaltet werden können. SQLite-Datenbanken sind dabei voll wettbewerbsfähig. Werden sie in der Praxis mit Applikationen verglichen, die andere Datenbanken nutzten, wird das eindrucksvoll bestätigt. Bei einer von SaM Solutions entwickelten Anwendung wurde dieser Vergleich einmal durchgespielt. Die Leistungsfähigkeit von SQLite war beim Lesen von Daten deutlich überlegen und beim Schreiben vergleichbar.

Dmitry Poloyko ist Projektmanager und IT-Analyst bei SaM Solutions in Minsk. SaM Solutions ist einer der großen Anbieter von Outsourcing-Dienstleistungen für die Softwareindustrie. Das Unternehmen ist seit mehr als 17 Jahren auf dem deutschen und europäischen Markt aktiv. Es hat seinen Hauptsitz in Gilching bei München und betreibt große Entwicklungszentren in Minsk (Weißrussland) und Dnepropetrovsk (Ukraine). Derzeit arbeiten mehr als 600 hochkarätige Fachkräfte weltweit für das Unternehmen. Weitere Informationen gibt es unter www.sam-solutions.de.
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -