PHP for 循环中允许的内存大小耗尽
Allowed memory size exhausted in PHP for loop
我在尝试操作 PHP 中的大量数组时遇到致命错误,结果 return 作为 HTTP 的响应 POST 请求:
Allowed memory size of 536870912 bytes exhausted
我已经尝试设置 ini_set('memory_limit', '-1');
以查看是否得到结果,但我没有得到任何类型的响应。 Postman 在我尝试发出 POST 请求时一直崩溃。
数组的起始结构就是这个。主体大小约为 25mb。主数组包含大约 22k 个具有这种结构的数组,我刚刚包含了 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;
在我看来,没有可以证明内存问题合理的无限循环或不良做法。唯一真正的问题可能是需要操作的数组的大小。
有什么建议吗?
在这种情况下处理大数据是一种不好的做法。
想象一下:你有包含 22k 个数组的变量 $a,你开始形成第二个变量 $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();
您正在将大量数据收集到数组中,然后才将其返回。
如果您相反,收集单个“$final”项目,并且 yield
它在 foreach 循环中,而不是将其放入一个不断增加的变量中,您仍然可以 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";
}
Information on 'yield' in PHP
这个答案是一个例子,说明如何在你的代码中实现一个缓冲区(内存中的一个有限数组),当它被填满时,将它的内容刷新到磁盘,最后你会发现磁盘上有一个巨大的数组JSON 格式。我在与你的情况类似的情况下使用这种方式并在 "memory usage" 方面取得了很好的结果,但正如我在评论中告诉你的那样,你需要重新考虑为什么你首先需要那个巨大的数组,以及是否有办法避开它,顺其自然。
使用此函数将为您节省 $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;
}
我在尝试操作 PHP 中的大量数组时遇到致命错误,结果 return 作为 HTTP 的响应 POST 请求:
Allowed memory size of 536870912 bytes exhausted
我已经尝试设置 ini_set('memory_limit', '-1');
以查看是否得到结果,但我没有得到任何类型的响应。 Postman 在我尝试发出 POST 请求时一直崩溃。
数组的起始结构就是这个。主体大小约为 25mb。主数组包含大约 22k 个具有这种结构的数组,我刚刚包含了 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;
在我看来,没有可以证明内存问题合理的无限循环或不良做法。唯一真正的问题可能是需要操作的数组的大小。
有什么建议吗?
在这种情况下处理大数据是一种不好的做法。
想象一下:你有包含 22k 个数组的变量 $a,你开始形成第二个变量 $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();
您正在将大量数据收集到数组中,然后才将其返回。
如果您相反,收集单个“$final”项目,并且 yield
它在 foreach 循环中,而不是将其放入一个不断增加的变量中,您仍然可以 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";
}
Information on 'yield' in PHP
这个答案是一个例子,说明如何在你的代码中实现一个缓冲区(内存中的一个有限数组),当它被填满时,将它的内容刷新到磁盘,最后你会发现磁盘上有一个巨大的数组JSON 格式。我在与你的情况类似的情况下使用这种方式并在 "memory usage" 方面取得了很好的结果,但正如我在评论中告诉你的那样,你需要重新考虑为什么你首先需要那个巨大的数组,以及是否有办法避开它,顺其自然。
使用此函数将为您节省 $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;
}