Maison  >  Article  >  développement back-end  >  Parlons de réflexions sur le débordement de mémoire PHP

Parlons de réflexions sur le débordement de mémoire PHP

青灯夜游
青灯夜游avant
2020-04-04 09:21:002692parcourir

Parlons de réflexions sur le débordement de mémoire PHP

Lorsque j'ai récemment exporté et importé des données à grande échelle, j'ai souvent rencontré le problème du débordement de mémoire PHP. Après avoir résolu le problème, j'ai résumé quelques expériences et les ai compilées dans des articles.

Points d'optimisation

  1. Optimisez les instructions SQL pour éviter les requêtes lentes, créez des index de manière raisonnable et interrogez les champs SQL spécifiés. L’optimisation ne sera pas abordée ici.

  2. Lorsque l'ensemble de résultats de la requête est un objet volumineux, il est converti en traitement de tableau. Il existe généralement des méthodes dans le framework pour convertir, telles que toArray() dans Laravel et asArray(). dans Yii2.

  3. Pour le découpage des données de grands tableaux, les fonctions PHP incluent array_chunk() et array_slice().

  4. Pour les grandes chaînes et objets, utilisez &.

  5. Les variables utilisées ne sont pas définies dans le temps.

  6. Le format de fichier exporté passe d'Excel à CSV

  7. ini_set('memory_limit',''), définissez la mémoire que le programme peut utiliser (non recommandé).

Pensée

Gestion de la mémoire

Comment gérer la mémoire de PHP ? Lors de l'apprentissage du langage C, les développeurs doivent gérer manuellement la mémoire. En PHP, le moteur Zend fournit un gestionnaire de mémoire spécial pour traiter les données liées aux requêtes. La demande de données associées ne nécessite que de répondre à une seule demande, et les données seront publiées au plus tard à la fin de la demande.

Parlons de réflexions sur le débordement de mémoire PHP

L'image ci-dessus est une capture d'écran de la description du site officiel

Prévenir les fuites de mémoire et libérer toute la mémoire le plus rapidement possible est une partie importante de gestion de la mémoire. Pour des raisons de sécurité, Zend Engine libérera toute la mémoire allouée par le verrou API mentionné ci-dessus.

Mécanisme de collecte des déchets

Une brève explication :

Avant PHP5.3, le comptage de références était utilisé pour la gestion. Les variables en PHP sont stockées dans le conteneur de variables de zval. Lorsque la variable est référencée, le nombre de références est +1. Lorsque le nombre de références de variable est 0, PHP détruira la variable dans la mémoire. Cependant, lorsque le décompte de références est référencé de manière cyclique, le décompte de références ne sera pas réduit à 0, provoquant une fuite de mémoire.

PHP a été optimisé depuis la version 5.3. Il n'entre pas dans le cycle de recyclage à chaque fois que le nombre de références diminue. La collecte des déchets ne démarre que lorsque le tampon racine est plein. Cela peut résoudre le problème des références circulaires et peut également restaurer. le total des fuites de mémoire reste inférieur à un seuil.

Code

Comme un débordement de mémoire est souvent rencontré lors de l'utilisation de phpexcel, voici un code pour générer un fichier csv :

<?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();
        }
    }
}

Appelez le code

$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);

Apprentissage recommandé : Tutoriel vidéo PHP

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer