огромные массивы

Тема в разделе "PHP", создана пользователем phillip, 30 май 2011.

Статус темы:
Закрыта.
Модераторы: latteo
  1. phillip

    phillip

    Регистр.:
    4 сен 2007
    Сообщения:
    413
    Симпатии:
    15
    есть файл, 3млн строк с url'ами. Надо в файле убрать те урлы, которые ведут с одного домена. то бишь уникализировать по доменам. Пример:

    https://nulled.cc/newthread.php?do=newthread&f=214
    https://nulled.cc/memberlist.php

    из этих двух ссылок надо оставить только одну, так как домены совпали.

    Как это сделать максимально быстро? Я стал делать так.... Забил все в один массив, затем беру первый элемент массива, заношу в массив $result. Далее беру еще одну строку, вычисляю домен в ней, ищу в $result добавляли ли такой домен. Если не добавляли, добавляем. И так далее. В итоге чем дальше идем, тем медленнее работает скрипт потому что $result разрастается. В итоге, за 3 часа у меня прошло 500к строк, и практически остановилось, далее идет ооочень медленно. Я подозреваю что 3млн он будет проходить еще до след.вечера.
    Подскажите как сделать чтоб было быстрее
     
  2. Conwell

    Conwell

    Регистр.:
    23 мар 2009
    Сообщения:
    337
    Симпатии:
    177
    Методом пузырька можно перебирать долго, но это самый простой способ сортировки: http://citcity.ru/74-sortirovka-osnovnyje-algoritmy.html тут можно прочитать про методы вообще.
    Я бы сделал немного по другому, отсортировал строки (получил бы значения доменов), после чего все это лучше влить в базу, а дальше уже делать выборку с объединением по полю домена. Mysql с GROUP BY сделает это несколько быстрее. При этом, сразу получится массив с отсортированными значениями.
     
    phillip нравится это.
  3. chang

    chang

    Регистр.:
    20 ноя 2009
    Сообщения:
    364
    Симпатии:
    117
    создаешь таблицу в БД
    и делаешь у нее поле уникальным

    потом в цикле читаешь файл с домена и тупо инсертишь с игнором ошибок

    при индексе "уникальный" СУБД сама будет все фильтровать .. причем это дело будет сортироваться ... выстраиваться в виде дерева .. т.е. имхо самый простой в реализации способ и при этом достаточно производительный
    как для разовой операции самое оно

    ну и к примеру можно делать инсерт вида

    ful_url domain
    и ключ цеплять только на domain
    ну и в domain вставлять лишь вырезанный домен ( не всю ссылку) так будет еще чуть быстрее фильтровать
     
    phillip нравится это.
  4. -=Xardas=-

    -=Xardas=-

    Регистр.:
    17 сен 2008
    Сообщения:
    250
    Симпатии:
    58
    согласен с chang, но более быстрым вариантом будет использование не мускуля а key-val хранилища, например redis, ибо так будет в сотни раз быстрее.
     
  5. phillip

    phillip

    Регистр.:
    4 сен 2007
    Сообщения:
    413
    Симпатии:
    15
    chang, чето я вставляю так как ты сказал, и получается у меня... что при работе с файлами удалялось меньше ссылок, а счас из 600к осталось 100к... то есть 5из6 не попали в бд, а раньше 3 из 5 не попадали.... где косяк может быть?
     
  6. -=Xardas=-

    -=Xardas=-

    Регистр.:
    17 сен 2008
    Сообщения:
    250
    Симпатии:
    58
    ты думаешь что здесь есть телепаты? Выложи код которым ты это делаешь.
     
  7. phillip

    phillip

    Регистр.:
    4 сен 2007
    Сообщения:
    413
    Симпатии:
    15
    В случае с бд писал так

    PHP:
    $count=0;
    $z=file('source.txt');
    echo 
    count($z).'<br/>';
    foreach(
    $z as $key => $url){
        
    preg_match("&(https|http)://(.+)?/&",$url,$m);
        
        if(!isset(
    $m[2])) continue;
        
            
        
    $STH $DBH->prepare("INSERT INTO `rama` VALUES( 0 , ? , ?)");
        @
    $STH->execute(array($url,$m[2]));
        
        
    $count++;
        if(
    $count==1000) { echo $key.'<br/>'flush(); $count=0; }
    в случае с файлами
    PHP:
    $domens=array();
    $count=0;
    foreach(
    $z as $key => $url){
        
    //echo $url."<br/>";

        
    $exist=null;
        
    preg_match("&(https|http)://(.+)?/&",$url,$m);
        
        if(!isset(
    $m[2])) continue;
        
        
        foreach(
    $domens as $domen){
            if(
    $domen==$m[2]) $exist=true;
        }
        if(!
    $exist){
            
    $domens[]=$m[2];
            
    //$fn=fopen('result.txt','w');
            
    $fn=fopen('result.txt','a');
            
    fwrite($fn$url);
            
    fclose($fn);
            
            
    $fn=fopen('result-domains.txt','a');
            
    fwrite($fn$m[2]);
            
    fclose($fn);
        }
        
    $count++;
        if(
    $count==1000) { echo $key.'<br/>'flush(); $count=0; }
        
    }
     
  8. latteo

    latteo Эффективное использование PHP, MySQL

    Moderator
    Регистр.:
    28 фев 2008
    Сообщения:
    1.450
    Симпатии:
    1.244
    1) Протестируй parse_url() на скорость, вместо preg_match.
    2) Вместо одиночных инсертов сформируй один большой sql с неотфильтрованными данными, затем импортируй его в бд. Например, через Navicat Premium. Выигрыш по скорости заметно отличается, проверенно.
    3) Ну и как альтернатива, не жалея оперативки загоняй все домены в один массив и проверь скорость работы функции array_unique
     
    phillip нравится это.
  9. Miraage

    Miraage Angular/Laravel

    Регистр.:
    3 июн 2008
    Сообщения:
    230
    Симпатии:
    51
    preg_grep + array_unique
     
    venetu и phillip нравится это.
Статус темы:
Закрыта.