Помогите оптимизировать добавление данных

SiZE

Новичок
Помогите оптимизировать добавление данных

Есть задача внести из XML файла данные в MySQL.

Условия: внести все полученные данные, предварительно проверив на уникальность.

XML обрабатываю через PHP, функцией simplexml_load_file(). Далее формирую массив. Получается порядка 100 000 — 300 000 записей для вставки. Раньше данных было мало, порядка 1000 - 3000 записей. И поэтому достаточно было обойти циклом массив. Сделать для каждой записи SELECT COUNT(id)... с условием проверки на уникальность и в зависимости от результат сделать INSERT или UPDATE.

С увеличением количества записей ситуация координально поменялась при такой вставке данных скрипт начинает жрать от 100 метров до гига и выше. А таких файлов несколько тысяч и сервак вообщем не выдерживает нагрузки.

В кратце код такой:
PHP:
<?
$xml = simplexml_load_file (file.xml);
newArr = array ();
foreach ($xml as $v) {
  $newArr['field1'] = $v['field1'];
  $newArr['field2'] = $v['field2'];
  $newArr['field3'] = $v['field3'];
}

foreach ($newArr as $k => $v) {
  $sql = "INSERT INTO table2 SET field1='$v['field1']'";
  $db->exec ($sql);
  $link = $db->insert_id(); // mysql_insert_id();
  $sql = "SELECT COUNT(id), id FROM table1 WHERE field1='$v['field1']' AND field2='$v['field2']' LIMIT 1";
  $res = $db->exec ($sql);
  if ($res) {
    $sql = "INSERT INTO table1 SET field1='$v['field1']', field2='$v['field2']', field3='$v['field3']', field4='$link'";
    $db->exec ($sql);
  } else {
    $sql = "UPDATE table1 SET field3='$v['field3']' WHERE id='$res['id']'";
    $db->exec ($sql);
  }
}
?>
1. Были мысли создать файл CSV и выгрузить данные в базу, тогда я не знаю как проверить на уникальность записи.
2. Или выгрузить в файл CSV и загрузить во временную таблицу, дубликаты удалить а таблицы слить. Но особо пока не представляю, как слить таблицы.
3. Или пройтись по всему массиву выпонить для каждой записи SELECT COUNT(id) ... удалить из массива дубликаты, на лету создать INSERT INTO table VALUES (1,2,3), (4,5,6), (7,8,9),..., n и загрузить. Не знаю как только MySQL отреагирует если количество VALUES будет порядка 100 000 — 300 000.

Мб что то еще?
 

brook

Новичок
Re: Помогите оптимизировать добавление данных

Во первых откажись от simplexml - он грузит файл целиком в память.
Юзай DOMDocument с Xpath и ранжируй там в селекторах item() - возможно это будет работать быстрее, кто сталкивался - покритикуйте.
Делай массовые вставки - конечно не по 10000 - делай пачками, например по 500.
 

zerkms

TDD infected
Команда форума
PHP:
$sql = "SELECT COUNT(id), id FROM table WHERE field1='$v['field1']' AND field2='$v['field2']' LIMIT 1";
  $res = $db->exec ($sql);
  if ($res) {

  } else {
    $sql = "UPDATE table SET field1='$v['field1']', field2='$v['field2']' WHERE id='$res['id']'";
    $db->exec ($sql);
  }
в чём сакральный смысл этого кода?
 

dr-sm

Новичок
1. insert on duplicate key update | insert ignore
2. start tran / commit по несколько тысяч записей.
3. парсить огромные XML'и лучше через SAX.
 

zerkms

TDD infected
Команда форума
dr-sm
на мой коммент посмотри :) что он там вообще апдейтит? :)))
 

dr-sm

Новичок
zerkms, ну он там код вроде вкратце привел.
так то да, момент загадочный )))
 

Alexandre

PHPПенсионер
Во первых откажись от simplexml - он грузит файл целиком в память.
Юзай DOMDocument с Xpath и ранжируй там в селекторах item() - возможно это будет работать быстрее, кто сталкивался - покритикуйте.
для больших XML-файлов надо использовать XMLReader
все сливать в промежуточную таблицу (желательно без индексов)
удалять дубликаты
мержить с основной тбл
 

SiZE

Новичок
в чём сакральный смысл этого кода?
Что бы понять суть действий производимых скриптом. То, что в примере он апдейтит тоже самое, так это только пример, для краткости. Есть другие поля, по которым не сравнивается уникальность записи, но обновляются при UPDATE. Вобщем думаю это не столь важно :)

-~{}~ 24.09.09 15:31:

Добавлю немного информации. Загрузка xml файла, обход XML дерева с созданием необхоимого массива занимает 15 секунд на 170 000 записей, пиковое потребление памяти 8-9 Мб. Для меня это время приемлемо и код минимален.
Загрузка в базу 170 000 записей в формате описанном выше (SELECT ... INSERT) занимает 20-25 минут и скрипт на PHP отжирает 500-1500 Мб памяти.

-~{}~ 24.09.09 15:32:

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

zerkms

TDD infected
Команда форума
пиши в файл, в соответствующем формате и потом LOAD DATA INFILE
 

SiZE

Новичок
Автор оригинала: zerkms
пиши в файл, в соответствующем формате и потом LOAD DATA INFILE
А как быть с тем, что мне для каждой записи надо получить ID из другой таблицы? :(
 

zerkms

TDD infected
Команда форума
SiZE
ну а что тут поделаешь? выгребай сразу побольше пачки, через IN(...)
 
Сверху