Подтвердить что ты не робот

Сериализовать большой массив в PHP?

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

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

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

4b9b3361

Ответ 1

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

$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 секунда - и довольно много памяти используется ^^
(Нет, это не производственный сервер: у меня довольно высокий memory_limit на этой машине разработки ^^)


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

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

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

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


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

Ответ 2

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

Менее дорогостоящий способ кэширования этих данных через 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().

Ответ 6

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

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

$string = json_encode($your_array_here);

и декодировать его

$array = json_decode($your_array_here, true);

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

Ответ 7

Хорошо... больше номеров! (PHP 5.3.0 OSX, не кеш кода операции)

@Паскаль на моей машине для n = 1 при 10k-итераторах производит:

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) 

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

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

float(57.064643859863)
int(1066440)

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

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

float(126.62566018105)
int(2944144)

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

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

Ответ 8

Нет, нет предела, и это:

set_time_limit(0);
ini_set('memory_limit ', -1);

unserialize('s:2000000000:"a";');

почему вам следует иметь safe.mode = On или расширение, например, Suhosin, установленное, в противном случае оно будет потреблять всю память в вашей системе.

Ответ 9

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

Ответ 10

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

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

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

Ответ 11

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

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

Ответ 12

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

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

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

Ответ 13

У меня есть случай, в котором unserialize генерирует исключение на большом сериализованном объекте, размер: 65535 (магическое число: 16 бит полный бит = 65536)