Storh
Des enregistrements stockés dans des fichiers pour PHP : documents JSONC, journaux segmentés en ajout seul et files d’attente adossées à un journal avec des identifiants UUIDv7.
composer require storh/storhDernière version v0.5.2 MIT PHP 8.2+
$pages = $docs
->query()
->where('kind')->eq('page')
->orderBy('title')
->limit(20)
->get();En pratique
La prise en main rapide en deux étapes : un document dans un répertoire de stockage, puis une entrée de journal et une tâche en file d’attente à côté.
Une racine, trois moteurs
StorageRoot::resolve() normalise un répertoire de base et un espace de noms pour former une racine ; chaque moteur y crée son propre répertoire à sa première utilisation.
Chaque moteur renvoie des objets StorageRecord avec id() et data(). La file d’attente ajoute chaque prise en charge et chaque achèvement à son journal d’événements.
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());$log = new SegmentedLog($root, 'events');
$log->put([
'type' => 'page.saved',
'pageId' => $home->id(),
]);
foreach ($log->stream(RecordQuery::all()->after($home->id())->limit(50)) as $record) {
// Process records lazily.
}
$queue = new Queue($root, 'jobs');
$jobId = $queue->enqueue(['task' => 'render', 'pageId' => $home->id()]);
$job = $queue->claim();
if (null !== $job) {
$queue->complete($job->id());
}Stockage de documents JSONC
Un fichier JSONC par enregistrement, identifiants UUIDv7, partitionnement selon deux caractères du dernier groupe de UUID, remplacement atomique et filtres de champs en flux.
Journal segmenté
Segments en ajout seul composés de lignes JSON préfixées par leur longueur et une somme de contrôle, index clairsemés, lectures par curseur et plage horaire, récupération après plantage et compactage.
File d’attente de journal
Les tâches passent par les états en attente, en cours de traitement et terminée au moyen d’événements compacts de file d’attente en ajout seul.
Enregistrements UUIDv7
Identifiants triables et consultables par date pour des curseurs stables, des parcours bornés et des chemins de stockage adaptés au partitionnement.
Écritures atomiques
Les écritures dans des fichiers temporaires et les renommages atomiques préservent l’intégrité des enregistrements. Après le plantage d’un processus d’écriture, la prochaine ouverture nettoie les fichiers temporaires orphelins.
Récupération après écriture partielle
La lecture des segments valide les champs de longueur et de somme de contrôle ; la récupération tronque les lignes finales partielles.
Fichiers en priorité
Aucun serveur de base de données, aucun adaptateur de framework, aucun paquet d’exécution Composer ; il suffit de fournir un répertoire.
Lectures JSONC
Les fichiers d’enregistrement peuvent contenir des commentaires et des virgules finales. storh écrit du JSON compact sur une seule ligne ; les commentaires restent donc présents jusqu’à la prochaine écriture.
Constructeur de requêtes
Requêtes chaînables telles que where('status')->in([...]) sur DocStore et SegmentedLog ; DocStore utilise les index secondaires disponibles.
Index secondaires
Index d’égalité, uniques et de plage stockés dans des fichiers à côté de la collection, dérivés des enregistrements et reconstructibles à tout moment.
Schémas
Les schémas de collection facultatifs valident chaque écriture et déclarent les champs à indexer.
Miroir SQL
Envoyez les collections vers SQLite ou MySQL pour les jointures, la recherche et les rapports, tandis que les fichiers restent la source canonique. Nécessite ext-pdo ou ext-mysqli uniquement en cas d’utilisation.
Pourquoi Storh
Un répertoire, pas un serveur
Vous fournissez un répertoire de base et storh y écrit. Il nécessite PHP 8.2 et ext-json, sans paquet d’exécution Composer ni adaptateur de framework.
Une forme d’enregistrement, trois dispositions
Les documents, les journaux et les files d’attente stockent tous un identifiant UUIDv7 accompagné d’un tableau de données. Choisissez la disposition sur disque adaptée à la charge de travail et conservez les trois moteurs sous une même racine.
Des vérifications à exécuter
verify() compare les index et l’état du journal à un parcours complet des fichiers. repair() met en quarantaine les enregistrements corrompus et tronque les fins de journal partiellement écrites, tandis que CLI affiche JSON pour les tâches cron et les contrôles d’état.
Des benchmarks avec leur configuration
README publie les médianes pour 50,000 enregistrements par moteur sur un Apple M1 Pro avec PHP 8.5, par exemple 5.6 µs par lecture ponctuelle. Chaque écriture de ces essais est synchronisée sur disque avec fsync ; les débits d’écriture dépendent donc du système de fichiers.
Comparaison avec
storh privilégie le stockage sur fichiers plutôt qu’un moteur de requêtes relationnel ; ces outils remplissent donc certaines fonctions qu’il ne propose pas.
| Outil | Idéal pour | Différence avec Storh |
|---|---|---|
| SQLite | Idéal pourUn moteur SQL autonome dans un seul fichier, avec des transactions ACID sérialisables qui résistent à un plantage du programme ou à une coupure de courant. Sa documentation le présente comme une solution de remplacement aux fichiers de données créés au cas par cas. | Différence avec Storhstorh conserve un fichier JSON par enregistrement et son cœur ne nécessite aucun pilote PDO. Il ne prend pas en charge les transactions multi-enregistrements ; sa documentation recommande une véritable base de données pour ce travail. Il peut envoyer les collections vers SQLite pour les jointures et les rapports, tandis que les fichiers restent la source canonique. |
| MySQL | Idéal pourUne base de données relationnelle hébergée par un serveur SQL multithread, avec des programmes et bibliothèques clients, des outils d’administration et un large éventail d’API. | Différence avec Storhstorh s’exécute dans le processus PHP sur un répertoire ; aucun serveur n’est donc à gérer. Pour les jointures, la recherche et les rapports, il met les collections en miroir dans MySQL, y compris au moyen d’un handle mysqli existant, tandis que les fichiers restent la source de vérité. |
| SleekDB | Idéal pourUne base de données documentaire à fichiers plats en PHP, sans dépendance tierce, qui stocke des données sans schéma dans de simples fichiers JSON. Son langage de requête API comprend les jointures, la recherche textuelle, GROUP BY et HAVING, ainsi que le filtrage des propriétés imbriquées. | Différence avec Storhstorh associe son magasin de documents à un journal segmenté en ajout seul et à une file d’attente adossée à un journal, sous une même racine, et synchronise chaque écriture sur disque avant de confirmer son enregistrement. Les jointures ne font pas partie de storh ; les collections sont envoyées vers SQLite ou MySQL pour les effectuer. |
Questions et réponses
Réponses courtes issues de la documentation de Storh.
Oui. DocStore sérialise les modifications au moyen d’un verrou d’écriture au niveau de la collection ; en cas d’écritures simultanées sur le même identifiant, le dernier renommage l’emporte. Les prises en charge dans la file d’attente conservent le verrou de celle-ci : deux processus de traitement ne peuvent donc pas prendre en charge la même tâche en attente.
Oui. Les lectures acceptent les commentaires et les virgules finales, mais la prochaine put() pour cet identifiant réécrit le fichier au format JSON compact et les supprime. En mode cache TRUST, les modifications des fichiers effectuées hors de storh ne sont pas détectées.
Les lectures ponctuelles par identifiant restent rapides tant que le système de fichiers peut gérer le nombre de fichiers. Les parcours de champs sans index dans DocStore ralentissent au-delà d’environ 10k enregistrements ; déclarez donc des index pour les champs filtrés. Les parcours et lectures par plage du journal segmenté restent efficaces jusqu’à environ 1M d’enregistrements.
Les écritures sont synchronisées sur disque avec fsync et publiées par renommage atomique ; à la réouverture d’un journal ou d’une file d’attente, les fins partiellement écrites sont tronquées jusqu’au dernier événement validé. La durabilité en cas de coupure de courant dépend toujours du respect de ces synchronisations par le système de fichiers et les options de montage.
Non. Envoyez les collections vers SQLite ou MySQL avec SqlMirror pour les jointures, la recherche et les rapports. Les écritures atomiques portant sur plusieurs enregistrements restent du ressort d’une véritable base de données faisant office de système de référence.
Non. Queue n’est pas un magasin d’enregistrements généraliste ; la charge utile d’une tâche est supprimée lorsque celle-ci se termine.
Commencez par la prise en main rapide, puis consultez Choisir un moteur pour adapter DocStore, SegmentedLog ou Queue à votre charge de travail.