Лучшие практики для импорта больших файлов CSV - PullRequest
23 голосов
/ 12 ноября 2010

Моя компания каждый месяц получает набор файлов CSV, полный информации о банковском счете, который мне нужно импортировать в базу данных.Некоторые из этих файлов могут быть довольно большими.Например, один занимает около 33 МБ и около 65 000 строк.

Сейчас у меня есть приложение Symfony / Doctrine (PHP), которое читает эти CSV-файлы и импортирует их в базу данных.В моей базе данных около 35 разных таблиц, и в процессе импорта я беру эти строки, разделяю их на составляющие объекты и вставляю в базу данных.Все это прекрасно работает, за исключением того, что оно медленно (каждая строка занимает около четверти секунды) и использует много памяти.

Использование памяти настолько плохо, что мне приходится разделять своиCSV файлы.Файл с 20 000 строками едва справляется. К тому моменту, когда он подходит к концу, у меня примерно 95% использования памяти.Импортировать этот файл из 65 000 строк просто невозможно.

Я обнаружил, что Symfony является исключительной средой для создания приложений, и обычно я бы не стал использовать что-либо еще, но в этом случае я готов броситьвсе мои предубеждения выходят в окно во имя спектакля.Я не привержен какому-либо конкретному языку, СУБД или чему-либо еще.

Переполнение стека не любит субъективные вопросы, поэтому я постараюсь сделать его как можно более субъективным: для тех из васу вас есть не только мнение, но и опыт импорта больших файлов CSV , какие инструменты / практики вы использовали в прошлом , которые были успешными?

Например, выпросто используйте Django ORM / OOP, и у вас не было проблем?Или вы читаете весь файл CSV в память и готовите несколько громадных INSERT заявлений?

Опять же, я хочу не просто мнение, а то, что действительно работало для вас в прошлом.

Редактировать: я не просто импортирую таблицу CSV из 85 столбцов в одну таблицу базы данных из 85 столбцов.Я нормализую данные и помещаю их в десятки разных таблиц.По этой причине я не могу просто использовать LOAD DATA INFILE (я использую MySQL) или любую другую функцию СУБД, которая просто читает в файлах CSV.

Кроме того, я не могу использовать какие-либо решения для Microsoft.

Ответы [ 10 ]

17 голосов
/ 12 ноября 2010

Простите, если я не совсем правильно понимаю вашу проблему, но кажется, что вы просто пытаетесь получить большой объем данных CSV в базу данных SQL.Есть ли какая-то причина, по которой вы хотите использовать веб-приложение или другой код для обработки данных CSV в операторах INSERT?Я успешно импортировал большие объемы данных CSV в SQL Server Express (бесплатная версия), используя SQL Server Management Studio и используя инструкции BULK INSERT.Простая массовая вставка будет выглядеть так:

BULK INSERT [Company].[Transactions]
    FROM "C:\Bank Files\TransactionLog.csv"
    WITH
    (
        FIELDTERMINATOR = '|',
        ROWTERMINATOR = '\n',
        MAXERRORS = 0,
        DATAFILETYPE = 'widechar',
        KEEPIDENTITY
    )
GO
11 голосов
/ 12 ноября 2010

У меня была точно такая же проблема около 2 недель назад. Я написал несколько .NET для вставки ROW BY ROW, и по моим подсчетам с количеством данных, которые у меня были, потребуется около недели, чтобы сделать это таким образом.

Поэтому вместо этого я использовал построитель строк, чтобы создать один ОГРОМНЫЙ запрос, и сразу отправил его в мою реляционную систему. Это заняло от недели до 5 минут. Теперь я не знаю, какую реляционную систему вы используете, но с огромными запросами вам, вероятно, придется настроить ваш параметр max_allowed_packet или аналогичный.

5 голосов
/ 23 сентября 2014

Во-первых: 33 МБ не большой. MySQL может легко обрабатывать данные такого размера.

Как вы заметили, построчная вставка идет медленно. Кроме того, использование ORM еще медленнее: накладные расходы на создание объектов, сериализацию и так далее. Использование ORM для 35 таблиц делает еще медленнее . Не делай этого.

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

Помимо этого, объем INSERT (column, ...) VALUES ... также работает. Не угадайте, каким должен быть размер вашей партии; время эмпирически , так как оптимальный размер пакета будет зависеть от вашей конкретной настройки базы данных (конфигурации сервера, типов столбцов, индексов и т. Д.)

Bulk INSERT не будет таким быстрым, как LOAD DATA INFILE, и вам все равно придется написать скрипт для преобразования необработанных данных в пригодные INSERT запросы. По этой причине я бы, вероятно, сделал LOAD DATA INFILE, если это вообще возможно.

4 голосов
/ 31 августа 2016

FWIW следующие шаги вызвали огромное ускорение моего LOAD DATA INFILE:

SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
SET sql_log_bin = 0;
#LOAD DATA LOCAL INFILE....
SET UNIQUE_CHECKS = 1;
SET FOREIGN_KEY_CHECKS = 1;
SET SESSION tx_isolation='READ-REPEATABLE';

См. Статью здесь

2 голосов
/ 03 марта 2015

Вы можете использовать Mysql LOAD DATA INFILE statemnt, он позволяет вам читать данные из текстового файла и очень быстро импортировать данные файла в таблицу базы данных.

LOAD DATA INFILE '/opt/lampp/htdocs/sample.csv' INTO TABLE discounts FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 ROWS (title,@expired_date,discount) SET expired_date = STR_TO_DATE(@expired_date, '%m/%d/%Y');

для получения дополнительной информации: http://dev.mysql.com/doc/refman/5.5/en/load-data.html и http://www.mysqltutorial.org/import-csv-file-mysql-table/

2 голосов
/ 10 октября 2011

Мне не нравятся некоторые другие ответы:)

Я обычно делал это на работе.

Вы пишете программу для создания большого сценария SQL, полного операторов INSERT.по одному в строке.Чем вы запускаете скрипт.Вы можете сохранить скрипт для дальнейшего использования (дешевый журнал).Используйте gzip, и он уменьшится до 90%.

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

Вы можете сделать несколько сотен вставок за транзакцию или все за одну транзакцию, это ваше дело.

Python - хороший язык для этого, но я уверен, что с php тоже все хорошо.

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

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

1 голос
/ 05 января 2017

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

#Method
public function getFileRecords($params)
{
    $fp = fopen('../' . $params['file'] . '.csv', 'r');
    //$header = fgetcsv($fp, 1000, ','); // skip header

    while (($line = fgetcsv($fp, 1000, ',')) != FALSE) {
        $line = array_map(function($str) {
            return str_replace('\N', '', $str);
        }, $line);

        yield $line;
    }

    fclose($fp);

    return;
}

#Implementation
foreach ($yourModel->getFileRecords($params) as $row) {
    // you get row as an assoc array;
    $yourModel->save($row);
}
1 голос
/ 12 ноября 2010

Если вы используете Sql Server и имеете доступ к .NET, вы можете написать быстрое приложение для использования класса SQLBulkCopy . Я использовал это в предыдущих проектах для очень быстрого ввода большого количества данных в SQL. Класс SQLBulkCopy использует BCP для SQL Server, поэтому, если вы используете что-то отличное от .NET, возможно, стоит посмотреть, открыта ли вам эта опция. Не уверен, что вы используете БД, отличную от SQL Server.

0 голосов
/ 30 июля 2014

Мне нужно время от времени делать это (импортировать большие нестандартизированные CSV-файлы, где каждая строка создает дюжину или около того связанных объектов БД), поэтому я написал скрипт на python, в котором я могу указать, что и куда и как все это связано , Затем скрипт просто генерирует операторы INSERT.

Вот оно: csv2db

Отказ от ответственности: я в основном нуб, когда дело доходит до баз данных, поэтому, возможно, есть лучшие способы сделать это.

0 голосов
/ 21 апреля 2013

Я читаю файл CSV, который имеет около 1М записей и 65 столбцов.Каждая 1000 записей, обработанных в PHP, содержит одну большую жирную инструкцию MySQL, которая входит в базу данных.Написание не занимает много времени.Это анализ, который делает.Объем памяти, используемой для обработки этого несжатого файла 600 МБ, составляет около 12 МБ.

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