Albireo CMS и много-много страниц

16-03-2025Время чтения ~ 7 мин.Albireo Framework / CMS 127

Последние дни я занят серьёзной переделкой системы для того, чтобы Albireo CMS могла работать с большим количеством страниц. Изменения настолько существенны, что я даже сделал отдельную ветку разработки, чтобы проверить новые алгоритмы.

Суть проблемы

Albireo CMS — это система, которая работает на файлах. Файл страницы — это обычный php-файл, внутри которого есть служебная информация. Именно в ней хранятся данные: заголовок, адрес, анонсы и т.п.

Система считывает служебную информацию и формирует один большой php-массив (назовём его pagesInfo). Когда нужно сделать выборку, например найти записи одной рубрики, то фактически происходит обращение к этому массиву и поиск внутри него нужных полей и имён файлов.

Очевидно, что нет смысла «дёргать» файлы на сервере для каждого посетителя, поэтому итоговый массив просто хранится в кэше.

Изначально кэш у меня был файловый, но в PHP с файлами работать не так просто: может случиться ситуация, когда формированием файла кэша будут заниматься два параллельных процесса, что в итоге приводит к повреждению файла. Вероятность этого неприятного события прямо пропорциональна размеру итогового файла. Для мелких файлов сбоев практически никогда не бывает, но если размер, скажем 100Кб, то сбои могут случаться постоянно.

Именно поэтому я сделал кэш на SQLite, поскольку это гарантированные транзакции. За всё время я ни разу не встретил проблем с этим хранилищем.

Что представляет собой кэш? По сути это просто серилизованный массив, который хранится в текстовом виде. Когда он нужен, просто считывается строка и потом десерилизуется до полноценного php-массива. Просто и сердито.

Плюс такого подхода в том, что можно физически удалить файл кэша (базу SQLite) и он построится заново автоматом.

Всё вроде бы хорошо, но есть нюанс. Размер кэша растёт пропорционально массиву pagesInfo. Вопрос не только в физическом размере базы, а в том, что выборки приходится делать из такого большого массива.

Изначально я не придавал этому значение, поскольку у меня небольшие размеры сайтов. Но со временем я заметил, что если число страниц/файлов больше 500, то это уже заметное увеличение потребляемой php-памяти.

Когда размер стал превышать 6-8Мб, я придумал алгоритм, когда данные во внутреннем хранилище могут храниться в сжатом виде (используется gzdeflate). Таким образом память упала до 1-2Мб (то есть примерно в 5 раз), правда за счёт немного увеличенного времени выполнения PHP (где-то + 0,1 .. 0,2 сек).

Сейчас я рассматриваю вариант о портировании MaxSite CMS в сторону Albireo CMS, и у меня возник вопрос о том, насколько flat-file система сможет работать с большим количеством файлов? Ведь система на базе данных не имеет проблем с тем, чтобы обсчитывать файлы данных — они все уже находятся в памяти сервера базы.

То есть любая Flat-File CMS будет иметь «узкое горлышко» именно в том, чтобы обсчитывать множество файлов.

Например если есть сайт на MaxSite CMS, где 5 тысяч записей, то это будет эквивалентно 5 тысячам файлов в Albireo CMS. Очевидно, что MaxSite CMS всегда будет выигрывать по скорости, даже если там будет 100 тыс. записей, просто потому что все данные в базе данных. Но, такие сайты — всё-таки исключение. Большинство сайтов вряд ли имеют более 1000 страниц, что для Albireo CMS не проблема.

Однако, всё равно есть некий предел, выше которого flat-file система не «прыгнет». Ну хотя бы из-за ограничений на количество файлов на сервере. К тому же большое количество страниц приводит к сложностям с их управлением.

Таким образом я задумался о том, как бы выразить всё это дело в цифрах.

Результаты тестирования

Сейчас я покажу результаты переделок Albireo CMS. Условно я разделяю версии на Albireo 1 (актуальная) и Albireo 2 (новая с переделками).

О самой переделке я расскажу ниже, но разница между версиями в том, что первая — это «чистые» файлы + серилизация массива pagesInfo, а Albireo 2 — это файлы + база SQLite.

Тестирование я проводил на своём ноутбуке (SSD, процессор 10 поколения), то есть это плюс/минус то, что может встречаться на реальном хостинге.

Для тестирования я сгенерировал несколько тысяч файлов страниц, где используются разные поля, даты, заголовки и т.п. Это фиктивные данные, но они близки к реальным. Файлы были размером 40-50Kб, то есть это достаточно «жирные» файлы.

Статистику я снимал несколько раз, поэтому привожу средние значения. Количество файлов/записей я привожу только как демо-данные, на самом деле было ещё примерно 420 файлов. Так что в реальности нужно их приплюсовать.

Небольшое дополнение. Ради интереса я посмотрел статистику «распиаренной» Grav CMS. Это тоже flat-file система. Я использовал последний дистрибутив, где сделал всего 5 страниц. В итоге время генерации примерно 0.34 секунды, а память «гуляет» от 16 до 18Мб. Это так, для сравнения.

18 тысяч файлов (суммарно 850 Мбайт)

Это совершенно экстремальное тестирование, потому что я даже не представляю себе сайтов с таким количеством записей. PHP не смог справится с таким объемом (речь о Albireo 1), поэтому мне пришлось увеличить максимальный размер php-памяти с 256Мб до 2Гб. В реальности же хватило бы и 280Мб.

Albireo 1

 Albireo 1Albireo 2
Размер кэша:63Mb13Mb
Построение кэша:14.1s/70.49Mb8.0s/10.66Mb
Построение кэша (gzdeflate):16.1s/16.49Mb-
Главная:9.5s/62.28Mb2.3s/2.19Mb
Главная (gzdeflate):11.2s/8.28Mb-

Здесь заметно, что в Albireo 1 сжатие достаточно эффективно — уменьшение с 70Мб до 16Мб. Но на это тратится дополнительные 2 секунды. Ну и очевидно, что даже с кэшем системе требуется почти 10 секунд на генерацию страницы. Здесь мы как раз попали в «узкое горлышко» работы с файлами — здесь просто нет возможности их быстрее обработать.

Но в Albireo 2 ситуация меняется. Здесь сервер намного быстрее, поскольку в основном идёт работа с базой SQLite.

10 тысяч файлов (суммарно 471 Мбайт)

Здесь и дальше я отключил gzdeflate-сжатие для Albireo 1 (в версии Albireo 2 его вообще нет).

 Albireo 1Albireo 2
Размер кэша:35Mb7Mb
Построение кэша:5.8s/40.52Mb4.2s/6.75Mb
Главная:3.3s/36.07Mb0.95s/2.04Mb

Лично я считаю, что время генерации менее 1 секунды — это относительно приемлемое время для современного сайта. Обратите внимание, что время генерации кэша в разных версиях достаточно близки (5.8s и 4.2s). Это опять указывает на то, что основное влияние — это количество файлов: чем их меньше, тем меньше разница в построении кэша.

5000 файлов (суммарно 236 Мбайт)

 Albireo 1Albireo 2
Размер кэша:18Mb4Mb
Построение кэша:2.7s/22.12Mb2.1s/4.39Mb
Главная:1.4s/19.94Mb0.42s/1.95Mb

В принципе эти данные показывают, что для актуальной версии Albireo CMS предел примерно 5000 файлов. Потому что при таком количестве 1.4s — неплохой результат. Это сравнимо с другими CMS. Например для WordPress это недостижимый результат (разве что ставить кэширование).

1000 файлов (суммарно 47 Мбайт)

 Albireo 1Albireo 2
Размер кэша:4Mb1Mb
Построение кэша:0.59s/6.29Mb0.66s/2.59Mb
Главная:0.32s/5.84Mb0.14s/1.87Mb

Здесь мы уже видим, что для PHP 1000 файлов вообще не проблема. И уже видно, что в Albireo 2 построение кэша чуть дольше — это из-за того, что работа с SQLite требует накладных расходов (в частности много INSERT).

420 файлов (суммарно 5Мбайт)

 Albireo 1Albireo 2
Размер кэша:2Mb0,5Mb
Построение кэша:0.26s/4.07Mb0.33s/2.18Mb
Главная:0.13s/3.78Mb0.085s/1.85Mb

Смысл тестирования

Заметьте, что для сайтов до 1000 страниц мы не видим принципиальной разницы в скорости работы, да и php-память потребляется в скромных пределах.

Лишь когда речь идёт о большем количестве, разница становится уже заметной. То есть можно «пилить» систему долго и нудно, но потом может оказаться, что её алгоритм в недостаточной степени учитывает большое количество файлов/страниц.

Суть переделок Albireo CMS

Основная идея в том, чтобы переделать кэширование в «умное». То есть вместо того, чтобы просто хранить серилизованный архив, мы используем базу SQLite по прямому назначению.

Когда строится кэш данные попадают в особую таблицу, которая хранит основные поля страниц. Например имя файла, заголовок, метку черновика, дату и т.п. Эта таблица служит основным источником получения данных.

Если раньше всё приходилось делать через php-массив, то сейчас можно использовать самые обычные SQL-запросы. Поскольку SQLite работает очень быстро, то в некоторых случаях я отмечал примерно 5-кратное ускорение.

Также это очень хорошо сказалось на потребляемой памяти и это не удивительно — теперь нет надобности хранить данные всех файлов в памяти.

Сейчас я ещё в процессе переделок. После этого будет большой этап тестирования и потом я уже начну проверять Albireo CMS на реальных хостингах.

Обновление сайтов на реальном хостинге показало примерно тоже время работы, что и раньше. Я думаю это из-за того, что у меня самый дешёвый тариф, где работа с процессором не самая быстрая (он в 2 раза слабее, чем соседний тариф). Но время стабильное (0.16-0.19s) и не меняется при увеличении количества файлов. Что касается php-памяти, то оно упало почти в 10 раз — до 0.73Mb.
Похожие записи
Комментарии (2) RSS
1 Віктор 2025-03-17 23:24:09

Теоретичні пошуки можна лише вітати, але з кешами у мене був і свій досвід на інформаційному ресурсі, організованому у вигляді сховища документів різних форматів (zip, jpg, pdf, tiff, mp3). Тобто сторінка являла собою одночасно читалку і генерацію файла кешу у форматі json (в разі його відсутності або зміни).

Сторінка завжди читала інфу з кешу, в якому були записані метатеги документів. Кеш генерувався один раз при додаванні, або видаленні відповідного документа і містився він поряд з php-скриптом самої сторінки. При чому, спочатку кешем був звичайний текстовий файл, потім у форматі rss, а потім - json, по них навіть пошук можна було робить.

Зовнішньо виглядало так, що сторінка була відображенням тематичного розділу з описом кожного документа, архіву, або мініатюри растрового зображення. Такий фрагментований кеш виявився доволі живучим при загальній кількості близько 3 тисяч файлів з їх описами у 25 розділах.

Коли Альбірео був ще молодим, то він сподобався тим, що ідея побудови розподіленого кешу мета-інформації запросто лягла на його структру і лише трохи адаптувалася: до шаблону додався скрипт генерації в upload окремої папки з назвою сторінки, після чого, в папку закидувалися документи, з яких витягувались мета-дані та формувався кеш розділу і мініатюр при потребі. Віддача, само собою, була миттєва. Оновлення кешу при додаванні, або вилученні документа зводилася до простого видалення папки за кешами, після чого вони автоматом генерувалися наново. Підхід дилетантський, але автор не претендує на овації, а лише хотів поділитися розв'язанням локальної задачі на файлах, без БД будь-якого формату.


2 admin 2025-03-18 11:21:12 admin

Да, вот схема, когда рядом с контекстным файлом расположен meta/info-файл достаточно распространена. У меня в LPF похожая схема использовалась. А вот JSON довольно медленный, хотя и читабельный формат. Если это не критично, то можно спокойно заменить на обычную серилизацию — она быстрее работает.

Оставьте комментарий!