当前位置: 首页 > 后端技术 > PHP

关于PHP内存溢出的思考

时间:2023-03-29 20:13:53 PHP

背景最近在做大规模的数据导出和数据导入时,经常遇到PHP内存溢出的问题。解决问题后,总结了一些经验,整理成文章记录。优化点优化SQL语句,避免慢查询,合理建立索引,查询指定字段。SQL优化这里就不展开了。当查询的结果集是大对象时,将其转换为数组。一般框架中都有方法,比如Laravel中的toArray(),Yii2中的asArray()。对于大型数组的数据切割处理,PHP函数包括array_chunk()和array_slice()。对于大字符串和对象,通过引用传递&。使用的变量及时取消设置。导出的文件格式由excel改为csvini_set('memory_limit','')来设置程序可以使用的内存(不推荐)。关于内存管理的思考PHP是如何管理内存的?在学习C语言时,开发者需要手动管理内存。在PHP中,Zend引擎提供了一个特殊的内存管理器来处理与请求相关的数据。请求相关数据只需要为单个请求服务,最迟在请求结束时释放数据。上图是官网描述的截图。防止内存泄漏,尽快释放所有内存是内存管理的重要内容。出于安全原因,ZendEngine将释放上述API锁分配的所有内存。垃圾回收机制简单描述一下:在PHP5.3之前,是通过引用计数来管理的。PHP中的变量存在于zval的变量容器中。当变量被引用时,引用计数+1。当变量引用计数为0时,PHP会销毁内存中的变量。但是,当引用计数循环引用时,引用计数不会减为0,从而导致内存泄漏。PHP5.3以后做了优化。并不是每次引用计数减少,就进入回收循环。垃圾收集只有在根缓冲区满后才开始,这可以解决循环引用问题,并将总内存泄漏保持在一个阈值。在下面。代码在使用phpexcel时经常会遇到内存溢出。下面是一段生成csv文件的代码:params['excelSavePath'];foreach(array_chunk($data,10000)as$key=>$value){self::$outPutFile='';$subject=!empty($fileName)?$文件名:'数据_';$subject.=date('YmdHis');如果(空($value)||空($formFields)){继续;}self::$outPutFile=$tmpPath。$主题。$键。'.csv';如果(!file_exists(self::$outPutFile)){touch(self::$outPutFile);}$index=array_keys($formFields);$小时eader=array_values($formFields);自我::输出($标头);foreach($valueas$k=>$v){$tmpData=[];foreach($indexas$item){$tmpData[]=isset($v[$item])?$v[$item]:'';}self::outPut($tmpData);}$fileArr[]=self::$outPutFile;}$zipFile=$tmpPath。$文件名。日期('YmdHi')。'。压缩';$zipRes=self::zipFile($fileArr,$zipFile);返回$zipRes;}/***向文件写入数据*@paramarray$data*/publicstaticfunctionoutPut($data=[]){if(is_array($data)&&!empty($data)){$data=implode(',',$数据);file_put_contents(self::$outPutFile,iconv("UTF-8","GB2312//IGNORE",$data).PHP_EOL,FILE_APPEND);}}/***压缩文件*@param$sourceFile*@param$distFile*@returnmixed*/publicstatic函数zipFile($sourceFile,$distFile){$zip=new\ZipArchive();如果($zip->open($distFile,\ZipArchive::CREATE)!==true){return$sourceFile;}$zip->open($distFile,\ZipArchive::CREATE);foreach($sourceFileas$file){$fileContent=file_get_contents($file);$file=iconv('utf-8','GBK',basename($file));$zip->addFromString($file,$fileContent);}$zip->close();返回$distFile;}/***下载文件*@param$filePath*@param$fileName*/publicstaticfunctiondownload($filePath,$fileName){if(!file_exists($filePath.$fileName)){header('HTTP/1.1404未找到');}else{//以只读和二进制模式打开文件$file=fopen($filePath.$fileName,"rb");//告诉浏览器这是一个文件流格式fileHeader("Content-type:application/octet-stream");//请求范围的度量单位Header("Accept-Ranges:bytes");//Content-Length指定请求或响应中包含的数据的字节长度Header("Accept-Length:".filesize($filePath.$fileName));//用来告诉浏览器该文件可以作为附件下载,下载的文件名为$file_name,这个变量Header("Content-Disposition:attachment;filename=".$fileName)的值;//读取文件内容,直接输出到浏览器echofread($file,filesize($filePath.$fileName));fclose($文件);出口();}}}调用时的代码$fileName="InventoryimportTemplate";$stockRes=[];//导出数据$formFields=['store_id'=>'storeID','storeName'=>'storename','sku'=>'SKUcode','name'=>'SKUname','stock'=>'stock','reason'=>'reason'];$fileRes=ExportService::exportData($fileName,$stockRes,$formFields);$tmpPath=\Yii::$app->params['excelSavePath'];//文件路径$fileName=str_replace($tmpPath,'',$fileRes);//下载文件ExportService::download($tmpPath,$fileName);原文地址:https://tsmliyun.github.io/20...欢迎交流。如有错误,请指出,谢谢。