Задать вопрос

Каким алгоритмом можно парсить json на php (критично низкое потребление памяти)?

Сразу уточню: у задачи есть множество других решений - увеличение лимита памяти для скрипта, использование MongoDB, исползование другого синтаксиса записей и так далее. По различным причинам эти варианты не подходят. Кроме того, тут присутствует спортивный интерес.
Итак: в одном проекте логи храфнтся в виде json записей в файлах. Такое решение было принято для упрощения сохранения и извлечения информационных данных, которые могут быть и массивами и объектами.
Естественно возникла проблема с использованием json_decode: даже небольшой лог файл (10mb) занимает больше 128mb памяти (в пике) для расшифровки. Весь файл необходимо читать целиком, т.к. в инструменте анализа преистствуют функции сортиировки и фильтрации записей.
Что представляет из себя лог файл:
{
	"timestamp":"2014-03-04T13:16:13+01:00",
	"message":"start exec test",
	"priority":1,
	"priorityName":"ALERT"
},{
	"timestamp":"2014-03-04T13:16:13+01:00",
	"message":"got logname",
	"priority":2,
	"priorityName":"CRIT",
	"info":"cronLogTest"
},{
	"timestamp":"2014-03-04T13:16:14+01:00",
	"message":"Some additional info",
	"priority":7,
	"priorityName":"DEBUG",
	"info":[
		{
			"Type":"rec",
			"Name":"name",
			"Description":"desc",
			"Lang":"EN"
		},{
			"Type":"rec",
			"Name":"name2",
			"Description":"desc2",
			"Lang":"DE"
		}
	]
},{
	"timestamp":"2014-03-04T13:16:15+01:00",
	"message":"stop exec test",
	"priority":1,
	"priorityName":"ALERT"
},


Отсутствие контейнера гарантирует целостность файла во время крэша, контейнер добавляется непосредствено перед парсингом.
Иатк, первое, что приходит в голову - парсить только верхние элементы: они все обладают одинаковыми полями и их парсинг займёт меньше памяти чем рекурсивный обход всей структуры с помощью json_decode (или другого парсера, которые были протестированы и не показали большую эффективность) и этих полей будет достаточно для сортировки и фильтрации. Вложенные записи могут быть декодированны уже непосредственно перед отдачей информации на фронтенд (используется пагинатор, так-что нет необходимости декодировать сразу всю информацию).

Итак, 2 вопроса:
1. Как бы вы подошли к вопросу ручного парсинга подобной структуры? У меня есть идеи алгоритмов, но меня интересует взгляд со стороны. Естественно критичным ресурсом является потребление памяти (скорость на втором плане).
2. Считаете ли вы такой вариант приемлимым? Изменение формата или хранилища логов не годится из-за необходимости чтения большого количества уже созданных логов на лайве - может есть другой вариант, который я не заметил?
  • Вопрос задан
  • 4290 просмотров
Подписаться 4 Оценить 3 комментария
Пригласить эксперта
Ответы на вопрос 3
rumkin
@rumkin
Если решить задачу не меняя условий, тогда нужно читать файл по кускам, резать куски по '},{' и пытаться распарсить до тех пор пока не обнаружится первый наименьший валидный блок, остаток добавлять к следующей итерации. Функция json_decode не выбрасывает никаких исключений или сообщений об ошибках, так что в него можно смело передавать невалидные данные. Это самый простой и эффективный способ решить задачу, без сторонних решений.

Если немного изменить (в том случае, когда нет гарантии, что файл будет отформатирован, как в примере), то между объектами лога (или достаточно большими блоками) я бы вставлял разделитель, например так:
},"--delimiter--",{ Затем считывал бы файл по кускам, разбивал по разделителю и парсил родным json_decode. Разделитель нужно сделать более универсальным, но это уже другой вопрос. Это будет наиболее близким к стандартам решением.

Вообще такой вариант хранения логов объединяет в себе все недостатки используемых технологий, в т.ч. и самого php. Так что советую в дальнейшем избегать таких решений — мало кто из коллег его оценит и захочет поддерживать.
Ответ написан
@portfelio
Исходя из ваших данных я бы взял С/С++ библиотеку и сделал бы к ней интерфейс из/в РНР.

Какие парсеры вы уже попробовали?
Ответ написан
xytop
@xytop
PHP/RoR web dev & tech lead
Вот есть готовая либа: https://github.com/janeklb/JSONCharInputReader

Парсит по мере поступления и шлет калбеки
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы