сериализовать большой массив в PHP? - PullRequest
13 голосов
/ 11 августа 2009

Мне любопытно, есть ли ограничение на размер сериализации в PHP. Можно ли сериализовать массив с 5000 ключами и значениями, чтобы он мог быть сохранен в кеш?

Я надеюсь кэшировать список друзей пользователей на сайте социальной сети, кэш нужно будет обновлять довольно часто, но его нужно будет читать почти при каждой загрузке страницы.

На одном сервере я предполагаю, что APC будет лучше, чем memcache для этого.

Ответы [ 13 ]

27 голосов
/ 11 августа 2009

Как уже ответили многие другие люди, просто для забавы, вот очень быстрый тест (смею ли я так это называть?) ; рассмотрим следующий код:

$num = 1;

$list = array_fill(0, 5000, str_repeat('1234567890', $num));

$before = microtime(true);
for ($i=0 ; $i<10000 ; $i++) {
    $str = serialize($list);
}
$after = microtime(true);

var_dump($after-$before);
var_dump(memory_get_peak_usage());

Я запускаю это на PHP 5.2.6 (в комплекте с Ubuntu jaunty).
И, да, есть только значения; нет ключей; и значения довольно просты: нет объекта, нет подмассива, нет ничего, кроме строки.

Для $num = 1 вы получаете:

float(11.8147978783)
int(1702688)

Для $num = 10 вы получаете:

float(13.1230671406)
int(2612104)

А для $num = 100 вы получите:

float(63.2925770283)
int(11621760)

Таким образом, кажется, что чем больше каждый элемент массива, тем больше времени занимает (на самом деле кажется справедливым) . Но для элементов, в 100 раз больших, вам не понадобится в 100 раз больше времени ...


Теперь с массивом 50000 элементов вместо 5000, что означает, что эта часть кода изменена:

$list = array_fill(0, 50000, str_repeat('1234567890', $num));

С $num = 1 вы получите:

float(158.236332178)
int(15750752)

Учитывая время, которое заняло 1, я не буду запускать его ни для $ num = 10, ни для $ num = 100 ...


Да, конечно, в реальной ситуации вы бы не делали это 10000 раз; так что давайте попробуем всего 10 итераций цикла for.

Для $num = 1:

float(0.206310987473)
int(15750752)

Для $num = 10:

float(0.272629022598)
int(24849832)

А для $num = 100:

float(0.895547151566)
int(114949792)

Да, это почти 1 секунда - и совсем немного памяти использовалось ^^
(Нет, это не рабочий сервер: у меня достаточно большой предел памяти на этой машине для разработки ^^)


Итак, в конце концов, чтобы быть немного короче, чем эти числа - и, да, у вас могут быть числа, которые говорят, что вы хотите их - Я бы не сказал, что есть «предел», как в «жестко запрограммированном» в PHP, но в итоге вы столкнетесь с одним из них:

  • max_execution_time (обычно на веб-сервере это никогда не превышает 30 секунд)
  • memory_limit (на веб-сервере обычно не более 32 МБ)
  • нагрузка, которую будет иметь ваш веб-сервер: во время работы одного из этих больших циклов сериализации потребовался 1 мой ЦП; если у вас на одной странице одновременно несколько пользователей, я позволю себе представить, что это даст; -)
  • терпение вашего пользователя ^^

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

Тем не менее, лучший способ узнать это проверить себя на реальных данных; -)


И вы также можете взглянуть на то, что может Xdebug сделать, когда дело доходит до профилирования : эта ситуация одна из тех, для которых она полезна!

7 голосов
/ 11 августа 2009

Функция serialize () ограничена только доступной памятью.

5 голосов
/ 11 августа 2009

PHP не устанавливает ограничений. Serialize возвращает представление байтового потока (строку) сериализованной структуры, поэтому вы просто получите большую строку.

4 голосов
/ 11 августа 2009

Единственным практическим ограничением является объем доступной памяти, поскольку сериализация включает создание строки в памяти.

3 голосов
/ 30 августа 2012

Как предложено Мыслителем выше:

Вы можете использовать

$string = json_encode($your_array_here);

и расшифровать его

$array = json_decode($your_array_here, true);

Возвращает массив. Это работает хорошо, даже если закодированный массив был многоуровневым.

3 голосов
/ 11 августа 2009

Нет ограничений, но помните, что сериализация и десериализация имеет свою стоимость.

Десериализация чрезвычайно дорога.

Менее дорогой способ кэширования данных - через var_export() как таковой (начиная с PHP 5.1.0, он работает с объектами):

$largeArray = array(1,2,3,'hello'=>'world',4);

file_put_contents('cache.php', "<?php\nreturn ".
                                var_export($largeArray, true).
                                ';');

Затем вы можете просто получить массив, выполнив следующее:

$largeArray = include('cache.php');

Ресурсы обычно не могут кешироваться.

К сожалению, если у вас есть циклические ссылки в вашем массиве, вам нужно будет использовать serialize().

2 голосов
/ 11 августа 2009

Хорошо ... больше чисел! (PHP 5.3.0 OSX, без кеша кода операции)

@ Код Паскаля на моей машине для n = 1 при 10 000 итеров выдает:

float(18.884856939316)
int(1075900)

Я добавляю unserialize () к вышеприведенному.

$num = 1;

$list = array_fill(0, 5000, str_repeat('1234567890', $num));

$before = microtime(true);
for ($i=0 ; $i<10000 ; $i++) {
    $str = serialize($list);
    $list = unserialize($str);
}
$after = microtime(true);

var_dump($after-$before);
var_dump(memory_get_peak_usage());

производит

float(50.204112052917)
int(1606768) 

Я предполагаю, что дополнительные 600 КБ или около того являются сериализованной строкой.

Мне было любопытно, что var_export и его включающий / eval партнер $str = var_export($list, true); вместо serialize () в оригинальном производит

float(57.064643859863)
int(1066440)

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

добавление eval('$list = '.$str.';'); вместо десериализации в приведенном выше производит

float(126.62566018105)
int(2944144)

Указывает на то, что, вероятно, где-то происходит утечка памяти при выполнении eval: - /.

Итак, опять же, это не очень хорошие тесты (я действительно должен изолировать eval / unserialize, помещая строку в локальный переменный или что-то в этом роде, но я ленивый), но они показывают соответствующие тенденции. var_export кажется медленным.

1 голос
/ 27 января 2014

Я только что натолкнулся на случай, когда мне показалось, что я достиг верхнего предела сериализации.

Я сохраняю сериализованные объекты в базе данных, используя поле mysql TEXT.

Предел доступных символов для однобайтовых символов составляет 65,535 , поэтому, хотя я могу сериализовать объекты намного большего размера, чем в PHP, невозможно десериализовать их, поскольку они усекаются по пределу TEXT поле.

1 голос
/ 11 августа 2009

Ваш вариант использования звучит так, как будто вам лучше использовать базу данных, а не полагаться исключительно на доступные ресурсы PHP. Преимущество использования чего-то вроде MySQL вместо этого состоит в том, что оно специально разработано с учетом управления памятью для таких вещей, как хранение и поиск.

Постоянно сериализировать и десериализовывать данные просто неинтересно, просто обновлять или изменять несколько фрагментов информации.

1 голос
/ 11 августа 2009

Если вы хотите кэшировать его (поэтому я предполагаю, что проблема в производительности), используйте вместо этого apc_add, чтобы избежать снижения производительности при преобразовании его в строку + кэш-память усиления в памяти.

Как указано выше, единственным ограничением размера является доступная память.

Несколько других ошибок: Данные serialize'd не переносимы между многобайтовыми и однобайтовыми кодировками символов. Классы PHP5 включают NUL-байты, которые могут вызвать хаос в коде, который их не ожидает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...