コンテンツへスキップ

Storh

PHP向けのファイルファーストなレコード保存。JSONCドキュメント、追記専用のセグメントログ、UUIDv7 IDを使うログベースのキュー。

ターミナル
composer require storh/storh

最新リリース v0.5.2 MIT PHP 8.2+

実践例

2部構成のクイックスタート:ストレージルートにドキュメントを保存し、その隣にログエントリとキュージョブを追加します。

1つのルートに3つのエンジン

StorageRoot::resolve()はベースディレクトリと名前空間を正規化してルートを作り、各エンジンは初回使用時にその下に専用のディレクトリを作成します。

すべてのエンジンはStorageRecordオブジェクトを返し、id()とdata()を備えています。キューは各ジョブの取得と完了をイベントログに追記します。

資料
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ドキュメントストア

レコードごとに1つのJSONCファイルを使用し、UUIDv7ID、UUIDの末尾グループから取った2文字によるシャーディング、アトミックな置換、ストリーミング形式のフィールドフィルターに対応します。

セグメントログ

長さとチェックサムを前置したJSON行を追記専用のセグメントに保存。疎なインデックス、カーソルおよび時間範囲読み取り、クラッシュ復旧、圧縮に対応。

ログキュー

コンパクトな追記専用のキューイベントを使って、ジョブを保留中、処理中、完了の各状態に移します。

UUIDv7レコード

安定したカーソル、範囲指定スキャン、シャーディングに適した保存パスを実現する、並べ替え可能で時刻を特定できるID。

アトミックな書き込み

一時ファイルへの書き込みとアトミックな名前変更で、レコード全体を保持します。書き手がクラッシュした後は、次に開いたときに残された一時ファイルを掃除します。

書き込み途中の破損から復旧

セグメントを読み取る際に長さとチェックサムの各フィールドを検証し、復旧時に末尾の不完全な行を切り詰めます。

ファイルファースト

データベースサーバーもフレームワーク用アダプターも、Composerの実行時パッケージも不要です。呼び出し元はディレクトリを渡します。

JSONCを読み取り

レコードファイルにはコメントや末尾のカンマを含められます。storhはコンパクトな1行のJSONを書き込むため、コメントは次の書き込みまで残ります。

クエリビルダー

DocStoreとSegmentedLogではwhere('status')->in([...])のように流れる形でクエリできます。DocStoreは利用可能な場合、セカンダリインデックスを使います。

セカンダリインデックス

等価、ユニーク、範囲インデックスをコレクションと並ぶファイルとして保存。レコードから生成され、いつでも再構築できます。

スキーマ

任意のコレクションスキーマで各書き込みを検証し、インデックスを付けるフィールドを宣言できます。

SQLミラー

コレクションをSQLiteまたはMySQLに取り込み、ファイルを正本として保ったまま結合、検索、レポートに活用できます。使用時のみext-pdoまたはext-mysqliが必要です。

Storhを選ぶ理由

サーバーではなくディレクトリ

ベースディレクトリを渡すと、storhはその場所に書き込みます。必要なのはPHP 8.2とext-jsonだけで、Composerの実行時パッケージもフレームワーク用アダプターも不要です。

1つのレコード形式、3つの保存方法

ドキュメント、ログ、キューはすべて、UUIDv7 IDとデータの配列を保存します。用途に合わせてディスク上の形式を選び、3つすべてを1つのルートにまとめられます。

実行できる検査

verify()はインデックスとログの状態を、ファイルの全件スキャンと照合します。repair()は破損したレコードを隔離し、不完全なログ末尾を切り詰めます。CLIはcronジョブとヘルスチェック用にJSONを出力します。

条件を明示したベンチマーク

READMEには、PHP 8.5を搭載したApple M1 Proで、各エンジンにつき50,000件のレコードを処理した際の中央値が掲載されています。たとえばポイント読み取りは1回あたり5.6 µsです。すべての書き込みでfsyncを実行するため、書き込み速度はファイルシステムによって異なります。

との比較

storhはリレーショナルクエリエンジンではなくファイルファーストのストレージです。そのため、ここに挙げるツールはstorhが対象としていない処理に対応しています。

他のツールとStorhの比較
SQLite 得意なこと単一ファイルで完結するSQLエンジン。プログラムのクラッシュや停電後も維持される、シリアライズ可能なACIDトランザクションを備えています。ドキュメントでは、場当たり的なデータファイルの代替として紹介されています。 Storhとの違いstorhはレコードごとにJSONファイルを1つ使い、コアにPDOドライバーは不要です。複数レコードのトランザクションには対応せず、ドキュメントではその用途に実データベースを案内しています。ファイルを正本として保ったまま、コレクションをSQLiteに取り込んで結合やレポートに利用できます。
MySQL 得意なことマルチスレッドのSQLサーバーが提供するリレーショナルデータベースです。クライアントプログラムやライブラリ、管理ツールに加え、幅広いAPIを備えています。 Storhとの違いstorhはPHPプロセス内でディレクトリを操作するため、サーバーを運用する必要はありません。結合、検索、レポートには、既存のmysqliハンドルを使ってコレクションをMySQLにミラーリングできます。ファイルが引き続き正本です。
SleekDB 得意なことPHPで動作するフラットファイル型のドキュメントデータベースです。サードパーティー依存がなく、スキーマ不要のデータをプレーンなJSONファイルに保存します。クエリAPIには結合、全文検索、group by、having、ネストされたプロパティのフィルターが含まれます。 Storhとの違いstorhはドキュメントストアに追記専用のセグメントログとログベースのキューを組み合わせ、1つのルートにまとめます。各書き込みは保存完了を報告する前にfsyncされます。storh自体は結合に対応せず、結合にはコレクションをSQLiteまたはMySQLに取り込みます。

質問と回答

Storhのドキュメントから、簡潔に回答します。

クイックスタートから始めて、「エンジンの選び方」を参考に、用途に合ったDocStore、SegmentedLog、Queueを選びましょう。