массивы — допустимый объем памяти, исчерпанный в PHP для цикла

Я сталкиваюсь с фатальной ошибкой, когда пытаюсь манипулировать огромным массивом массивов в PHP и возвращать результат как ответ HTTP POST запрос:

Разрешенный объем памяти 536870912 байт исчерпан

Я уже пытался установить ini_set('memory_limit', '-1'); чтобы увидеть, получу ли я результат, но я не получил никакого ответа. Почтальон падал все время, когда я пытался сделать запрос POST.

Это начальная структура массива. Размер тела составляет около 25 МБ. Основной массив содержит около 22 тыс. Массивов с этой структурой, я только что включил 2:

Array
(
[0] => Array
(
[id] => 14
[isActive] => 1
[personId] => 0023fff16d353d16a052a267811af53bc8bd42f51f2266a2904ca41db19dfd32_0
[gender] => m
[age] => 69
[linedata_0] => 2018-03-01 17:15:18, 155.59, 294.076; 2018-03-01 17:16:04, 502.968, 249.947; 2018-03-01 17:16:44, 276.837, 270.593; 2018-03-01 17:17:28, 431.68, 371.14; 2018-03-01 17:17:34, 851.622, 355.915
)

[1] => Array
(
[id] => 180
[isActive] => 1
[personId] => 02659982ae8286409cc5bb283089871b62f2bafbbad517941d64e77ecf2b62b1_0
[gender] => m
[age] => 69
[linedata_0] => 2018-03-01 13:20:05, 155.599, 293.841; 2018-03-01 13:20:48, 495.468, 249.582; 2018-03-01 13:21:28, 258.791, 260.748; 2018-03-01 13:23:20, 859.061, 352.237; 2018-03-01 13:23:32, 56.1404, 269.858
)
)

Здесь ниже часть php для манипулирования массивом, чтобы получить ожидаемый конечный результат, взорвав временную метку и координаты для каждого пользователя:

$final_result = [];

foreach($query_result as $row)
{
$line_datas =explode(";",$row["linedata_0"]);
$linedata = [];
$final = [];
$d = [];

for($s =0; $s < count($line_datas); $s++){
$line_data = explode(",",$line_datas[$s]);
$d["timestamp"] = utf8_encode($line_data[0]);
$d["x"]= utf8_encode($line_data[1]);
$d["y"] = utf8_encode($line_data[2]);

array_push($linedata,$d);
}

$final["id"]= $row["id"];
$final["isActive"]= $row["isActive"];
$final["personId"]= utf8_encode($row["personId"]);
$final["name"] = NULL;
$final["gender"] = utf8_encode($row["gender"]);
$final["age"] = utf8_encode($row["age"]);
$final["linedata"]=$linedata;

array_push($final_result, $final);
}

return $final_result;

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

Какие-либо предложения?

2

Решение

Этот ответ является примером того, как реализовать буфер (ограниченный массив в памяти) в вашем коде и, когда он заполнен, сбросить его содержимое на диск, в конце вы найдете огромный массив на диске в формате JSON. Я использовал этот способ в ситуации, аналогичной вашей, и получил отличный результат в отношении «использования памяти», но, как я уже говорил вам в комментариях, вам нужно переосмыслить, зачем вам нужен этот ОГРОМНЫЙ массив в первую очередь, и если есть способ избежать это, пойти с этим.

использование этой функции сэкономит вам память, используемую $final_result массив и заменить его строковым буфером $ final_result, но мы контролируем его использование памяти. Однако ваш $query_result массив по-прежнему будет занимать необходимую память.

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

/**
* proccess the HUGE array and save it to disk in json format [element,element]
*
* @param string $fileName absulote file name path you want to save the proccessed array in
* @return int processed elements count
*/
function buildMyArrayInFile($fileName)
{
$flushCheckPoint = 100;// set the buffer size as needed, depending on the size of PHP allowed memory and average element size
$processedElements = 0;
$final_result = "[";

file_put_contents($fileName, "");//prepare the file and erase anything in it

foreach($query_result as $row)
{
$line_datas =explode(";",$row["linedata_0"]);
$linedata = [];
$final = [];
$d = [];

for($s =0; $s < count($line_datas); $s++){
$line_data = explode(",",$line_datas[$s]);
$d["timestamp"] = utf8_encode($line_data[0]);
$d["x"]= utf8_encode($line_data[1]);
$d["y"] = utf8_encode($line_data[2]);

array_push($linedata,$d);
}

$final["id"]= $row["id"];
$final["isActive"]= $row["isActive"];
$final["personId"]= utf8_encode($row["personId"]);
$final["name"] = NULL;
$final["gender"] = utf8_encode($row["gender"]);
$final["age"] = utf8_encode($row["age"]);
$final["linedata"]=$linedata;


$final_result .= json_encode($final) . ",";
$processedElements ++;
if($processedElements % $flushCheckPoint === 0){
//the array has reached the limit, flush the array to disk
file_put_contents($fileName, $final_result, FILE_APPEND);
$final_result = "";
}

}

$final_result = rtrim($final_result, ",");//trim the last comma
$final_result .= "]";
//flush the remaning data in $final_result
file_put_contents($fileName, $final_result, FILE_APPEND);

return $processedElements;

}

это еще одна простая версия функции для тестирования

// test
var_dump(buildMyArrayInFile2("/home/myuser/myArray.json"));
// outputs int(7)



function buildMyArrayInFile2($fileName)
{
$flushCheckPoint = 2;// set the buffer size as needed, depending on the size of PHP allowed memory and average element size
$processedElements = 0;
$final_result = "[";

file_put_contents($fileName, "");//prepare the file and erase anything in it

$test_result = [1,2,3,4,"wee","hello\nworld",5];
foreach($test_result as $row)
{
$final_result .= json_encode($row) . ",";
$processedElements ++;
if($processedElements % $flushCheckPoint === 0){
//the array has reached the limit, flush the array to disk
file_put_contents($fileName, $final_result, FILE_APPEND);
$final_result = "";
}

}

$final_result = rtrim($final_result, ",");//trim the last comma
$final_result .= "]";
//flush the remaning data in $final_result
file_put_contents($fileName, $final_result, FILE_APPEND);

return $processedElements;
}
1

Другие решения

Вы собираете большое количество данных в массив и только потом возвращаете их.

Если вы вместо этого, соберите один предмет ‘$ final’, и yield он внутри цикла foreach, вместо того, чтобы помещать его в постоянно увеличивающуюся переменную размера, вы все равно сможете выполнять поиск по вызову функции.

Вот упрощенный пример, где $ i выступает в качестве образца возвращаемого значения вместо вашего массива $ final собранных данных.

<?php
function count_one_to_three() {
for ($i = 1; $i <= 3; $i++) {
// Note that $i is preserved between yields.
yield $i;
}
}

$generator = count_one_to_three();
foreach ($generator as $value) {  // you can also foreach(count_one_to_three() as $value)
echo "$value\n";
}

Информация о «доходности» в PHP

2

В этом случае плохо работать с большими данными.

Представьте себе эту вещь: у вас есть переменная $ a, которая содержит 22k массивов, и вы начали формировать вторую переменную $ b, которая также будет содержать 22k массивов.

Таким образом, в конце вашего скрипта у вас будет 2 переменные с массивами 22k.

Чтобы избежать этих проблем, вы должны получать свои данные партиями. Например, 500 строк в одном цикле.


    function findRows($offset = 0, &$final_result = []) {
$sql = 'SELECT * FROM my_table LIMIT ' . $offset . ', 500';
//your code to find rows

if ($query_result) {
$offset = $offset + 500;

foreach($query_result as $row) {
//your another code
array_push($final_result, $final);
}
findRows($offset, $final_result);
}

return $final_result;
}

return findRows();
2
По вопросам рекламы [email protected]