Здравствуйте, есть такая задачка: 1. имеем огромный файл xls (прайс) 2. база на мускуле 3. шаред хостинг (Nginx frontend + Apache backend) Что делаем: 1. через библиотеку превращаем xls -> csv 2. читаем файл через fgetcsv 3. проверяем на наличие товара в БД, если нет товара, то записываем, если есть - обновляем сейчас скрипт на этом этапе жрет много памяти + проц (конечно, на этапе добавления в БД) как можно оптимизировать работу такого приложения?
Код (Text): $warehousQuery = $this->db->get('shops_warehous', 1); $warehousRow = $warehousQuery->row_array(); $handle = @fopen($fileCache, "r"); if ($handle) { $fopenLog = fopen($_SERVER['DOCUMENT_ROOT'] . "/excel_parser/" . $_POST['filename'] . ".log","w+") or die("Невозможно открыть / создать лог файл"); $counter = 0; while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { $connectValue = ($connectFieldName == 'id') ? intval($data[$connectFieldKey]) : trim($data[$connectFieldKey]); if (!empty($connectValue)) { $this->db->select('id'); $resultQuery = $this->db->get_where('shops_products', array ($connectFieldName => $connectValue), 1); $dataRow = Array (); foreach ($allFields as $keyField => $fieldName) { if ($fieldName != "id") { $fieldValue = trim($data[$keyField]); if ($fieldName == "price" || $fieldName == "old_price") { $fieldValue = floatval($fieldValue); } elseif ($fieldName == "warehous_val") { $fieldValue = preg_replace('/[^0-9]/', '', $fieldValue); } elseif ($fieldName == "name" && isset($_POST['settings_change_url']) && !empty($_POST['settings_change_url'])) { $dataRow['url'] = translit_url($fieldValue); } $dataRow[$fieldName] = $fieldValue; } } $dataProduct = $dataRow; unset($dataProduct['warehous_val']); if ($resultQuery->num_rows() == 1) { $resultRow = $resultQuery->row_array(); $this->db->where('id', $resultRow['id']); $this->db->update('shops_products', $dataProduct); if (isset($dataRow['warehous_val'])) { $this->db->where('product_id', $resultRow['id']); $this->db->delete('shops_warehous_data'); $this->db->insert('shops_warehous_data', Array ( 'product_id' => $resultRow['id'], 'warehous_id' => $warehousRow['id'], 'variant_id' => 0, 'count' => $dataRow['warehous_val'] )); } fwrite($fopenLog, 'Обновлен товар #' . $resultRow['id'] . "<br/>"); } elseif (isset($_POST['settings_add']) && !empty($_POST['settings_add'])) { if (!empty($dataProduct['name']) && !empty($dataProduct['price'])) $this->db->insert('shops_products', $dataProduct); $insertId = $this->db->insert_id(); if (isset($dataRow['warehous_val'])) { $this->db->insert('shops_warehous_data', Array ( 'product_id' => $insertId, 'warehous_id' => $warehousRow['id'], 'variant_id' => 0, 'count' => $dataRow['warehous_val'] )); } fwrite($fopenLog, 'Добавлен новый товар #' . $insertId . "<br/>"); } unset($resultQuery); unset($resultRow); // $counter++; // if ($counter == 20) break; } } fclose($fopenLog); fclose($handle);
Всем спасибо. Сделал следующее: 1. Все запросы на insert и update собрал в один 2. получаю все записи у которых не пустое связующее поле, загоняю значения в массив - избавляюсь от частых select'ов
если вы все данные либо добавляете либо обновляете- а почему бы перед началом не чистить таблицу .. а потом тупо инсертить помойму так быстрее будет
Если id заново добавляемого товара не изменится - то да, так лучше. Если изменится, то не вариант - на товар могут быть ссылки.
Кстати, для обновления/добавления есть http://phpclub.ru/mysql/doc/replace.html, если уж задача - оптимизировать, то логично будет перенести обновление на мускуль.
Замечания: REPLACE работает как DELETE + INSERT, то есть если у нас есть foreign keys с каскадным удалением, или триггеры на удаление, то всёпиздец! Тут больше подойдет INSERT... ON DUPLICATE KEY UPDATE Ну и, минуточку, раз мы всё равно все записи удаляем, то один раз TRUNCATE, а затем n раз INSERT будет гораздо быстрее чем n раз REPLACE. Короче, совет неудачный. И replace и insert...on duplicate расчитывают на совпадение первичного/уникального ключа. Если уникальность обеспечена автоинкрементом, который рождается в процессе накатывания данных, то у нас нет зацепок для подобной "оптимизации". Топикстартер вобщето плачет о большом расходе памяти. Если собирать данные в большие пучки для быстрой вставки, тогда память конечно тратится. Тут наверное надо искать компромисс, экспериментировать с размером "пучка". Проверять каждый id на стороне PHP это гарантированные тормоза! Стоит попробовать вставлять ВСЕ данные из файла во временную таблицу, затем одним запросом сливать ее в целевую с INSERT...ON DUPLICATE UPDATE. (Или INSERT...IGNORE -- автору видней) Кстати, если реализуешь трюк со временной таблицей, то LOAD DATA INFILE тебе в помощь! Удачи!
Бред полнейший. Такие операции не делаются через веб-интерфейс. Никакие апачи-хреначи тут не валялись. Из текстовых данных делается файл типа дампа с той лишь разницей что insert снабжен словом ignore и всеми необходимыми условиями. Теперь заходим в шелл, или если не есть - в хостинг до PMA, или что там есть и забиваем этот заброс в режиме импорта таблиц. А, так у вас там и id есть? В том cvs. Ну тогда это репликация и делается она еще раз по-другому. Догадаться реплицировать через скрипт - это могучий ход. Особенно впечатляет толпа советчиков как это лучше сделать. Добавлено спустя 6 минут 40 секунд: Все блин элементарно http://dev.mysql.com/doc/refman/5.0/en/replication-options- ... e-do-table Нет шелла? Дамп табли в локаль, там реплика и обратно на сервер. Добавлено спустя 1 минуту 2 секунды: Ежу понятно из csv сперва надо создать таблицу.
ты откуда взялся такой сердитый? кто сказал, что это репликация? топикстартер вроде спросил как быстро заливать csv в существующую таблицу. апач действительно непричем.