2016-11-25 7 views
-1

У меня есть большой файл CSV, который мне нужно разбить на 4 части, а затем отправить данные в базу данных; проблема в том, что файл CSV может быть 1 ГБ + (и их может быть больше, чем один за раз), что создает различные временные задержки и проблемы с памятью.Разбор и обработка больших файлов

Мне нужна помощь в том, как я могу улучшить и ускорить процесс.

Файл, который я тестировал, содержит 45000 записей; ~ 10mb файл.

В настоящее время я загружаю файл в массив, который использует в 3 раза больше размера файла, поэтому для файла 10mb мы говорим о памяти 30 МБ; Я надеюсь уменьшить потребность в памяти 30 МБ, читая файл по строкам, но чтобы посмотреть на это.

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

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

Первоначально я попытался создать большую строку и отправить ее все в БД, но для записи 22k требуется около 2 ГБ памяти RAM; даже если этот процесс довольно быстрый, я все время теряю память.

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

Следующий шаг - посмотреть на создание файла mysql с полным списком запросов и импорт всего этого в MySQL с помощью функции импорта mysql.

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


Update 1 Прямо сейчас я не загрузить файл в память больше, и файл построчно процесса по линии. Для обработки большого файла не требуется много времени. Для обработки файла данных 500 МБ требуется несколько секунд.

На импорт в БД сторону вещей у меня есть два метода в месте, которое я пытаюсь:

  1. Использование PDO, оператор импорта в то время: занимает около 1 минуты для обработки файла данных 5 Мб; очень медленно
  2. Использование mysql < file.sql, импорт выполняется быстрее, занимает около 1 минуты для обработки файла данных 10 МБ; который лучше, но все же медленный. Компромисс заключается в том, что мне нужно сгенерировать файл .sql, который может быть очень большим, в настоящий момент файл данных 20 МБ приведет к файлу .sql размером около 600 МБ;

На данный момент я рассматриваю попытку «LOAD DATA INFILE», но мне нужно выполнить работу из-за нескольких таблиц, имеющих отношение один к одному, и мне нужно иметь последний вставленный идентификатор.


Update 2

загрузки данных Local входной_файл удалось решенных некоторые проблемы жестких дисков; Мне также пришлось использовать SplFileObject, чтобы упростить чтение файлов.Файл, который я создаю для Load Data Local Infile, по-прежнему довольно большой, но он намного лучше, чем раньше.

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

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

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

в первом цикле я имею для разбора параметров для таблицы B для улучшения производительности второго цикла.

У меня есть довольно много foreach/for операторов во втором цикле, из-за которых второй цикл занимает x4 как можно дольше.

Производительность на текущий момент следующим за 10MB файла:

  • Первый цикл 6 секунд
  • Второй контур 12 секунд
  • Среднее Общее время 18-20 второго

Однако производительность как представляется, ухудшается по мере увеличения файла, 40 МБ файла:

  • Первый цикл 30 секунд
  • Второй контур 60 второй
  • Среднее Общее время 90-100 секунд

Если я не параметры анализатора в первом цикле для выполнения Таблица B для 10Мб:

  • Первый цикл 3 секунды
  • Второй контур 16 секунд
  • Средний балл: 19-22 секунда

Производительность для файла с 40 Мбайт велика в первом цикле, но ужасно во втором цикле.

Без каких-либо Foreach петель в первом и втором цикле потребуется около 3-4 секунд, чтобы обработать 10MB

Пример цикла Еогеасп в первом цикле, который организует параметры Таблица B:

public function parseRawParam($line, $titles) { 
     $params = []; 

     $line = str_replace("\n", "", $line); 
     $rows = explode(",", $line); 

     for($row_i = 4; $row_i < count($rows); $row_i++) { 
      if(strlen(trim($rows[$row_i])) < 1) { 
       break; 
      } 

      $params[$titles[$row_i]] = $rows[$row_i]; 
     } 

     return $params; 
    } 

$ линия может иметь параметры в любом месте от 4 и ДО 160.

Второго контура имеет цикл Еогеаспа, который обрабатывает вставку Params и это то, что занимает много времени:

public function insertParam($record_id, $params) { 
     $sql = ""; 
     foreach ($params as $param => $value) { 
      $sql = '"' . $record_id . '","' . str_replace("'", "\'", trim($param)) . '","' . trim($value) . '";'; 
     } 
     return $sql; 
    } 

Я смотрел на производительность между различными версиями PHP, а PHP7 намного быстрее не сопоставлялся с php 5.6, поэтому я ищу обновить версии PHP и повысить производительность.

+0

исследования LOAD DATA INFILE – e4c5

+0

Попробуйте кусок его ...? Это компромисс в пространстве. Сколько места у вас есть или сколько времени вы готовы пожертвовать. 1 строка за запрос → очень медленная, 22 тыс. Строк на запрос → быстрая, но огромная потребность в памяти. Оптимальное решение → где-то между ними. – deceze

ответ

0

Для базы данных вам необходимо использовать LOAD DATA INFILE. Вы должны иметь возможность создавать текстовый файл строк для массового вставки в таблицу базы данных за один раз, если вы делаете какие-либо изменения данных в своих CSV-данных. Это должно уменьшить блокировку для одиночных вставок строк таблицы, которые являются дорогостоящими.

http://dev.mysql.com/doc/refman/5.7/en/load-data.html

+0

проблема, которая у меня есть, - это файл csv, содержащий данные для пяти разных таблиц; два из которых имеют отношение «один-много», и мне нужно использовать последний вставленный идентификатор. – user3402600

+0

. Не просите ли вы сделать слишком много за один удар. Может ли проблема сломаться там, где выполняются эти другие вставки. Коллекция идентификаторов собирается в одном SELECT, а затем хранится в массиве и искажается, а не ударяет по db для каждого из них? Всегда существует более простое решение, если оно разбито. – Jimbo

+0

В настоящее время одним из решений, которое я рассматриваю, является использование триггеров. Соответствующие таблицы: записи и параметры; 1 может иметь много параметров. План состоит в том, чтобы добавить дополнительный столбец в таблицу «записей», в которой будут храниться параметры; при вставке строки триггер будет каким-то образом захватить «параметры» из ячейки и вставить новые строки в таблицу «параметры». Другое решение состоит в том, чтобы дважды прокручивать данные, что вы сказали, первый цикл создает sql-файл для таблицы «records»; после завершения загрузки данных infile мы снова зацикливаем и создаем файл для «параметров»; Проблема теперь в дисковой памяти – user3402600

Смежные вопросы