Выявить проблему с памятью в коде Perl / DBI - PullRequest
3 голосов
/ 29 марта 2011

Для начала - это не мой код - возникла проблема с кодом, и я пытаюсь выяснить, как отладить проблему.Я бы внес множество изменений в код, если бы мне дали такую ​​возможность (чрезмерные скобки, глобальные переменные, использование функции соединения вместо foreach и т. Д. И т. Д. И т. Д.).Он полон плохой практики, но это не то, что мне нужно для помощи.

Вот фрагмент кода Perl (Нет подпрограмм, ничего особенного - в основном откройте файл для результатов запросавыполнить запрос и вывести результаты в файл):

# earlier in the program, @row, $field, and $output are all declared globally, like this:
my @row;
my $field;
my $output;

# a file is opened for output, with filehandle ROWOUT
# a database statement handle (DBD::DB2) is executed

while ( @{row} = ${sth}->fetchrow_array ) {
    foreach ${field}( @{row} ) {
        ${field} =~ s/\s+$//;
        ${output} = "${output}\~${field}";
    }

    ${output} =~ s/\~//;
    print ROWOUT "${output}\n";
    undef ${output};
}

Где-то в цикле while скрипт Perl падает с ошибкой Out of Memory! (не чистый сбой - он просто перестает работатьс этим сообщением.)

В большинстве случаев объем этого запроса очень мал.Результаты запроса на этот раз, когда произошел сбой скрипта, намного больше (все еще не огромны): 150 000 строк, а каждая строка имеет ширину около 1200 байт.

Вещи, о которых я думал:

  1. Функция fetchrow_array в DBI достаточно умна, чтобы не вытягивать полный набор данных в память, верно?Я предполагаю, что данные находятся в базе данных, и fetchrow_array извлекает одну строку за раз, так что даже если у вас есть 10 миллиардов строк, у вас не должно быть проблем с памятью - это правильно?
  2. Вызов undef в переменной $output освободит используемую память, правильно?Если это не так, это может быть другое место, где может существовать проблема с памятью.
  3. Память, используемая переменной @row, будет повторно использоваться (?) Каждый раз при получении новой строки, правильно?Если нет, я мог бы увидеть, как использование глобального массива для хранения каждой строки может уничтожить память.

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

Заранее спасибо!

Ответы [ 3 ]

3 голосов
/ 03 апреля 2011

Возможно, вы (возможно, случайно) кэшируете слишком много строк. Вы можете узнать, сколько было внесено, проверив $sth->{RowsInCache}. Если это undef, то кеша нет, в противном случае вы получите количество строк.

Вы также можете избавиться от гимнастики, которую вы делаете с $output, переписав ее следующим образом:

while ( my @this_row = $sth->fetchrow_array ) {
    # Get rid of this line once you figure out your memory problem.
    print STDERR "Using ", ($sth->{RowsInCache} || 0), " rows in cache\n";

    print ROWOUT join('~', map { s/\s+$// } @this_row), "\n";
}

Итак, если у вас слишком много строк в кеше, вы можете ограничить его с помощью:

my $dbh = DBI->connect($dsn, $user, $pass, { RowCacheSize => 20 })
    or die "Cannot connect to $dsn: $DBI::errstr\n";

Из документации DBI вы можете управлять кешем (если ваш драйвер его поддерживает), используя значение следующим образом:

 0 - Automatically determine a reasonable cache size for each C<SELECT>
 1 - Disable the local row cache
>1 - Cache this many rows
<0 - Cache as many rows that will fit into this much memory for each C<SELECT>.
0 голосов
/ 30 марта 2011

Что касается # 1, я полагаю, что он загружает весь результат в память. Edit: я помню, что это опция в DBI

Для # 2 и # 3, вы действительно должны локализовать свои переменныек объему, в котором они используются.

Я подозреваю, что у вас фактически не хватает памяти после выполнения, хотя я знаю, что вы сказали иначе.Кажется маловероятным, что вы используете много памяти в этом цикле.Если, конечно, ROWOUT на самом деле не является ссылкой на переменную в памяти, но мы не знаем этого, если вы не предоставите полный скрипт.

0 голосов
/ 29 марта 2011

Увеличьте уровень трассировки и запустите код под отладчиками Perl и GDB. Вам нужно выяснить, где именно процесс выходит из-под контроля.

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

...