MacでMAMP v2.0を使用_ _ Apache/2.0.64(Unix) - PHP/5.3.5 - DAV/2 mod_ssl/2.0.64 - OpenSSL/0.9.7l - MySQL 5.5.9メモリリーク、PHPおよびMySQL Blobストリーミングファイルのデバッグ
私は実行しようとしているスクリプトを持っています。私はデバッグしようとしましたが、修正する方法を解決できない大きなメモリリークを与えているようです。
基本的に、スクリプトはファイルマネージャモジュールの一部です。 IDを指定すると、ファイルのダウンロードを処理します。
ファイル全体は、BLOBとして64KBのチャンク(レコードごと)でデータベーステーブルに格納され、要求に応じてクライアントにストリーム配信されます。
Database: file_management
Tables: file_details, file_data
file_details:
FileID - int(10) AUTO_INCREMENT
FileTypeID - int(10)
FileType - varchar(60)
FileName - varchar(255)
FileDescription - varchar(255)
FileSize - bigint(20)
FileUploadDate - datetime
FileUploadBy - int(5)
file_details:
FileDataID - int(10) AUTO_INCREMENT
FileID - int(10)
FileData - BLOB
私は実際に(PHPのエラーログから)この1で取得していますエラー:
[10月31日 - 2011年9時47分39秒] PHPの致命的なエラー:134217728バイトの許可メモリサイズを/root/htdocs/file_manager/file_manager_download.phpの行150で使い果たされた(63326173バイトを割り当てようと試みました)
ファイルが十分に小さい場合、実際のダウンロード機能は40MB未満ですが、しかし、それが上になると、上記のエラーの60mbファイルのように、失敗します。それは0kbのファイルをダウンロードするだけです。
明らかに、134217728バイトは63326173バイト(128MBと60MB)を超えています。 134217728バイトの
可メモリサイズは、php.iniのディレクティブです:「のmemory_limit = 128M;メモリの最大量は、スクリプトが消費される可能性があり、」私は256Mに設定すると
を、それは私がその60mbのファイルをダウンロードすることを可能にするだけでなく、約80mbのファイルをダウンロードすることができます。
また、これを1024Mに設定すると、260MBのファイルをダウンロードすることができます。
これは、問題がすべてのメモリを浪費しているスクリプトのどこかでリークしていることがわかります。ここで
は、ダウンロード・スクリプトです:
ini_set('display_errors',1);
error_reporting(E_ALL & ~E_NOTICE);
$strDB=mysql_connect("localhost","username","password")or die ("Error connecting to mysql.. Error: (" . mysql_errno() . ") " . mysql_error());
$database=mysql_select_db("file_management",$strDB);
if (isset($_GET["id"])) {
// List of nodes representing each 64kb chunk
$nodelist = array();
// Pull file meta-data
$sql_GetFileDetails = "
SELECT
FileID,
FileTypeID,
FileType,
FileName,
FileDescription,
FileSize,
FileUploadDate,
FileUploadBy
FROM file_details WHERE FileID = '".$_GET["id"]."';";
$result_GetFileDetails = mysql_query($sql_GetFileDetails) or die ("No results for this FileID.
Your Query: " . $sql_GetFileDetails . "
Error: (" . mysql_errno() . ") " . mysql_error());
if (mysql_num_rows($result_GetFileDetails) != 1) { die ("A MySQL error has occurred.
Your Query: " . $sql_GetFileDetails . "
Error: (" . mysql_errno() . ") " . mysql_error()); }
// Set the file object to get details from
$FileDetailsArray = mysql_fetch_assoc($result_GetFileDetails);
// Pull the list of file inodes
$sql_GetFileDataNodeIDs = "SELECT FileDataID FROM file_data WHERE FileID = ".$_GET["id"]." order by FileDataID";
if (!$result_GetFileDataNodeIDs = mysql_query($sql_GetFileDataNodeIDs)) { die("Failure to retrive list of file inodes
Your Query: " . $sql_GetFileDataNodeIDs . "
Error: (" . mysql_errno() . ") " . mysql_error()); }
while ($row_GetFileDataNodeIDs = mysql_fetch_assoc($result_GetFileDataNodeIDs)) {
$nodelist[] = $row_GetFileDataNodeIDs["FileDataID"];
}
$FileExtension = explode(".",$FileDetailsArray["FileName"]);
$FileExtension = strtolower($FileExtension[1]);
// Determine Content Type
switch ($FileExtension) {
case "mp3": $ctype="audio/mp3"; break;
case "wav": $ctype="audio/wav"; break;
case "pdf": $ctype="application/pdf"; break;
//case "exe": $ctype="application/octet-stream"; break;
case "zip": $ctype="application/zip"; break;
case "doc": $ctype="application/msword"; break;
case "xls": $ctype="application/vnd.ms-excel"; break;
case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
case "gif": $ctype="application/force-download"; break; // This forces download, instead of viewing in browser.
case "png": $ctype="application/force-download"; break; // This forces download, instead of viewing in browser.
case "jpeg": $ctype="application/force-download"; break; // This forces download, instead of viewing in browser.
case "jpg": $ctype="application/force-download"; break; // This forces download, instead of viewing in browser.
default: $ctype="application/force-download"; // This forces download, instead of viewing in browser.
}
// Send down the header to the client
header("Date: ".gmdate("D, j M Y H:i:s e", time()));
header("Cache-Control: max-age=2592000");
//header("Last-Modified: ".gmdate("D, j M Y H:i:s e", $info['mtime']));
//header("Etag: ".sprintf("\"%x-%x-%x\"", $info['ino'], $info['size'], $info['mtime']));
header("Accept-Ranges: bytes");
//header("Cache-Control: Expires ".gmdate("D, j M Y H:i:s e", $info['mtime']+2592000));
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=\"".$FileDetailsArray["FileName"]."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Type: ".$FileDetailsArray["FileSize"]);
ob_end_clean();
ob_start();
ob_start("ob_gzhandler");
$sql_GetFileDataBlobs = "SELECT FileData FROM file_data WHERE FileID = ".$_GET["id"]." ORDER BY FileDataID ASC;";
if (!$result_GetFileDataBlobs = mysql_query($sql_GetFileDataBlobs)) { die("Failure to retrive list of file inodes
Your Query: " . $sql_GetFileDataBlobs . "
Error: (" . mysql_errno() . ") " . mysql_error()); }
while ($row_GetFileDataBlobs = mysql_fetch_array($result_GetFileDataBlobs)) {
echo $row_GetFileDataBlobs["FileData"];
}
ob_end_flush();
header('Content-Length: '.ob_get_length());
ob_end_flush();
}
私はXdebugをし、出力ピークメモリ使用量の結果を使用していたが、何も総ピークメモリ使用量で、限界近くのどこかのような何かをして行くことが表示されません900kbのページ。
私はそれがメモリにファイルチャンクを集約していると思っているのですが、ファイルのチャンクがその量に達する唯一のものであり、スクリプトが失敗する原因になります。
私はあなたが好きなら、あなたが私のスクリプトをテストすることができるようにデータベースにファイルをアップロードするスクリプトを提供することができ、ちょうど私は任意の助け
乾杯を教えて!
ミック
* ///////// ///////// *
を解決した私は、hafichukする感謝を言うために偉大な応答をしたいです私の全問題を解決しました。
問題は2倍でした。
1 - 私はwhileループ内でob_flush()を使用していませんでした。私はそれを追加し、メモリを解放してダウンロード数を増やすことができましたが、無制限ではないことが分かりました。
例えば、memory_limit = 128Mとすると、今では40MB以上をダウンロードできるようになりました。実際には約200MBまで増やすことができました。しかし、これが再び失敗した場所です。最初のメモリ問題が解決しました。
レッスン1:オブジェクトをフラッシュしてください!
2 - 私は私のSQLクエリの結果を取得するためにmysql_queryを使用していました。問題は、これらの結果をバッファリングし、これが私のメモリ制限の問題に追加されていることです。
代わりにmysql_unbuffered_queryを使用しましたが、これは完全に機能します。
これにはいくつかの制限がありますが、結果を読み取っている間にテーブルがロックされます。
レッスン2:必要ない場合は、mysql結果をバッファしないでください。
(プログラム的な制限の範囲内で)最後のレッスン:これらの修正作業のすべてが、しかし、それはそれらの組み合わせに問題がないことを確認するためにいくつかのより多くのテストが必要です。
また、私はオブジェクトとPHPのメモリ割り当てについて多くのことを学びました。xdebugが提供するものよりも少し視覚的にプロセスを視覚的にデバッグする方法があればいいと思います。誰かがxdebugがこのプロセスで実際にどのように光を当てるかについてのアイデアがあれば、コメントに私に教えてください。
これは今後誰かを助けてくれることを願っています。
乾杯
ミック
whileループでhttp://php.net/manual/en/function.ob-flush.phpを試しましたか? – hafichuk
あなたは伝説です!それは(ほぼ)完全に機能します。ちょうどあなたに知らせるために、memory_limitディレクティブの完全な余裕に働くように見えるので、128Mに設定して140mbのファイルを試しても動作しますが、250mbのファイルをダウンロードしようとすると、 PHPの致命的なエラー:/ root/htdocs/file_manager/file_manager_downloadに134217728バイトの空きメモリサイズ(132392961バイトを割り当てようとしました)が使用可能になりました。[31-Oct-2011 11:46:01] 125行目の.phpはob_flush()です。ライン。 – Quantico773
mysql_unbuffered_queryは私の一日を保存しました。 – webtweakers