php, mysql, утечка моей памяти - PullRequest
       2

php, mysql, утечка моей памяти

2 голосов
/ 28 августа 2011

Я не ожидал, что этот сценарий (выбрасывание) будет просачиваться, и я не понял, кто виноват. Вы можете что-нибудь заметить? Несмотря на то, что это неиспользуемый код, я обеспокоен тем, что повторю это в будущем. Мне никогда не приходилось управлять памятью в PHP, но из-за количества строк в БД мой экземпляр PHP взорвался (уже увеличил объем памяти до 1 ГБ).

Таблица в Калифорнии особенно больше остальных (в настоящее время 2,2 м строк, меньше, поскольку я удаляю повторяющиеся строки). Я получаю сообщение об ошибке в строке 31 ($ row = mysql_fetch_assoc ($ res))

Неустранимая ошибка: допустимый объем памяти 1073741824 байт исчерпан (пробовал выделить 24 байта) в C: \ Documents and Settings \ R \ Мои документы \ My Веб-страницы \ cdiac \ cdiac_ dup.php в строке 31

PHP 5.3.0, mysql 5.1.36. часть установки wamp.

вот и весь код. цель этого сценария - удалить дублирующиеся записи (данные были собраны в сегментированные таблицы, что в то время было намного быстрее, но теперь я должен объединить эти таблицы)

что это вызывает? что-то я пропускаю? или мне просто нужно следить за размером памяти и вызывать сборку мусора вручную, когда он становится большим?

<?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"; 
?>

Ответы [ 3 ]

2 голосов
/ 28 августа 2011

Работайте умнее, а не усерднее:

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

Это даст вам все кортежи station_id / year / month, которые появляются в таблице более одного раза.Предполагая, что большинство ваших данных не являются дубликатами, это сэкономит вам много памяти, поскольку теперь вам просто нужно пройти через эти кортежи и исправить строки, соответствующие им.

2 голосов
/ 28 августа 2011

Я бы попробовал две вещи:

1) Вместо выполнения запросов UPDATE и DELETE внутри цикла с использованием mysql_query, сохраните их в текстовом файле, чтобы выполнить позже.Например: file_put_contents('queries.sql', $update, FILE_APPEND );

2) Вместо того, чтобы делать все внутри цикла while ($row = mysql_fetch_assoc($res)), сначала захватите все результаты запроса SELECT, затем закройте соединение с базой данных, освободив все ресурсы базы данных, включая результат запроса.Только после этого выполните процесс цикла.

Если у вас не хватает памяти при сохранении результатов базы данных в одном массиве, вы можете вместо этого попытаться сохранить результаты во временном файле (одна запись на строку / FILE_APPEND),и затем использовать этот файл в цикле (чтение одной строки на запись, используя функцию fgets).

0 голосов
/ 10 мая 2013

Я обнаружил это при попытке отследить проблему использования памяти в моем скрипте. Решив проблему для себя, я подумал, что стоит добавить здесь ответ для следующего человека, который придет с той же проблемой.

Я использовал mysqli, но то же самое относится и к mysql.

Проблема, которую я обнаружил, заключалась в том, что запросы не освобождали свои результаты. Решением было использование mysqli_free_result () после выполнения запросов на обновление и удаление. Но, что более важно, в mysqli_query для цикла я использовал дополнительный параметр * MYSQLI_USE_RESULT *. Это имеет побочные эффекты, поэтому используйте отдельное соединение для запросов на обновление и удаление.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...