簡體   English   中英

的PHP,MySQL的,我的內存泄漏

[英]php, mysql, my memory leaking

我沒想到這個腳本(被丟棄)會泄漏,而且我還沒有弄清楚罪魁禍首是什么。 你能發現什么嗎? 盡管這是一次性的代碼,但我擔心將來會重復此過程。 我從來沒有用PHP管理內存,但是隨着數據庫中的行數增加,它炸毀了我的php實例(已經將內存增加到1Gb)。

加利福尼亞州的表格特別大(目前為220萬行,隨着我刪除重復的行而減少)。 我在第31行收到內存錯誤($ row = mysql_fetch_assoc($ res))

致命錯誤:在第31行的C:\\ Documents and Settings \\ R \\ My Documents \\ My Webpages \\ cdiac \\ cdiac_ dup.php中,耗盡了1073741824字節的允許內存大小(嘗試分配24字節)

PHP 5.3.0,MySQL 5.1.36。 沼澤安裝的一部分。

這是整個代碼。 該腳本的目的是刪除重復的條目(將數據采集到分段表中,這在當時要快得多,但是現在我必須合並這些表。)

是什么原因造成的? 我忽略的東西? 還是只需要查看內存大小並在內存變大時手動調用垃圾回收?

<?php

define('DBSERVER', 'localhost');
define('DBNAME', '---');
define('DBUSERNAME', '---');
define('DBPASSWORD', '---');

$dblink = mysql_connect(DBSERVER, DBUSERNAME, DBPASSWORD);
mysql_select_db(DBNAME, $dblink);


$state = "AL";
//if (isset($_GET['state'])) $state=mysql_real_escape_string($_GET['state']); 
if (isset($argv[1]) ) $state = $argv[1];

echo "Scanning $state\n\n";


// interate through listing of a state to check for duplicate entries (same station_id, year, month, day)
$DBTABLE = "cdiac_data_". $state;
$query = "select * from $DBTABLE ";
$query .= " order by station_id, year, month, day ";

$res = mysql_query($query) or die ("could not run query '$query': " . mysql_errno() . " " . mysql_error());

$last = "";
$prev_row;
$i = 1;
$counter = 0;
echo ".\n";
while ($row = mysql_fetch_assoc($res)) {  
  $current = $row["station_id"] . "_" . $row["year"] . "_" . sprintf("%02d",$row["month"]) . "_" . sprintf("%02d",$row["day"]);
  echo str_repeat(chr(8), 80) . "$i  $current ";
  if ($last == $current) {
    //echo implode(', ', $row) . "\n";

    // merge $row and $prev_row
    // data_id  station_id, state_abbrev, year, month,  day,  TMIN, TMIN_flags, TMAX, TMAX_flags, PRCP, PRCP_flags, SNOW, SNOW_flags, SNWD, SNWD_flags

    printf("%-13s %8s %8s\n", "data_id:", $prev_row["data_id"], $row["data_id"]);
    if ($prev_row["data_id"] == $row["data_id"]) echo " + ";

    $set = "";
    if (!$prev_row["TMIN"] && $row["TMIN"])  $set .= "TMIN = " . $row["TMIN"] . ", ";
    if (!$prev_row["TMIN_flags"] && $row["TMIN_flags"])   $set .= "TMIN_flags = '" . $row["TMIN_flags"] . "', ";
    if (!$prev_row["TMAX"] && $row["TMAX"])   $set .= "TMAX = " . $row["TMAX"] . ", ";
    if (!$prev_row["TMAX_flags"] && $row["TMAX_flags"])   $set .= "TMAX_flags = '" . $row["TMAX_flags"] . "', ";
    if (!$prev_row["PRCP"] && $row["PRCP"])   $set .= "PRCP = " . $row["PRCP"] . ", ";
    if (!$prev_row["PRCP_flags"] && $row["PRCP_flags"])   $set .= "PRCP_flags = '" . $row["PRCP_flags"] . "', ";
    if (!$prev_row["SNOW"] && $row["SNOW"])   $set .= "SNOW = " . $row["SNOW"] . ", ";
    if (!$prev_row["SNOW_flags"] && $row["SNOW_flags"])   $set .= "SNOW_flags = '" . $row["SNOW_flags"] . "', ";
    if (!$prev_row["SNWD"] && $row["SNWD"])   $set .= "SNWD = " . $row["SNWD"] . ", ";
    if (!$prev_row["SNWD_flags"] && $row["SNWD_flags"])   $set .= "SNWD_flags = '" . $row["SNWD_flags"] . "', ";

    $delete = "";
    $update = "";
    if ($set = substr_replace( $set, "", -2 )) $update = "UPDATE $DBTABLE SET $set WHERE data_id=".$prev_row["data_id"]." and year=".$row["year"]." and month=".$row["month"]." and day=".$row["day"].";\n";
    if ($row["data_id"] != $prev_row["data_id"]) $delete = "delete from $DBTABLE where data_id=".$row["data_id"]." and year=".$row["year"]." and month=".$row["month"]." and day=".$row["day"].";\n\n";

    if ($update) {
      $r = mysql_query($update) or die ("could not run query '$update' \n".mysql_error());
    }
    if ($delete) {
      $r = mysql_query($delete) or die ("could not run query '$delete' \n".mysql_error());
    }    

    //if ($counter++ > 5) exit(0);
  }
  else {
    $last = $current;
    unset($prev_row);
    //copy $row to $prev_row
    foreach ($row as $key => $val) $prev_row[$key] = $val;
  }

  $i++;
}

    echo "\n\nDONE\n"; 
?>

我會嘗試兩件事:

1)而不是使用mysql_query在循環內運行UPDATE和DELETE查詢,而是將它們保存在文本文件中,以便以后執行。 例如: file_put_contents('queries.sql', $update, FILE_APPEND );

2)而不是執行while ($row = mysql_fetch_assoc($res))循環內的所有操作,而是首先獲取所有SELECT查詢結果,然后關閉數據庫連接以釋放所有數據庫資源,包括查詢結果。 僅在此之后,才執行循環過程。

如果在將數據庫結果存儲在一個數組中時內存不足,則可以嘗試將結果保存到一個臨時文件中(每行一條記錄/ FILE_APPEND),然后在循環中使用該文件(每條記錄讀取一行,使用fgets函數)。

更聰明地工作,而不是更辛苦:

SELECT station_id, year, month FROM table
    GROUP BY station_id, year, month
    HAVING COUNT(*) > 1

這將使您多次出現在表中的所有station_id /年/月元組。 假設您的大多數數據不是重復的,那將為您節省大量內存,因為現在您只需要遍歷這些元組並修復與它們匹配的行。

在嘗試跟蹤我的腳本中的內存使用問題時,我發現了這一點。 為我解決了這個問題后,我認為值得在此為下一個遇到同樣問題的人添加一個答復。

我使用的是mysqli,但適用於mysql的情況大致相同。

我發現的問題是查詢沒有釋放其結果。 解決方案是在執行更新和刪除查詢后使用mysqli_free_result()。 但更重要的是,在mysqli_query的循環中,我使用了* MYSQLI_USE_RESULT *的額外參數。 這樣做有副作用,因此請對更新和刪除查詢使用單獨的連接。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM