За последние 24 часа нас посетили 17919 программистов и 1648 роботов. Сейчас ищут 987 программистов ...

Работа с очень большим файлом

Тема в разделе "PHP для новичков", создана пользователем graf_vorontsov, 13 фев 2013.

  1. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.251
    Адрес:
    там-сям
    Ну хорошо, fgets станет в полтора раза быстрее чем он же с параметром. Всё равно он будет заметно медленнее, чем stream_get_line. Я натравил ваш скрипт на свою "Войну и мир" -- 63.530905008316 против 43.740499019623.

    Так что вывод вы сделали неправильный.
     
  2. graf_vorontsov

    graf_vorontsov Активный пользователь

    С нами с:
    12 фев 2011
    Сообщения:
    183
    Симпатии:
    1
    Адрес:
    Украина, Харьков
    у меня время выполнения файла на 3470 строк
    в этом случае 108.04760408401
    Код (Text):
    1.     $handle = fopen("cross.txt", "r");
    2.    
    3.     while(($fle = fgetcsv($handle, 100000000,",","'"))!== FALSE){      
    4.         $query = mysql_query("INSERT INTO `crossnumbers` (ARL_ART_ID, ARL_SEARCH_NUMBER, ARL_BRA_ID, ARL_DISPLAY_NR) VALUES ('$fle[0]', '$fle[1]', '$fle[3]', '$fle[4]')");
    5.        
    6.     }
    а в этом 114.80180907249

    Код (Text):
    1.     $handle = fopen("cross.txt", "r");
    2.    
    3.     while (!feof($handle)) {
    4.         $line = stream_get_line($handle, 0, "\r\n");
    5.         $fle = str_getcsv($line, ",", "''", "\r\n");
    6.        
    7.         $query = mysql_query("INSERT INTO crossnumbers (ARL_ART_ID, ARL_SEARCH_NUMBER, ARL_BRA_ID, ARL_DISPLAY_NR) VALUES ('$fle[0]', '$fle[1]', '$fle[3]', '$fle[4]')");
    8.  
    9.     }
     
  3. Chushkin

    Chushkin Активный пользователь

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Если 108 это секунды, то у Вас движок настроен "нехорошо".
    Решение: использовать транзакции, по 1000-3000 записей на одну транзакцию.
    Для mySQL скорость записи в режиме транзакции должна быть несолько тысяч записей в секунду. Если нет, то смотреть настройки движка.
     
  4. graf_vorontsov

    graf_vorontsov Активный пользователь

    С нами с:
    12 фев 2011
    Сообщения:
    183
    Симпатии:
    1
    Адрес:
    Украина, Харьков
    делаю так
    Код (Text):
    1.    $handle = fopen("cross.txt", "r");
    2.    
    3.     while (!feof($handle)) {
    4.         $line = stream_get_line($handle, 0, "\r\n");
    5.         $fle = str_getcsv($line, ",", "''", "\r\n");
    6.        
    7.         $query = mysql_query("INSERT INTO crossnumbers (ARL_ART_ID, ARL_SEARCH_NUMBER, ARL_BRA_ID, ARL_DISPLAY_NR) VALUES ('$fle[0]', '$fle[1]', '$fle[3]', '$fle[4]')");
    8.  
    9.     }
    и выдаёт ошибку вот такую
    1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'BMES4204', '134', 'BM-ES-4204', 'MOOG')' at line 1

    Добавлено спустя 1 минуту 19 секунд:
    а как использовать то что вы мне советуете? можете примерчик кинуть?
    и как настроить движок по другому и что именно настраивать?
     
  5. Chushkin

    Chushkin Активный пользователь

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Мои данные (Ваш тест):
    - Total: 12118735 lines in 22.5143 sec (stream, как есть)
    - Total: 12119553 lines in 57.3517 sec (fgets, как есть)
    - Total: 12119553 lines in 19.5919 sec (fgets без параметра)
    - Total: 12119553 lines in 28.3024 sec (fgets без параметра, но с rtrim)
    - Total: 0 lines in 19.6603 sec (stream, как есть и без подсчёта времени каждые 100000)
    - Total: 0 lines in 17.1730 sec (fgets без параметра и без подсчёта времени каждые 100000)
    - Total: 0 lines in 27.2675 sec (fgets без параметра с rtrim и без подсчёта времени каждые 100000)
    К диску обращения практически нет (файл весть в буфере Win7, я так понимаю). Загрузка проца i7 - "стандартные" 12%.
    Как-то так...

    Добавлено спустя 6 минут 16 секунд:
    1) Изучите доку по транзакциям.
    2) затем в цикле каждую 1000 записей commit и запуск снова
    1) Читать доку по движку, который Вы используете.
    2) Если Вам это не нужно по работе (часто), то лучше не лезть и использовать транзакции - так много проще.
     
  6. MaXyC_Web_Studio

    MaXyC_Web_Studio Активный пользователь

    С нами с:
    31 дек 2006
    Сообщения:
    678
    Симпатии:
    3
    Адрес:
    Новоуральск
    фиас распарсил хмл за пару часов. вес больше 4 гигов.
     
  7. Chushkin

    Chushkin Активный пользователь

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Что есть "фиас"?
     
  8. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    Федеральная информационная адресная система.
    грубо говоря это замена КЛАДРа. более полная база, лучше структура, обновления, форматы хранения и т.д.
     
  9. Chushkin

    Chushkin Активный пользователь

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Аааа, понятно - вариант "казнить нельзя помиловать" от MaXyC_Web_Studio :D
     
  10. graf_vorontsov

    graf_vorontsov Активный пользователь

    С нами с:
    12 фев 2011
    Сообщения:
    183
    Симпатии:
    1
    Адрес:
    Украина, Харьков
    дайте пожалуйста примерчик по этой теме
     
  11. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    примерчик?
    START TRANSACTION;
    INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9),...,(10001,10002, 10003);
    COMMIT;
     
  12. Chushkin

    Chushkin Активный пользователь

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Во-во, такая непонятнка частенько бывает ;)
    INSERT с множественным VALUES и так выполняется атомарно, т.е. транзакционные рамки не нужны.

    Вот молодёжь обленилась ныне, даже одну извилину лень напрячь! :(

    Модифицируем Ваш вариант, что-то вроде:
    Код (Text):
    1.    $handle = fopen("cross.txt", "r");
    2.     $i = 0;
    3.     mysql_query('begin transaction');
    4.     while(($fle = fgetcsv($handle, 0,",","'"))!== FALSE){
    5.         $query = mysql_query("INSERT INTO `crossnumbers` (ARL_ART_ID, ARL_SEARCH_NUMBER, ARL_BRA_ID, ARL_DISPLAY_NR) VALUES ('$fle[0]', '$fle[1]', '$fle[3]', '$fle[4]')");
    6.       $i++;
    7.       if($i > 999) {
    8.         mysql_query('commit')
    9.         mysql_query('begin transaction');
    10.         $i = 0;
    11.       }
    12.     }
    13.     mysql_query('commit')
    Добавлено спустя 3 минуты 19 секунд:
    Но вообще-то igordata прав в том, что ещё лучше, чем транзакции, использовать множественные значения values() для пакетного добавления записей - будет ещё быстрее.
     
  13. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    хз насчёт транзакций. если мне надо было всунуть базу из файла, то я б залочил таблицы. но такую операцию конечно нельзя делать часто. Либо тогда надо делать всё по-уму на транзакциях,но прочие выборки должны учитывать эти транзакции и выбирать себе строки в случае необходимости с залочкой тоже.

    ну это просто пример...я так понял, что транзакции нужны как раз для того, чтобы стопорнуть, потом порциями фигачить с перерывами на чтение новой порции из файла. А атомарные операции рулят. С ними спокойно. Иногда проще пожертвовать частью логики и сделать типа как INSERT ON DUPLICATE KEY UPDATE и после выборку, чем делать выборку и на фейле инсертить, т.к. придётся лочить таблицу во втором случае.
     
  14. graf_vorontsov

    graf_vorontsov Активный пользователь

    С нами с:
    12 фев 2011
    Сообщения:
    183
    Симпатии:
    1
    Адрес:
    Украина, Харьков
    если делать так, то выдаёт ошибку Out of memory
    а если так
    Код (Text):
    1.     $handle = fopen("TOF_ART_LOOKUP.txt", "r");
    2.            
    3.     mysql_query('begin transaction');
    4.    
    5.     $i = 0;
    6.    
    7.     while (!feof($handle)) {
    8.         $line = stream_get_line($handle, 0, "\r\n");
    9.         $fle = str_getcsv($line, ",", "''", "\r\n");
    10.    
    11.         //грузим номера в таблицу БД
    12.         $query = mysql_query("INSERT INTO `crossnumbers` (`ARL_ART_ID`, `ARL_SEARCH_NUMBER`, `ARL_BRA_ID`, `ARL_DISPLAY_NR`) VALUES ('$fle[0]', '$fle[1]', '$fle[3]', '$fle[4]')");
    13.         $i++;
    14.         if($i > 999) {
    15.             mysql_query('commit');
    16.             mysql_query('begin transaction');
    17.             $i = 0;
    18.         }
    19.        
    20.         mysql_query('commit');
    21.     }  
    то заливает в базу только 13701 строк и прекращается выполнение скрипта, ошибок не выдаёт
     
  15. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    Прям беда на беде!.. =(
     
  16. Chushkin

    Chushkin Активный пользователь

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Учите матчасть и всё будет работать. ;)
    п.с.
    Никто за Вас делать не будет здесь, - могут только подсказать. И то не каждый раз.
     
  17. graf_vorontsov

    graf_vorontsov Активный пользователь

    С нами с:
    12 фев 2011
    Сообщения:
    183
    Симпатии:
    1
    Адрес:
    Украина, Харьков
    я знаю, и не прошу чтоб кто-то что-то за меня делал, просто не всегда понятны подсказки которые дают ...
    я учусь, маловато ещё знаю, но по мере того как сталкиваюсь с проблемой, разбираю и учу эту тему, но не всегда всё наглядно понятно.
    спасибо за помощь. Пока что пошёл я другим путём, минуя файл. Выгружаю напрямую из одной БД в другую.. что-то файл мне не дался.
    Но хочу всётаки разобрать эту тему с файлом...
     
  18. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    в чем затык-то?
     
  19. sobachnik

    sobachnik Старожил

    С нами с:
    20 апр 2007
    Сообщения:
    3.380
    Симпатии:
    13
    Адрес:
    Дмитров, МО
    Может достигнут max_execution_time ?
     
  20. Chushkin

    Chushkin Активный пользователь

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Простой синтетический Тест скорости добавления записей в БД.

    mySQL 5.5, InnoDB; PHP, mysqli
    Таблица:
    Код (Text):
    1. CREATE TABLE `ttt` (
    2.   `id` int(11) NOT NULL AUTO_INCREMENT,
    3.   `text` varchar(255) DEFAULT NULL,
    4.   PRIMARY KEY (`id`)
    5. ) ENGINE=InnoDB CHARSET=cp1251
    В тест artoodetoo (см.выше) был добавлен кусок:
    Код (Text):
    1. $d->query("insert into ttt (text) values('{$d->real_escape_string($line)}')");
    2. if(! ($i % 1000)) {
    3.   $d->query('commit');
    4.   $d->query('start transaction');
    5. }
    Результаты для (innodb_flush_log_at_trx_commit = 2):
    Код (Text):
    1. - Total: 1000000 lines in   2.0179 sec (stream_get_line, простое чтение)
    2. - Total: 1000000 lines in 108.3384 sec (+mysqli, без транзакций /для сравнения/)
    3. - Total: 1000000 lines in  74.4685 sec (+mysqli, 100 зап/транзакцию)
    4. - Total: 1000000 lines in  71.3403 sec (+mysqli, 1000 зап/транзакцию)
    5. - Total: 1000000 lines in  68.9027 sec (+mysqli, 10000 зап/транзакцию)
    6. - Total: 1000000 lines in  41.9548 sec (+mysqli, 100 значений VALUES, без транзакций)
    7. - Total: 1000000 lines in  29.8311 sec (+mysqli, 1000 значений VALUES, без транзакций)
    Итого: Множественный VALUES где-то в ~2 раза быстрее. Позволяет добавлять до 33 тысяч зап/сек.

    Результаты для (innodb_flush_log_at_trx_commit = 1 /default):
    Код (Text):
    1. - Total:    1000 lines in  50.3604 sec (+mysqli, без транзакций /т.е. ~50000 сек для 1 млн.зап./)
    2. - Total: 1000000 lines in 724.3489 sec (+mysqli, 100 зап/транзакцию)
    3. - Total: 1000000 lines in 147.4507 sec (+mysqli, 1000 зап/транзакцию)
    4. - Total: 1000000 lines in  78.0385 sec (+mysqli, 10000 зап/транзакцию)
    5. - Total: 1000000 lines in 600.8820 sec (+mysqli, 100 значений VALUES, без транзакций)
    6. - Total: 1000000 lines in  90.5256 sec (+mysqli, 1000 значений VALUES, без транзакций)
    7. - Total: 1000000 lines in  35.5952 sec (+mysqli, 10000 значений VALUES, без транзакций)
    Итого: Множественный VALUES где-то в ~2 раза быстрее.
    Кроме того, пакетное добавление 10000 записей в ~2.5 раза быстрее, чем в 1000 зап, и позволяет добавлять до 28 тысяч зап/сек.

    Как-то так...
    Конечно, ничего даром не даётся - например, чем больше пакет, тем больше требуется RAM.
     
  21. sobachnik

    sobachnik Старожил

    С нами с:
    20 апр 2007
    Сообщения:
    3.380
    Симпатии:
    13
    Адрес:
    Дмитров, МО
    Тут ещё стоит упомянуть конкретное железо, на котором тестировалось, наверно :)
     
  22. Chushkin

    Chushkin Активный пользователь

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Всё "стандартно": Win7 x64, Intel i7-2600K, HD WD green 2T, RAM 8G. Нагрузка на проц ~12%.
    Диск самое тормозное место, - хорошо видно во втором тесте.
    п.с.
    Естественно, число записей в каждом конкретном случае будет разное. Например, если добавлять x32 число, то скорость в записях/сек может быть намного больше. А если текст в мегабайт, то и 20 з/сек может не быть. Плюс движок, +кодировка, +индексы и т.п. Железо конечно тоже влияет.
     
  23. MaXyC_Web_Studio

    MaXyC_Web_Studio Активный пользователь

    С нами с:
    31 дек 2006
    Сообщения:
    678
    Симпатии:
    3
    Адрес:
    Новоуральск
    чувствую в этом топике силу русского ума!
     
  24. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    а я вообще не понимаю о чем этот топик. если можешь сдампить базу то начерта тебе скрипт. если надо делать регулярно, то опять-таки это делается средствами самих серверов БД... хзхз. Но проблема есть, и её тут принято решать не задавая вопросов аначерта и зачемтебеэто.
     
  25. graf_vorontsov

    graf_vorontsov Активный пользователь

    С нами с:
    12 фев 2011
    Сообщения:
    183
    Симпатии:
    1
    Адрес:
    Украина, Харьков
    да, не пойму в чём затык .. переделал этот файл по другому, попробую без кавычек, а то они реально большую нагрузку похоже дают..
    и ещё я при загрузке в БД сразу пытался присваивать некоторым значениям другие.. вот и от этого нагрузка ещё шла, прийдётся разделить процессы эти

    не, с этим норм..
    Код (Text):
    1. set_time_limit(0);