Zum Inhalt springen

Storh

Dateibasiertes Speichern von Datensätzen für PHP: JSONC-Dokumente, segmentierte Append-only-Logs und logbasierte Warteschlangen mit UUIDv7-IDs.

Terminal
composer require storh/storh

Neueste Version v0.5.2 MIT PHP 8.2+

In der Praxis

Der Schnellstart in zwei Teilen: zuerst ein Dokument im Speicherstamm, dann ein Logeintrag und ein Warteschlangenauftrag daneben.

Ein Stamm, drei Engines

StorageRoot::resolve() normalisiert ein Basisverzeichnis und einen Namensraum zu einem Stammverzeichnis. Bei der ersten Verwendung erstellt jede Engine darin ihr eigenes Verzeichnis.

Jede Engine gibt StorageRecord-Objekte mit id() und data() zurück. Die Warteschlange hängt jeden Abruf und Abschluss an ihr Ereignislog an.

Dokumente
use Storh\DocStore;
use Storh\Queue;
use Storh\RecordQuery;
use Storh\SegmentedLog;
use Storh\StorageRoot;

$root = StorageRoot::resolve(__DIR__ . '/var/storh', 'demo');

$docs = new DocStore($root, 'pages');
$home = $docs->put([
    'kind' => 'page',
    'title' => 'Home',
]);

$loaded = $docs->get($home->id());

JSONC-Dokumentspeicher

Eine JSONC-Datei pro Datensatz, UUIDv7-IDs, Aufteilung anhand der ersten zwei Zeichen der letzten Gruppe von UUID, atomarer Austausch und Filter für Felder beim Streamen.

Segmentiertes Log

Append-only-Segmente aus JSON-Zeilen mit vorangestellter Länge und Prüfsumme, dünne Indizes, Cursor- und Zeitbereichsabfragen, Absturzwiederherstellung und Komprimierung.

Log-Warteschlange

Aufträge durchlaufen die Zustände Ausstehend, In Bearbeitung und Erledigt; dafür werden kompakte, nur angehängte Warteschlangenereignisse verwendet.

UUIDv7-Datensätze

Sortierbare, zeitlich adressierbare IDs für stabile Cursor, begrenzte Scans und speicherpfadfreundliches Sharding.

Atomare Schreibvorgänge

Schreibvorgänge in temporäre Dateien und atomare Umbenennungen halten Datensätze intakt. Nach einem Absturz des Schreibers bereinigt das nächste Öffnen dessen verwaiste temporäre Dateien.

Wiederherstellung unvollständiger Schreibvorgänge

Beim Lesen von Segmenten werden Längen- und Prüfsummenfelder überprüft; die Wiederherstellung kürzt unvollständige Schlusszeilen.

Dateibasiert

Kein Datenbankserver, kein Framework-Adapter, keine Composer-Laufzeitpakete; du übergibst ein Verzeichnis.

Liest JSONC

Datensatzdateien dürfen Kommentare und nachgestellte Kommas enthalten. storh schreibt kompakte JSONC-Einzeiler JSON; Kommentare bleiben daher bis zum nächsten Schreibvorgang erhalten.

Abfrage-Builder

Fluent-Abfragen wie where('status')->in([...]) mit DocStore und SegmentedLog; DocStore verwendet vorhandene sekundäre Indizes.

Sekundäre Indizes

Gleichheits-, eindeutige und Bereichsindizes werden als Dateien neben der Sammlung gespeichert, aus den Datensätzen abgeleitet und können jederzeit neu erstellt werden.

Schemas

Optionale Sammlungsschemas überprüfen jeden Schreibvorgang und legen fest, welche Felder indiziert werden.

SQL Mirror

Übertrage Sammlungen in SQLite oder MySQL für Joins, Suche und Berichte, während die Dateien maßgeblich bleiben. ext-pdo oder ext-mysqli wird nur bei Verwendung benötigt.

Warum Storh

Ein Verzeichnis statt eines Servers

Du übergibst ein Basisverzeichnis, in das storh schreibt. Benötigt werden PHP 8.2 und ext-json, aber weder Composer-Laufzeitpakete noch ein Framework-Adapter.

Eine Datensatzform, drei Speicherlayouts

Dokumente, Logs und Warteschlangen speichern alle eine UUIDv7-ID und ein Datenarray. Wähle das Speicherlayout passend zur Arbeitslast und lege alle drei unter einem Stammverzeichnis ab.

Prüfungen zum Ausführen

verify() vergleicht Indizes und Logstatus mit einem vollständigen Dateiscan. repair() stellt beschädigte Datensätze unter Quarantäne und kürzt unvollständige Logenden; CLI gibt JSON für cron-Aufträge und Zustandsprüfungen aus.

Benchmarks samt Konfiguration

Das README veröffentlicht Medianwerte für 50,000 Datensätze pro Engine auf einem Apple M1 Pro mit PHP 8.5, zum Beispiel 5.6 µs pro Einzelabfrage. Bei jedem Schreibvorgang dieser Läufe wird fsync ausgeführt; die Schreibraten hängen daher vom Dateisystem ab.

Im Vergleich zu

storh ist dateibasierter Speicher statt einer relationalen Abfrage-Engine. Diese Tools übernehmen daher einige Aufgaben, die storh auslässt.

Storh im Vergleich zu anderen Werkzeugen
SQLite Gut geeignet fürEine eigenständige SQL-Engine in einer einzigen Datei mit serialisierbaren ACID-Transaktionen, die auch bei einem Programmabsturz oder Stromausfall bestehen bleiben. Die Dokumentation beschreibt sie als Ersatz für improvisierte Datendateien. Unterschiede zu Storhstorh speichert einen Datensatz pro JSON-Datei; der Kern benötigt keinen PDO-Treiber. Transaktionen über mehrere Datensätze gibt es nicht, und laut Dokumentation gehört diese Aufgabe in eine echte Datenbank. Sammlungen lassen sich für Joins und Berichte in SQLite übertragen, während die Dateien maßgeblich bleiben.
MySQL Gut geeignet fürEine relationale Datenbank, die von einem multithreadfähigen SQL-Server bereitgestellt wird, mit Clientprogrammen und Bibliotheken, Verwaltungstools und einer breiten Auswahl an APIs. Unterschiede zu Storhstorh läuft innerhalb des PHP-Prozesses und greift auf ein Verzeichnis zu; ein Serverbetrieb ist daher nicht nötig. Für Joins, Suche und Berichte spiegelt es Sammlungen in MySQL, auch über ein vorhandenes mysqli-Handle. Die Dateien bleiben die maßgebliche Datenquelle.
SleekDB Gut geeignet fürEine Flat-File-Dokumentdatenbank in PHP ohne Abhängigkeiten von Drittanbietern, die schemalose Daten als einfache JSON-Dateien speichert. Ihre Abfragesprache API umfasst Joins, Textsuche, group by und having sowie Filter für verschachtelte Eigenschaften. Unterschiede zu Storhstorh kombiniert seinen Dokumentspeicher mit einem segmentierten Append-only-Log und einer logbasierten Warteschlange unter einem Stammverzeichnis und führt vor der Bestätigung jedes Schreibvorgangs fsync aus. Joins gehören nicht zu storh; dafür überträgt es Sammlungen in SQLite oder MySQL.

Fragen und Antworten

Kurze Antworten aus der Storh-Dokumentation.

Starte mit dem Schnellstart und verwende anschließend „Eine Engine auswählen“, um DocStore, SegmentedLog oder Queue passend zu deiner Arbeitslast zu wählen.