2015-05-09 2 views
2

Я пробегаем по 40 ассоциативных массивов:PHP цикл - низкая производительность SQL

array(
    'key0' => value, 
    'url0' => value, 
    'tit0' => value, 
    'cdn0' => value, 
    'cdn1' => value, 
    'cdn2' => value, 
) 

и я совершаю множественным select и один возможныеinsert запросы. Я попытался оптимизировать производительность, уменьшив количество запросов.

foreach($buf){ 
    $sth1->execute();//SELECT * FROM metadata WHERE url = '{$buf['url0']}' 
    $sth2->execute();//SELECT * FROM metadata WHERE key = '{$buf['key0']}' 

    if(!$ret=$sth1->fetch(PDO::FETCH_ASSOC)){ 
    if($sth2->fetch(PDO::FETCH_ASSOC)){ 
     die('key duplicate - error'); 
    } 

    $data[ ] = $buf; 

    $sth3->execute();//INSERT INTO metadata ... 
    } else { 
    $data[ ] = $ret; 
} 

Это, однако, медленно (цикл занимает около 4,2 секунды). Я попытался сделать это быстрее, удалив запрос.

foreach($buf){ 
    $sth1->execute();//SELECT * FROM metadata WHERE url = '{$buf['url0']}' OR key = '{$buf['key0']}' 

    if(!$ret=$sth1->fetch(PDO::FETCH_ASSOC)){  
    $sth3->execute();//INSERT INTO metadata ... 

    $data[ ] = $buf; 
    } else { 
    if($ret['key']==$generated_key){die('key duplicate - error');} 

    $data[ ] = $ret; 
} 

Это по какой-то причине сделало его еще медленнее (5-6 с). Таким образом, я остался невежественным. Как я могу сделать это с разумным временем загрузки? Я попытался поставить $sth2->execute в операторе if(!$ret...), но это не дает мне никакой скорости.

Это не похоже на INSERT, что является проблемой, поскольку большинство из array данных уже IN базы данных. Всякий раз, когда я запускаю запросы в phpmyAdmin, он делает это за 0.0000000003 секунды, поэтому он должен иметь какое-то отношение к циклу.

+1

Я бы попытался использовать одну транзакцию для всех вставок. Он изменяет объем работы, который должна выполнять база данных и когда она это делает. Запустите «транзакцию» перед вашим «циклом foreach» и закройте его. Имейте в виду. любые ошибки, то все 40 вставок нужно будет сделать снова. –

+0

Готово. Небольшое увеличение скорости. (.2s). Спасибо. – KaekeaSchmear

+0

Хмм ... это неутешительно. Я бы предложил «объяснить план» и запрограммировать запрос, чтобы увидеть, где он проводит время. Я также хотел бы рассмотреть «подготовку» запросов до цикла «foreach». –

ответ

2

Ускорение # 1: Перемещение $sth2->execute(); в начало после первого if. Кажется, вам не нужен результат, если тест url преуспевает.

Ускорение # 2: Обязательно наличие INDEX(url) и INDEX(key);

Ускорение # 3: Измените INDEX(key) на UNIQUE(key) и пропустите SELECT ... key; просто проверьте наличие ключа dup после выполнения INSERT.

Ускорение # 4: (Этот может или не поможет.) Сделайте все из SELECT ... url в одном запросе: SELECT ... url IN (40-urls-in-list). (Требуется Ускорение # 2.) Сохраните результаты в ассоциативном массиве и пройдите через него, чтобы сделать остальную часть вашего материала SELECT/INSERT.

Форсировочная # 5: Построить «партию» INSERT (несколько строк в одногоINSERT), а затем execute его в конце цикла 40 пунктов.

Просьба указать SHOW CREATE TABLE.

MyISAM? или InnoDB? Вы настроили innodb_buffer_pool_size на 70% доступной оперативной памяти? Если нет, это может повысить производительность.

0

К сожалению, информации о вашей стратегии индексирования MySQL или количестве выбранной вами информации недостаточно.

Возможные вопросы, которые приходят на ум в первой секунде:

  • при выполнении запросов из MySQL вы попали кэш запросов, из кэша петли находится в каком-то момент истекло
  • плохой стратегии индексирования в базе данных - выбор по VARCHAR не всегда лучшее решение, используя символ (16) и сумма MD5 (или нечто подобное), вероятно, немного лучше подход
  • у вас есть какой-то незаметный цикл в итерациях

Решение для всех этих вопросов заключается в том, чтобы отображать много отладочных данных с меткой времени точности, читать их и записывать SQL-запросы в журнал MySQL-сервера и запускать все это в одной партии в консоли/PHPMyAdmin для лучшей отладки.