2011-12-22 7 views
6

У меня есть что-то, что выглядит следующим образом:Ускорение Perl DBI fetchrow_hashref

my $report = new ReportGenerator; #custom object 
my $dbh = $dbc->prepare('SELECT * FROM some_table WHERE some_condition'); #DBI handle 
$dbh->execute(); 
while(my $href = $dbh->fetchrow_hashref){ 
    $report->process_record($href); 
} 
$dbh->finish(); 
print $report->printReport(); 

Моя проблема заключается в том, что каждая итерация цикла очень медленно. Проблема заключается в MySQL. Мне было интересно, можно ли поместить какую-то оболочку в цикл while, чтобы она извлекала более одной записи за раз, в то же время, выборка всех записей в память тоже не практична. Меня не беспокоит эффективность кода (hashref vs arrayref и т. Д.). Скорее, меня интересует выборка, позволяющая говорить 10000 записей за раз.

База данных имеет ~ 5 миллионов записей. Я не могу изменить/обновить сервер.

Благодаря

+0

Этот код должен работать достаточно быстро. Вы уверены, что выбор не займет много времени? Вы можете захотеть, сколько времени займет выполнение. И, конечно же, ваш процесс может быть медленным. Вы можете попробовать синхронизировать только выборку без процесса. –

ответ

8

Вы можете использовать fetchall_arrayref функции, которая принимает аргумент «maxrows»:

while (my $data = $dbc->fetchall_arrayref(undef, 10000)) { 
    for my $row(@{$data}) { 
    $report->process_record($row); 
    } 
} 

Вы также можете посмотреть на RowCacheSize собственности, которая пытается контролировать, сколько записей возвращаются в выборке от вашего водителя.

+1

fetchall_arrayref не рекомендуется, когда вы просто обрабатываете записи по одному и отбрасываете их. Это связано с тем, что для хранения всех полей всех строк необходимо сделать много распределений памяти, а выделение памяти дорого. См. Стр. 22 из http://www.slideshare.net/Tim.Bunce/dbi-advanced-tutorial-2007 –

4

Какой бит медленный? Это вызов execute, fetchrow_hashref или process_record? Мне кажется маловероятным, что проблема fetchrow_hashref. Скорее всего, это выполнение запроса или черный ящик process_record.

Но это все догадки. Здесь невозможно реально помочь. Я рекомендую вам получить некоторые реальные данные о производительности кода, используя Devel::NYTProf.

+0

Я уже делал это и нашел, что проблема не связана с этим вопросом, и сказал, что оба метода довольно близки к друг друга. 221сек против 239 сек. Поэтому все еще есть небольшое улучшение. хотя я нашел интересное узкое место в поиске хэшей. У меня есть функция, которая проверяет, существует ли хэш, если он получает значение, и если он доцент, он извлекает его из mysql. со средним значением avg 4μs/call. проблема в том, что функция получает 15 миллионов раз. который составляет почти 1 минуту. но это не то, что можно легко устранить. – Smartelf

3

Самый быстрый способ выборки строк как хэшей с помощью DBI является использование bind_columns() так:

$sth->execute; 
    my %row; 
    $sth->bind_columns(\(@row{ @{$sth->{NAME_lc} } })); 
    while ($sth->fetch) { 
     print "$row{region}: $row{sales}\n"; 
    } 

Это только уместно, если вы счастливы для каждой строки повторно использовать тот же хэш.

Помимо этого, я согласен с davorg, избегайте догадок: сначала измерьте.

Для получения дополнительной информации об использовании DBI, включая производительность, см. Мой tutorial slides (с 2007 года, но по-прежнему актуальным).