Как сериализовать большие объекты / массивы в JSON
Мое приложение должно производить JSON объекта, который имеет большой data
свойство типа массив. Массив должен оставаться в памяти, поскольку он собирает выходные данные БД, и некоторые свойства могут быть определены только после того, как массив будет завершен.
Осложнение: массив основан на числах и должен отображаться как таковой в выводе json, поэтому должен быть прямым json_encode()
это не вариант.
Чтобы сделать это возможным на машинах с низкой спецификацией, таких как RasPi, я рассмотрел вопрос об уменьшении потребления памяти:
- использование
SPLFixedArray
- использование
string
а такжеpack()
Оба подхода решают проблему с памятью хранения массивов, но терпят неудачу, когда дело доходит до кодирования в JSON.
Я смотрел на реализацию JsonSerializable
но так как это заставляет пользователей возвращать результат, который затем закодирован в Json, я вернулся к
public function jsonSerialize() {
return $this->toArray();
}
у которого те же проблемы с памятью.
zendframework/Component_ZendJson
выглядит многообещающе, так как ищет объекты, имеющие toJson()
способ обеспечить свою собственную кодировку как string
вместо object
,
Мне интересно, есть ли лучшие варианты, которые не дают проблем с памятью?
2 ответа
В своем исследовании я рассмотрел 5 различных подходов для хранения больших массивов кортежей в памяти, которые обобщены здесь с их результатами (с выборкой по 50 тыс. Записей):
наивный
Экспортировать json просто с помощью json_encode, используя
array(array(), array())
Память: 18,5 МБ (огромная)
Время: ~100 мс для сборки и выгрузки массива (Windows PC)Библиотека SPL
Этот подход хранит все во вложенных
SPLFixedArrays
:SPLFixedArray[SPLFixedArray]
, Экспорт в JSON завершенZend\Json\Encoder
путем реализацииtoJson
метод.Память: 15,5 МБ (по- прежнему большой)
Время: ~ 1,3 с, х10 медленнееБиблиотека SPL
Похоже на 2, но вместо внутреннего
SPLFixedArray
использует упакованные строки из PHPpack()
функция.Память: 3,5 МБ (в 5 раз меньше)
Время: ~ 1,3 с, х10 медленнее - по-видимомуpack()
так же медленно, как вложенный массив.Библиотека SPL
Похоже на 2, но вместо внутреннего
SPLFixedArray
фактические кортежи просто записываются в виде последовательных значений в корневой массив.Память: 3,25 МБ (опять же меньше)
Время: ~0,7 с, только в 6 раз медленнее - у нас здесь есть победитель?pack()
Похоже на 3, но вместо рута
SPLFixedArray
упаковать все в одну строку, используя PHPpack()
функция. Очевидно, что для этого нужны знания и фиксированная идентичная структура отдельных массивов.Память: 1,25 МБ (очень маленькая - только 1/12 от первоначальной памяти)
Время: ~ 1,7 с, х16 медленнее
ЗАКЛЮЧЕНИЕ
Хотя (5) предлагает лучшее использование памяти, он также чрезвычайно медленный. Для моих целей я остановился на (4), который составляет около 20% от первоначального потребления памяти, но - с учетом кодирования JSON - также в 5-6 раз медленнее. Приемлемый компромисс.
По данным json.org:
JSON построен на двух структурах:
- Коллекция пар имя / значение. На разных языках это реализовано как объект, запись, структура, словарь, хеш-таблица, список ключей или ассоциативный массив.
- Упорядоченный список значений. В большинстве языков это реализовано как массив, вектор, список или последовательность.
Я не знаю, есть ли проблемы с памятью с этим, но рассмотрим следующий код:
<?php
// No declared index causes no index in JSON
$arr = array('dssdf','38904uj');
echo json_encode($arr).'<br><br>';
// ["dssdf","38904uj"]
// start array at 0 removes the index from JSON
$arr = array('0'=>'dssdf','1'=>'38904uj');
echo json_encode($arr).'<br><br>';
// ["dssdf","38904uj"]
// start array at 1 forces the index to show in JSON
$arr = array('1'=>'dssdf','2'=>'38904uj');
echo json_encode($arr).'<br><br>';
// {"1":"dssdf","2":"38904uj"}
// skip an index forces the index to show in JSON
$arr = array('0'=>'dssdf','1'=>'38904uj','3'=>'321as5d4');
echo json_encode($arr).'<br><br>';
// {"0":"dssdf","1":"38904uj","3":"321as5d4"}
// JSON_FORCE_OBJECT option forces indexes
$arr = array('0'=>'dssdf','1'=>'38904uj');
echo json_encode($arr, JSON_FORCE_OBJECT).'<br><br>';
// {"0":"dssdf","1":"38904uj"}
?>