Нужно обработать большой файл, посмотреть некоторые данные в строках и если нужно, добавить записи в бд. Как можно ускорить работу? Я так понимаю нужно использовать многопоточность и pthreads?
Сейчас опишу ситауцию, так понятней станет: из огромного фала читаются строки, в строках ищутся домены. Найденный домен нужно проверить по базе геоип (локальной или удаленно). Потому эти данные заносятся в бд. Если задачу разбить на несколько потоков, то не нужно ждать пока каждый домен по геоип пробьется и запись добавится в базу, это будет параллельно происходить. Хотелось бы этот процесс максимально ускорить. А обработка в цепочку это как?
если у тебя много-процессорный комп + подходящий язык и алгоритм, позволяющий эффективно разнести потоки без блокировок, вот тогда может быть и много-… Добавлено спустя 7 минут 17 секунд: я объясню как НЕ в цепочку: допустим твои данные в XML и ты умеешь пользоваться SimpleXML. твой первый порыв будет сделать simplexml_load_file($filename), после чего всё умирает. то же самое с PHPExel и множеством других прекрасных инструментов. тупо не хватит памяти. придется искать способ подсасывать файл по частям. с твоим геоип может быть не настолько показательно, если ты сумеешь размазать файл по потокам. но реально на php и mysql ты не сможешь одновременно читать и писать много потоков ибо есть блокировки. а вот оверхеда хлебнешь.
Я поэтому про pthreads и спрашиваю. Например когда нужно было инфу с множества сайтов стянуть, многопоточность решала. А здесь все равно скорость получается не очень большая, хоть и поставил потоков под 300 и в бд максимальное кол-во соединений до 500. Скорость конечно лучше чем в одном каждую строку по очереди обрабатывать, но все равно не то. Да, и что за обработка в цепочку?
Памяти у меня достаточно, на локальной машине 8 гб, на сервере вообще дофига. Можно конечно и построчно читать (это и есть в цепочку, только дошло) там сервер ссд, поэтому норм будет. А что за блокировки и какой оверхед? Я с такой задачей в первый раз сталкиваюсь. Интересно как ее решить.
Читай, дели на порции, запускай в отдельном треде дальше на обработку каждую, но это НЕ для PHP задача.
Я так примерно в тестовом варианте и делал - пихал по N строк в массив и этот массив отдавал треду. Но скорость все-равно не ахти. А почему задача не для php?
Я не вижу проблемы в том чтобы плодить форки, знающие с какой по счету строки начать и сколько обработать. Хоть двести форков.
Братья, чем рассуждать, сделайте. Будет интересно оценить. freelsd, ты начинай делать, а общество поможет.
Почему-то мне кажется, от ТСа мы н и ч е г о не увидим ))) Предлагаю альтернативный открытый челлендж по быстрому разбору файла: viewtopic.php?f=47&t=56710
Ну я могу свое убожество забросить, только как оформлять парсинг строки в классе тредов? Просто чтение строки, выделение из нее мыла и запись в бд? Если устроит, то сегодня мб выложу кодес. Добавлено спустя 33 минуты 54 секунды: Вот схема тестовой бд: Код (PHP): CREATE table mail ( id int NOT NULL AUTO_INCREMENT, mail varchar(255), domain varchar(255), PRIMARY KEY(id) )ENGINE = MYISAM; Вот пример многопоточного скрипта: Код (PHP): <?php $DB['host'] = "localhost"; $DB['user'] = "root"; $DB['password'] = ""; $DB['database'] = "test"; $data = file("test_data.txt"); $data_size = count($data); if($data_size < 1) exit("No data to process.\n"); $workers = [ ]; $t_count = 50; $max_arr_size = 150; $flag = 1; $k = 0; $perc_diff = 1; $last_percent = 0; $s_time = microtime ( TRUE ); while ( $flag === 1 ) { $c_w = count ( $workers ); if ($c_w < $t_count) { for($i = $c_w; $i < $t_count - $c_w; $i ++) { if ($k >= $data_size) { $flag = 0; break; } $array_size = 0; $new_array = array (); while ( $array_size < $max_arr_size ) { if ($k >= $data_size) break; if ($array_size >= $max_arr_size) break; $new_array [] = $data [$k]; $k ++; $array_size ++; } //echo "--".$k."\n"; $workers [$i] = new test_thread ( $new_array, $DB ); // echo $data[$k]."\n"; $workers [$i]->start (); //$k ++; } } //echo "gogog"; $c_w = count ( $workers ); for($i = 0; $i < $c_w; $i ++) { if ($workers [$i]->join ()) { //echo "joining $i\n"; unset ( $workers [$i] ); } } $e_time = microtime ( TRUE ); $t = $e_time - $s_time; $percent = ($k / $data_size) * 100; if (($percent - $last_percent) >= $perc_diff || $percent == 100) { $last_percent = $percent; echo "progress: " . $percent . " percent ($k of $data_size) \n"; echo "time: " . $t . "\n"; } //sleep(1); } class test_thread extends Thread { private $my_str; private $new_arr; private $db_data; function __construct($new_arr, $db_data) { $this->my_arr = $new_arr; $this->db_data = $db_data; } public function run() { //print_r($this->my_arr); //exit(); $DB = $this->db_data; //print_r($DB); //exit(); $mysqli_obj = new mysqli($DB['host'], $DB['user'], $DB['password'], $DB['database'] ); if(mysqli_connect_errno()) { printf("MySQL connection error: %s", mysqli_connect_error()); return; } $arr = $this->my_arr; foreach($arr as $str){ $str = trim($str); preg_match("/[_A-Za-z0-9-\.]+(\.[_A-Za-z0-9-]+)*@([A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,5}))/", $str, $matches); $mail = trim($matches[0]); $domain = trim(strtolower($matches[2])); $query = "INSERT INTO mail(mail, domain) values('".$mysqli_obj->real_escape_string($mail)."', '".$mysqli_obj->real_escape_string($domain)."')"; //echo $query."\n"; $result = $mysqli_obj->query($query); if($mysqli_obj->error) { echo $mysqli_obj->error."\n"; } } return; } } Для его нормальной работы нужен php7 x64 + pthreads 3.1.x (наверное любой v3 подойдет). Когда тестовый пример делал, то убрал из него чтение ГеоИП данных из бинарного файла и скорость получилась очень даже неплохой: Код (PHP): progress: 99 percent (1980000 of 2000000) time: 57.551290988922 progress: 100 percent (2000000 of 2000000) time: 58.096322059631 Может дело в том что у меня винт тормознутый и можно как-нибудь прочитать файл в память и уже оттуда с ним работать. Но там не простой текстовый файл а бинарний в каком-то своем формате и нужны специальные апи для работы с ним: Код (PHP): $gi = geoip_open("GeoIP/GeoIP.dat", GEOIP_STANDARD); $country = geoip_country_name_by_addr($gi, $ip); Получается что именно такие операции тормозят всю работу. А простое чтение и запись в бд, как оказалось, весьма шустро происходит.
Проблема оказалась еще в том что скрипт пользует Код (PHP): $ip = gethostbyname($domain); который работает весьма тормознуто. Интересно, как это обойти?
Да неплохая идея. Мне тут подсказали что можно в самом скрипте кэшировать домен=ип. Только это у меня пока не получается для многопоточного скрипта запилить.
Протестил я с кэшированием средствами рнр. Наверное ваш вариант с кэширующим днс пока самое лучшее решение.