Storh
Записи на основі файлів для PHP: документи JSONC, сегментовані журнали лише для дописування та черги на основі журналів з ідентифікаторами UUIDv7.
composer require storh/storhОстанній випуск v0.5.2 MIT PHP 8.2+
$pages = $docs
->query()
->where('kind')->eq('page')
->orderBy('title')
->limit(20)
->get();На практиці
Короткий посібник із двох частин: документ у кореневому каталозі сховища, а поруч із ним — запис журналу та завдання черги.
Один кореневий каталог, три рушії
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());$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());
}Сховище документів JSONC
Один файл JSONC на запис, ідентифікатори UUIDv7, шардування за двома символами з останньої групи UUID, атомарна заміна та потокові фільтри полів.
Сегментований журнал
Сегменти лише для дописування з рядками JSON, що мають префікси довжини й контрольної суми, розріджені індекси, читання за курсором і часовим діапазоном, відновлення після збою та ущільнення.
Черга журналу
Завдання переходять між станами очікування, обробки та завершення за допомогою компактних подій черги, що додаються до журналу.
Записи UUIDv7
Сортовані ідентифікатори з часовою адресацією для стабільних курсорів, обмеженого сканування та шляхів зберігання, зручних для шардування.
Атомарний запис
Запис у тимчасовий файл і атомарне перейменування забезпечують цілісність записів. Після аварійного завершення роботи записувача наступне відкриття видаляє його осиротілі тимчасові файли.
Відновлення після часткового запису
Під час читання сегментів перевіряються поля довжини й контрольної суми; відновлення обрізає неповні кінцеві рядки.
Спершу файли
Без сервера бази даних, адаптера фреймворку та пакетів середовища виконання Composer; викликач передає каталог.
Читання JSONC
Файли записів можуть містити коментарі й кінцеві коми. Під час запису storh створює компактний однорядковий JSON, тому коментарі зберігаються до наступного запису.
Конструктор запитів
Ланцюжки запитів на кшталт where('status')->in([...]) у DocStore та SegmentedLog; DocStore використовує вторинні індекси, якщо вони є.
Вторинні індекси
Індекси рівності, унікальності й діапазонів зберігаються у файлах поруч із колекцією, формуються на основі записів і можуть бути перебудовані будь-коли.
Схеми
Необов’язкові схеми колекцій перевіряють кожен запис і визначають, для яких полів створювати індекси.
Дзеркало SQL
Передавайте колекції в SQLite або MySQL для об’єднань, пошуку й звітності, залишаючи файли канонічним джерелом даних. Потрібен ext-pdo або ext-mysqli, лише якщо цю функцію використовують.
Чому Storh
Каталог замість сервера
Ви передаєте базовий каталог, і storh записує дані в нього. Потрібні PHP 8.2 та ext-json, без пакетів середовища виконання Composer і адаптера фреймворку.
Одна форма запису, три способи зберігання
Документи, журнали й черги зберігають ідентифікатор UUIDv7 і масив даних. Виберіть спосіб зберігання на диску для свого завдання та розмістіть усі три рушії в одному кореневому каталозі.
Перевірки, які можна виконати
verify() перевіряє індекси й стан журналу, скануючи всі файли. repair() поміщає пошкоджені записи в карантин і обрізає неповні кінцеві фрагменти журналу, а CLI виводить JSON для завдань cron та перевірок стану.
Бенчмарки з описом умов
README публікує медіанні показники для 50,000 записів на рушій на комп’ютері Apple M1 Pro з PHP 8.5, наприклад 5.6 µs для читання одного запису. Усі записи в цих запусках синхронізуються через fsync, тому швидкість запису залежить від файлової системи.
Порівняно з
storh — це файлове сховище, а не рушій реляційних запитів, тому ці інструменти виконують деякі завдання, які воно не охоплює.
| Інструмент | Переваги | Відмінність від Storh |
|---|---|---|
| SQLite | ПеревагиАвтономний рушій SQL в одному файлі із серіалізованими транзакціями ACID, що зберігають цілісність після аварійного завершення програми чи збою живлення. У документації його описано як заміну спеціально створеним файлам даних. | Відмінність від Storhstorh зберігає кожен запис в окремому файлі JSON, а для його ядра не потрібен драйвер PDO. Транзакцій між кількома записами немає; документація радить використовувати для цього повноцінну базу даних. Колекції можна передавати в SQLite для об’єднань і звітності, залишаючи файли канонічним джерелом даних. |
| MySQL | ПеревагиРеляційна база даних, яку обслуговує багатопотоковий сервер SQL, із клієнтськими програмами й бібліотеками, адміністративними інструментами та широким набором API. | Відмінність від Storhstorh працює в процесі PHP із каталогом, тому керувати сервером не потрібно. Для об’єднань, пошуку й звітності він копіює колекції в MySQL, зокрема через наявний дескриптор mysqli, а файли залишаються джерелом істини. |
| SleekDB | ПеревагиДокументна база даних на основі файлів у PHP без сторонніх залежностей, що зберігає дані без схеми у звичайних файлах JSON. Її мова запитів API підтримує об’єднання, текстовий пошук, group by і having, а також фільтри для вкладених властивостей. | Відмінність від Storhstorh поєднує сховище документів із сегментованим журналом лише для дописування та чергою на його основі в одному кореневому каталозі й виконує fsync для кожного запису перед підтвердженням його збереження. Об’єднання не входять до storh; для них колекції можна передати в SQLite або MySQL. |
Запитання та відповіді
Короткі відповіді з документації Storh.
Так. DocStore серіалізує зміни за допомогою блокування запису на рівні колекції; у разі одночасного запису того самого ідентифікатора перемагає останнє перейменування. Під час отримання завдань черга блокується, тому два обробники не отримають те саме завдання, що очікує на виконання.
Так. Під час читання допускаються коментарі й кінцеві коми, але наступний запис put() для цього ідентифікатора перезаписує файл у компактному форматі JSON і видаляє їх. У режимі кешу TRUST зміни файлів поза storh не виявляються.
Читання за ідентифікатором залишається швидким, доки файлова система може обробляти таку кількість файлів. Сканування полів DocStore без індексів сповільнюється приблизно після 10k записів, тож визначте індекси для полів, за якими фільтруєте. Сканування сегментованого журналу й читання діапазонів добре працюють приблизно до 1M записів.
Записи синхронізуються через fsync і публікуються атомарним перейменуванням. Після повторного відкриття журналу або черги неповні кінцеві фрагменти обрізаються до останньої зафіксованої події. Надійність збереження даних у разі втрати живлення все одно залежить від того, чи підтримують ці операції синхронізації файлова система й параметри монтування.
Ні. Передавайте колекції в SQLite або MySQL за допомогою SqlMirror для об’єднань, пошуку й звітності. Атомарний запис кількох записів залишається завданням повноцінної бази даних, яка є системою обліку.
Ні. Черга не призначена для зберігання довільних записів, а дані завдання видаляються після його завершення.
Почніть із короткого посібника, а потім перегляньте розділ «Вибір рушія», щоб підібрати DocStore, SegmentedLog або Queue для свого завдання.