Надо оптимизнуть код. варианты

zyablik

Новичок
Надо оптимизнуть код. варианты

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

опишу ситуацию:
есть файл данных (csv, разделитель точка с запятой) в котором хранится:
- название подставной фирмы отправления
- название станции отправления на железной дороге
- название станции прибытия на железной дороге
- название подставной фирмы прибытия
- разные другие данные

есть двухуровневые справочники соотвествий, в готорых определенны
города к которым относятся станции
действительные названия фирм к которым относятся подставные названия

задача, залить файл в базу с установленными значениями справочников

допустим в исходном файле
фирма отправления | станция отправления | станция прибытия | фирма прибытия
"рога и копыта" | курская | новосиб-гл. | "золотой бык"

итп..

в справочнике имеем соответствие значений
фирма "ВЕСЫ"
имеет подставные фирмы
- "рога и копыта"
- "р.и.г"
итп

фирма "ИЛЬФ"
- "золотой бык"
- "бычок"
итп

города

Москва
- курская
- рижская

Новосибирск
- новосиб-гл.
- новосиб-колц.


надо распарсить, положить в таблицу в таком виде
"рога и копыта" | курская | новосиб-гл. | "золотой бык" | "Весы" | "Москва" | "Новосибирск" | "Ильф"

у каждого справочника имеется свой ключ.
значения справочников связанны по парентИД

не у каждого значения в исходном файле есть значение из справочника.
в исходном файле может быть записанно "новосиб-гл" без точки, или наоборот с точкой, или вообще в кавычках, но это надо обратотать.. тоесть найти совпадения. максимизировать находжение соответствий.


ваши советы и решения.. задача на самом деле реализованна, но хочется оптимизировать. тк
файл из 400000 строк обрабатывается около 15 минут, что есть очень долго.


З.Ы.
рассматриваю варианты с использованием XML в качестве файла справочников вместо таблицы MySQL...
З.З.Ы. с XML знаком поверхностно вследствии чего не знаю какие резульатты по скорости может дать такой пепеход
 

zyablik

Новичок
парсилка запускается вручную
например при внесении новых значений справочников.

бывает и по 10 раз в день.

человеческий фактор. 15 мин ждать при внесении нового значения - неприятно...
 

zyablik

Новичок
а зачем ждать-то? чего ждать?
файл из 400000 строк обрабатывается около 15 минут, что есть очень долго.
парсилка которая генерит статические данные. пока парсилка полностью не отработает, статические данные недоступны/неверны, тк не полные. соотвественно приходится ждать полной обработки данных. и дальше можно работать
задача на самом деле реализованна, но хочется оптимизировать.
 

Фанат

oncle terrible
Команда форума
парсилка которая генерит статические данные
ух ты! а я не догадался...
я думал, там приплод Урюпинской птицефабрики подсчитывают.

А ждать-то зачем? какого результата? Появления в браузере букв "Окей"?
 

zyablik

Новичок
Фанат
ты гений!
именно букав ОК!

скрипт выполняется 15 минут, что непонятного?

а по существу варианты можешь предложить?
 

kruglov

Новичок
Померяйте, какие блоки больше всего тормозят, их и оптимизируйте.
 

zyablik

Новичок
вообщемто тк особых вариантов предложенно не было то расскажу как было сделанно

1. файл просто загонялся в некую таблицу аля временной
2. запускался скрипт который в цикле пробегал по всем строкам искал соотвествия и кидал во вторую таблицу

процесс обработки из временной таблицы во вторую как раз и тормозит больше всего

вот собственно код
PHP:
$rec= mysql_query("SELECT ElementID,station_prib,station_otpr,firm_otpr,firm_prib,rail,gruz,dat FROM `temp`");
while($records = mysql_fetch_array($rec))
{
	$station_prib = mysql_fetch_object(mysql_query("SELECT ParentID,value FROM term WHERE value LIKE '".$records["station_prib"]."'"));
	$city_prib = mysql_fetch_object(mysql_query("SELECT ShortName FROM wr_main WHERE ElementID = '".$station_prib->ParentID."'"));
	$station_otpr = mysql_fetch_object(mysql_query("SELECT ParentID,value FROM term WHERE value LIKE '".$records["station_otpr"]."'"));
	$city_otpr = mysql_fetch_object(mysql_query("SELECT ShortName FROM wr_main WHERE ElementID = '".$station_otpr->ParentID."'"));
	$frm_alias = mysql_fetch_object(mysql_query("SELECT ParentID,ElementID FROM term WHERE value like '".$records[firm_otpr]."'"));
	$firm_otpr = mysql_fetch_object(mysql_query("SELECT ShortName FROM wr_main WHERE ElementID = '".$frm_alias->ParentID."'"));	
	$query_string = "INSERT INTO `temp2` VALUES(
			'".$records["ElementID"]."',
			'".$records["station_otpr"]."',
			'".$records["firm_otpr"]."',
			'".$records["gruz"]."',
			'".$records["rail"]."',
			'".$records["station_prib"]."',
			'".$records["firm_prib"]."',
			'".$records["dat"]."',
			'".$city_otpr->ShortName."',
			'".$city_prib->ShortName."',
			'".$firm_otpr->ShortName."',
			''
			);";
	mysql_query($query_string);
}
больше всего смущает постоянное обращение к справочнику.

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

З.Ы.
именно этот кусок больше всего и тормозит...
 

Фанат

oncle terrible
Команда форума
kruglov
а какой смысл-то? ну, оптимизирует он в два раза.
будет не 15 минут, а 7. это кардинально улучшит ситуацию с ожиданием?
насчет предворительного формирования хешей уже думал, пока не решился сделать, тк поюсь что упадет точность определения города и фирмы для каждой записи
ого!
это как?
ты гений!
именно букав ОК!
спасибо =)
а зачем их ждать?
 

Dreammaker

***=Ф=***
Хм, тут ещё есть вопросы почему тормозит?

400 000 строк умножить на 6 запросов да ещё и некоторые с LIKE - это ж придумать ещё нужно.. :)

Посмотреть в сторону INSERT ... SELECT...
SELECT'ом вытянивать нужные данные использовать AND..

По возможности избавится от LIKE'ов

Таким образом скрипт (вытягивание данных (с помощью LOAD DATA INFILE), обработка строковыми функциями MySQL + вставка данных с помощью INSERT... SELECT... ) у меня занимало несколько секунд процессорного времени на виртуальном хостинге.. Задача примерно похожая была. Записей было 300 000+
 

zyablik

Новичок
Автор оригинала: Фанат
kruglov
а какой смысл-то? ну, оптимизирует он в два раза.
будет не 15 минут, а 7. это кардинально улучшит ситуацию с ожиданием?

ого!
это как?

спасибо =)
а зачем их ждать?
собстна перед выборкой из временной таблицы пробежать справочкини и набрать многомерный асоциативный массив
while ()
вместо того чтобы делать селект из справочкиков искать значение в набранном массиве...

насколько быстрее это будет - надо тестить

можно подойсти с другой стороны..
можно опять же пробегать все справочники.
и по значениям справочкиков апдейтить все записи с совпадающими значениями
аля
PHP:
$q = mysql_query("SELECT * from dict")
while ($result = mysql_fetch_array($q))
{
   mysql_query("UPDATE temp SET city_otpr ='".$result["value"]."' WHERE station_otpr = '".$result["key"]."'  ") // это приблизительный запрос.. уже не помню какие там поля
}
-~{}~ 24.03.06 00:32:

Автор оригинала: Dreammaker
Хм, тут ещё есть вопросы почему тормозит?

400 000 строк умножить на 6 запросов да ещё и некоторые с LIKE - это ж придумать ещё нужно.. :)

Посмотреть в сторону INSERT ... SELECT...
SELECT'ом вытянивать нужные данные использовать AND..

По возможности избавится от LIKE'ов

Таким образом скрипт (вытягивание данных (с помощью LOAD DATA INFILE), обработка строковыми функциями MySQL + вставка данных с помощью INSERT... SELECT... ) у меня занимало несколько секунд процессорного времени на виртуальном хостинге.. Задача примерно похожая была. Записей было 300 000+
всех тонкостей оператора LIKE не знаю, использую для того чтобы по возможности исключить человеческий фактор когда например вместо
НОВОСИБ-СР. будет новосиб-сп
или чтото подобное

а можно для идиотов про LOAD DATA INFILE и обработка строковыми функциями MySQL + вставка данных с помощью INSERT... SELECT...

например кусок функционируюго кода??? примера было бы достаточно ))))
 

Dreammaker

***=Ф=***
Код коммерческий :) сейчас на двух сайтах работает.

Насчёт LOAD DATA [LOCAL] INFILE можно почитать в мане. Если на хостинге эта конструкция разрешена очень поможет - закачивает в базу аж свистит. То есть решается проблема долгого парсинга.

Насчёт остального: присмотрелся я к коду - структура самой базы неидеальна. Разные по своей сути объекты засунуты в одну таблицу. Что скорее всего помешает использовать мой способ. Так ли это на 100%, не знаю, нужно больше времени на анализ и продумывание.

На счёт INSERT... SELECT... тоже можно в мане почитать.

Я бы в этом случае (если ещё не поздно, хотя уже наверное, да..) перепроектировал базу, чтобы между объектами стояли прямые зависимости, а не нужно було достававать через .. кучу селектов.

из попутных советов: попробуй убрать индексы с таблицы temp2, если они там есть, при таких больших объёмах вставляемых данных они мешают, а не помогают.
 

zyablik

Новичок
Автор оригинала: Dreammaker
Код коммерческий :) сейчас на двух сайтах работает.

Насчёт LOAD DATA [LOCAL] INFILE можно почитать в мане. Если на хостинге эта конструкция разрешена очень поможет - закачивает в базу аж свистит. То есть решается проблема долгого парсинга.

Насчёт остального: присмотрелся я к коду - структура самой базы неидеальна. Разные по своей сути объекты засунуты в одну таблицу. Что скорее всего помешает использовать мой способ. Так ли это на 100%, не знаю, нужно больше времени на анализ и продумывание.

На счёт INSERT... SELECT... тоже можно в мане почитать.

Я бы в этом случае (если ещё не поздно, хотя уже наверное, да..) перепроектировал базу, чтобы между объектами стояли прямые зависимости, а не нужно було достававать через .. кучу селектов.

из попутных советов: попробуй убрать индексы с таблицы temp2, если они там есть, при таких больших объёмах вставляемых данных они мешают, а не помогают.
Спасибо за советы! кое что воспринял, кое что переделаю.

заинтересовался лоад дата, понял как работает, уже хотел было переписать, да к сожалению при парсинге дополнительная обработка данных идет, кавыки лишние убираются, пробелы, апострофы, итп мусор..

насчет структуры:
предполагаю что просто сложилось неверное впечатление о структуре.
структура это CMS в которую дописался модуль

есть индексная таблица в которой хранятся ключевые параметры объектов (могу неправильно выразиться, или запустаться в терминах)
есть системные таблицы управляющие характеристиками объектов
есть объектные таблицы

например

таблица wr_main
ElementID | ParentID | ElementOrder | ElementName | ShortName | ItemID | ...

таблица справочкиков term
ElementID | ParentID | ElementOrder | ElementName | value

при обработке в таблицу temp2 в качестве значений и кладется ключ ShortName

в качестве решения видимо добавлю поле ShortName в таблицу термс
будет проще, упроститься алгоритм => ускрение работы

убрать индексы - ускорить обработку, но замедлить последующие обращения к финальной таблице..
короче в данном случае не могу от них отказаться. на будущее воспринял как ценный совет ))

2 Фанат:
не сразу понял о чем ты )))
видимо ты прав, справочники на то и справочники чтобы содержать точные значения , а не семантический поиск осуществлять...
придется повторно объяснять людям что если чтото не появляется в статистике то это они неточные значения завели. отсюда и недочеты
 

Dreammaker

***=Ф=***
>да к сожалению при парсинге дополнительная обработка данных идет, кавыки лишние убираются, пробелы, апострофы, итп мусор..

Вот для этого я использовал строковые функции MySQL.

С помощью LOAD DATA INFILE загнал во временную таблицу. А потом прошёлся по нему удаляя ненужное и одновременно заганяя данные в нужную таблицу что-то типа..

PHP:
$sql="INSERT INTO table2 (value1, value2, value3) 
     SELECT ".symReplace().",  value4, value5 FROM table";


function symReplace(){
   //Подготовливаем кусок запроса для вырезания всех небуквенно-цифровых символов из столбца. 
   $str='';
    for ($i=32; $i<48;$i++) {
        $str.=chr($i);
    }
    for ($i=58; $i<65;$i++) {
        $str.=chr($i);
    }
    for ($i=91; $i<97;$i++) {
        $str.=chr($i);
    }
    for ($i=123; $i<127;$i++) {
        $str.=chr($i);
    }
    $s='value6';
    for ($i=0;$i<33;$i++) { 
        $s='replace('.$s.',"'.$str[$i].'","")';
    }
    return $s;
}
Код не блещёт красотой, но работает :) И этот путь оказался более быстрым, чем резать символы на стороне пхп. Добавлю: в моём случае.

>насчет структуры:
предполагаю что просто сложилось неверное впечатление о структуре.
структура это CMS в которую дописался модуль

код о ктром я говорил - это код компонента для СМС Мамбо.. Всегда можно выйти за границы навязываемые нам матрицей :)

>убрать индексы - ускорить обработку, но замедлить последующие обращения к финальной таблице..

Нужно оценить насколько теряется скорость. Возможно не на так уж и много. В моём случае скорость хоть и уменшилась, но вихуально это не было заметно.

update: при делании примера была сдлана опечатка.. Поправил.
 

zyablik

Новичок
индексы достаточно сильно влияют.
с 50-60 секундного запроса до 5-10 секунд сокращается время обработки в последующем
 

Dreammaker

***=Ф=***
zyablik, хм, что-то видать не так в Датском Королевстве.

5-10 секунд это очень много. Видно и там проблемы есть.
 

Wicked

Новичок
zyablik
парсилка которая генерит статические данные. пока парсилка полностью не отработает, статические данные недоступны/неверны, тк не полные. соотвественно приходится ждать полной обработки данных. и дальше можно работать

если эти даные во время работы парсера нужны только для чтения (например, для отображения онлайн-юзерам), то можно сделать так. Парсер производит аккумулирует результаты в другие таблицы, а по окончании происходит подмена рабочих таблиц этими другими.
 

zyablik

Новичок
данные нужны для того чтобы вывести все города и сумму значений других столбцов
с различной детализацией

в исходных данных есть еще столбцы. stolbec1, stolbec2

например надо вывести все города и у каждого города подсчитать сумму значений первого столбца, сумму значений второго, и сумму (SUM(stolbec1)+SUM(stolbec2))

в дальнейшем надо иметь возможность открыть конкретный город, и показать все записи, и вывести сводный отчет по всем фирмам в этом городе

UPD:
данные сумарные значения надо иметь возможность выбирать за разные периоды времени
 

vGhost

Новичок
А зачем обрабатывать одни и теже строки по сто раз?
была у меня подобная задача.. С парсингом бызы песен аля делит аудиосторе и тд.
1)Вычисляй от строки (из csv файла) CRC32 перед её разбором и храни его в базе, дальше просто пропускаеш повторную обработку строк црк которых не изменилось..
2) используй кеширование все названия фирм или чего там можно привести к определённому общиму шаблону (не обязательно менять текст при этом) т.е. удаляеш знаки припинания окончания гл или чё там у тебя, опускаеш в нижний регистр и вычисляеш CRC32 от полученной строки и держи в отдельной таблице соответствие ID фирмы или чего там - crc по обоим индекс таблица с FIXED ROWS работает раз в 100 быстрее чем поиск по like но перед работой я бы (если есть много памяти допустим 50мб) сделал её выборку в массив и не базу опрашивал при поиске.

3) юзай ротацию баз.. Т.е. держи 2 базы - актуальная и та с которой ведётся работа скриптом.. Скрипт закончил работу базы поменял...

И т.д.

З.Ы. чтоб юзер в браузере не ждал - юзай отправку процесса в бекграунд (к сожалению если сервак не ваш хостеры обычно запрешают выполнение шел команд из пхп)
 
Сверху