Heim  >  Artikel  >  Backend-Entwicklung  >  Lassen Sie uns über Gedanken zum PHP-Speicherüberlauf sprechen

Lassen Sie uns über Gedanken zum PHP-Speicherüberlauf sprechen

青灯夜游
青灯夜游nach vorne
2020-04-04 09:21:002615Durchsuche

Lassen Sie uns über Gedanken zum PHP-Speicherüberlauf sprechen

Beim großen Datenexport und Datenimport stoße ich in letzter Zeit häufig auf das Problem eines PHP-Speicherüberlaufs. Nachdem ich das Problem gelöst habe, habe ich einige Erfahrungen zusammengefasst und in Artikeln zusammengefasst.

Optimierungspunkte

  1. Optimieren Sie SQL-Anweisungen, um langsame Abfragen zu vermeiden, Indizes sinnvoll zu erstellen und spezifizierte SQL-Felder abzufragen Optimierung wird hier nicht besprochen.

  2. Wenn die Abfrageergebnismenge ein großes Objekt ist, wird sie in die Array-Verarbeitung konvertiert. Im Framework gibt es im Allgemeinen Methoden zum Konvertieren, z. B. toArray() in Laravel und asArray(). in Yii2.

  3. Zum Datenschneiden großer Arrays umfassen die PHP-Funktionen array_chunk() und array_slice().

  4. Für große Zeichenfolgen und Objekte verwenden Sie &.

  5. Verwendete Variablen werden rechtzeitig deaktiviert.

  6. Das exportierte Dateiformat wird von Excel in CSV geändert

  7. ini_set('memory_limit',''), legt den Speicher fest, den das Programm verwendet verwenden kann (nicht empfohlen).

Denken

Speicherverwaltung

Wie verwalte ich den PHP-Speicher? Beim Erlernen der C-Sprache müssen Entwickler den Speicher manuell verwalten. In PHP stellt die Zend-Engine einen speziellen Speichermanager zur Verarbeitung anforderungsbezogener Daten zur Verfügung. Für die Anforderung relevanter Daten ist nur eine einzige Anfrage erforderlich und die Daten werden spätestens am Ende der Anfrage freigegeben.

Lassen Sie uns über Gedanken zum PHP-Speicherüberlauf sprechen

Das Bild oben ist ein Screenshot der Beschreibung von der offiziellen Website

Speicherlecks zu verhindern und den gesamten Speicher so schnell wie möglich freizugeben, ist ein wichtiger Teil von Speicherverwaltung. Aus Sicherheitsgründen gibt Zend Engine den gesamten durch die oben erwähnte API-Sperre zugewiesenen Speicher frei.

Garbage-Collection-Mechanismus

Eine kurze Erklärung:

Vor PHP5.3 wurde die Referenzzählung für die Verwaltung verwendet. Variablen in PHP werden im Variablencontainer von zval gespeichert. Wenn auf die Variable verwiesen wird, beträgt der Referenzzähler +1. Wenn der Variablenreferenzzähler 0 ist, zerstört PHP die Variable im Speicher. Wenn jedoch zyklisch auf den Referenzzähler verwiesen wird, wird der Referenzzähler nicht auf 0 reduziert, was zu einem Speicherverlust führt.

PHP wurde seit 5.3 optimiert. Es tritt nicht jedes Mal in den Recyclingzyklus ein, wenn der Root-Puffer voll ist, und kann auch wiederhergestellt werden Die Gesamtzahl der Speicherlecks bleibt unter einem Schwellenwert.

Code

Da bei der Verwendung von PHPExcel häufig ein Speicherüberlauf auftritt, finden Sie hier einen Code zum Generieren einer CSV-Datei:

<?php

namespace api\service;

class ExportService
{

    public static $outPutFile = &#39;&#39;;

    /**
     * 导出文件
     * @param string $fileName
     * @param $data
     * @param array $formFields
     * @return mixed
     */
    public static function exportData($fileName = &#39;&#39;, $data, $formFields = [])
    {
        $fileArr = [];
        $tmpPath = \Yii::$app->params[&#39;excelSavePath&#39;];

        foreach (array_chunk($data, 10000) as $key => $value) {
            self::$outPutFile = &#39;&#39;;
            $subject          = !empty($fileName) ? $fileName : &#39;data_&#39;;
            $subject          .= date(&#39;YmdHis&#39;);
            if (empty($value) || empty($formFields)) {
                continue;
            }

            self::$outPutFile = $tmpPath . $subject . $key . &#39;.csv&#39;;
            if (!file_exists(self::$outPutFile)) {
                touch(self::$outPutFile);
            }
            $index  = array_keys($formFields);
            $header = array_values($formFields);
            self::outPut($header);

            foreach ($value as $k => $v) {
                $tmpData = [];
                foreach ($index as $item) {
                    $tmpData[] = isset($v[$item]) ? $v[$item] : &#39;&#39;;
                }
                self::outPut($tmpData);
            }
            $fileArr[] = self::$outPutFile;
        }
        
        $zipFile = $tmpPath . $fileName . date(&#39;YmdHi&#39;) . &#39;.zip&#39;;
        $zipRes = self::zipFile($fileArr, $zipFile);
        return $zipRes;
    }

    /**
     * 向文件写入数据
     * @param array $data
     */
    public static function outPut($data = [])
    {
        if (is_array($data) && !empty($data)) {
            $data = implode(&#39;,&#39;, $data);
            file_put_contents(self::$outPutFile, iconv("UTF-8", "GB2312//IGNORE", $data) . PHP_EOL, FILE_APPEND);
        }
    }

    /**
     * 压缩文件
     * @param $sourceFile
     * @param $distFile
     * @return mixed
     */
    public static function zipFile($sourceFile, $distFile)
    {
        $zip = new \ZipArchive();
        if ($zip->open($distFile, \ZipArchive::CREATE) !== true) {
            return $sourceFile;
        }

        $zip->open($distFile, \ZipArchive::CREATE);
        foreach ($sourceFile as $file) {
            $fileContent = file_get_contents($file);
            $file        = iconv(&#39;utf-8&#39;, &#39;GBK&#39;, basename($file));
            $zip->addFromString($file, $fileContent);
        }
        $zip->close();
        return $distFile;
    }
    
        /**
     * 下载文件
     * @param $filePath
     * @param $fileName
     */
    public static function download($filePath, $fileName)
    {
        if (!file_exists($filePath . $fileName)) {
            header(&#39;HTTP/1.1 404 NOT FOUND&#39;);
        } else {
            //以只读和二进制模式打开文件
            $file = fopen($filePath . $fileName, "rb");

            //告诉浏览器这是一个文件流格式的文件
            Header("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);

            //读取文件内容并直接输出到浏览器
            echo fread($file, filesize($filePath . $fileName));
            fclose($file);
            exit();
        }
    }
}

Rufen Sie den Code auf

$fileName = "库存导入模板";
$stockRes = []; // 导出的数据
$formFields = [
    &#39;store_id&#39;  => &#39;门店ID&#39;,
    &#39;storeName&#39; => &#39;门店名称&#39;,
    &#39;sku&#39;       => &#39;SKU编码&#39;,
    &#39;name&#39;      => &#39;SKU名称&#39;,
    &#39;stock&#39;     => &#39;库存&#39;,
    &#39;reason&#39;    => &#39;原因&#39;
];
$fileRes    = ExportService::exportData($fileName, $stockRes, $formFields);
$tmpPath    = \Yii::$app->params[&#39;excelSavePath&#39;]; // 文件路径
$fileName   = str_replace($tmpPath, &#39;&#39;, $fileRes);

// 下载文件
ExportService::download($tmpPath, $fileName);

Empfohlenes Lernen: PHP-Video-Tutorial

Das obige ist der detaillierte Inhalt vonLassen Sie uns über Gedanken zum PHP-Speicherüberlauf sprechen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:github.io. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen