Оптимизация массивных операций MySQL INSERT

У меня есть приложение, которому нужно запускать ежедневный скрипт; ежедневный сценарий состоит из загрузки CSV-файла с 1 000 000 строк и вставки этих строк в таблицу.

Я размещаю свое приложение в Dreamhost. Я создал цикл while, который проходит через все строки CSV и выполняет запрос INSERT для каждой из них. Дело в том, что я получаю "500 Internal Server Error". Даже если я разобью его на 1000 файлов по 1000 строк в каждом, я не смогу вставить более 40 или 50 тысяч строк в один и тот же цикл.

Есть ли способ оптимизировать ввод? Я также рассматриваю возможность использования выделенного сервера; Как вы думаете?

Спасибо!

Педро


person Pedro    schedule 14.09.2010    source источник
comment
Я не могу вспомнить, есть ли в MySQL операции массовой вставки, но это, возможно, стоит изучить, а не 1000000 отдельных операторов вставки.   -  person FrustratedWithFormsDesigner    schedule 15.09.2010
comment
Вы должны прочитать следующее: stackoverflow.com/questions/3523831/   -  person RobertPitt    schedule 15.09.2010


Ответы (10)


Большинство баз данных имеют оптимизированный процесс массовой вставки — MySQL — это ФАЙЛ ЗАГРУЗКИ ДАННЫХ. синтаксис.

Чтобы загрузить файл CSV, используйте:

LOAD DATA INFILE 'data.txt' INTO TABLE tbl_name
  FIELDS TERMINATED BY ',' ENCLOSED BY '"'
  LINES TERMINATED BY '\r\n'
  IGNORE 1 LINES;
person OMG Ponies    schedule 14.09.2010
comment
+1, но я сомневаюсь, что это сработает в сценарии общего хостинга, поскольку сервер базы данных обычно отделен от веб-сервера и, следовательно, не будет иметь доступа к пользовательским файлам. - person casablanca; 15.09.2010
comment
@casablanca: PHP предоставляет интерфейс-оболочку IIRC, это всего лишь вопрос загрузки файлов и разрешений. - person OMG Ponies; 15.09.2010
comment
Если вы не можете загрузить файл данных на хост-сервер базы данных, используйте LOAD DATA LOCAL INFILE ..., после чего вы можете загрузить файл данных на клиентский конец соединения MySQL, который в данном случае является хостом приложения PHP. - person Bill Karwin; 15.09.2010

Вставьте несколько значений вместо того, чтобы делать

insert into table values(1,2);

do

insert into table values (1,2),(2,3),(4,5);

До соответствующего количества строк за раз.

Или выполните массовый импорт, который является наиболее эффективным способом загрузки данных, см.

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

person nos    schedule 14.09.2010

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

Я не использовал MySQL несколько лет, но у них есть очень хороший документ, в котором описывается, как ускорить вставку для массовых вставок: http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html

Несколько идей, которые можно почерпнуть из этого:

  • Отключить/включить клавиши вокруг вставок:

    ALTER TABLE tbl_name DISABLE KEYS; ALTER TABLE tbl_name ENABLE KEYS;

  • Используйте много значений в ваших операторах вставки.

    То есть: INSERT INTO table (col1, col2) VALUES (val1, val2),(.., ..),...

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

  • Запустите команду FLUSH TABLES еще до того, как вы начнете, чтобы убедиться, что нет ожидающих операций записи на диск, которые могут снизить производительность вставки.

Думаю, это ускорит дело. Я бы предложил использовать LOCK TABLES, но я думаю, что отключение ключей делает это спорным.

ОБНОВЛЕНИЕ

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

  • Убедитесь, что в вашей таблице нет данных, которые «конфликтуют» с загружаемыми новыми данными (если вы начинаете с нуля, здесь будет полезен оператор TRUNCATE).
  • Написание сценария для очистки ваших входных данных, чтобы исключить дублирование локально. Проверка дубликатов, вероятно, в любом случае будет стоить вам много времени в базе данных.
  • Если вы сделаете это, ENABLE KEYS не должен дать сбой.
person Mike Axiak    schedule 14.09.2010

Вы можете создать скрипт cronjob, который добавляет x записей в базу данных по одному запросу. Скрипт Cronjob проверит, не добавил ли последний импорт всех необходимых строк, он возьмет еще x строк.

Таким образом, вы можете добавить столько строк, сколько вам нужно.

Если у вас есть свой выделенный сервер, это проще. Вы просто запускаете цикл со всеми запросами на вставку.

Конечно, вы можете попробовать установить для time_limit значение 0 (если оно работает на DreamHost) или увеличить его.

person Vaidas Zilionis    schedule 14.09.2010

Ваш PHP-скрипт, скорее всего, завершается, потому что он превысил лимит времени скрипта. Поскольку вы находитесь на общем хосте, вам не повезло.

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

person casablanca    schedule 14.09.2010

Предложение OMG Ponies отличное, но я также «вручную» отформатировал данные в тот же формат, который использует mysqldump, а затем загрузил их таким образом. Очень быстро.

person mkoistinen    schedule 14.09.2010

Вы пробовали делать транзакции? Просто отправьте команду BEGIN в MySQL, сделайте все вставки, затем выполните COMMIT. Это значительно ускорит его, но, как сказал Касабланка, ваш скрипт, вероятно, тоже истекает.

person jonescb    schedule 14.09.2010

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

Я обнаружил, что в моей ситуации я не мог MySQL принять один большой оператор INSERT, но обнаружил, что если я разделю его на группы примерно по 10 000 INSERT за раз, как это было предложено nos, то он сделает свою работу довольно быстро. Следует отметить, что при выполнении нескольких INSERT, подобных этому, вы, скорее всего, достигнете ограничения времени ожидания PHP, но этого можно избежать, сбросив время ожидания с помощью set_time_limit($seconds), я обнаружил, что делая это после каждого успешного INSERT работал очень хорошо.

Вы должны быть осторожны при этом, потому что вы можете случайно попасть в цикл с неограниченным тайм-аутом, и для этого я бы предложил проверить, чтобы убедиться, что каждый INSERT был успешным, либо проверив ошибки, о которых сообщает MySQL, с помощью mysql_errno() или mysql_error(). Вы также можете отлавливать ошибки, проверяя количество строк, затронутых INSERT, с помощью mysql_affected_rows(). Затем вы можете остановиться после первой ошибки.

person chrispen    schedule 14.09.2010

Было бы лучше, если бы вы использовали sqlloader. Вам понадобятся две вещи: первый управляющий файл, в котором указаны действия, которые должен выполнять загрузчик SQL, и второй файл csv, который вы хотите загрузить. Вот ссылка ниже, которая поможет вам. http://www.oracle-dba-online.com/sql_loader.htm

person Tushar Sagar    schedule 05.08.2011

Перейдите в phpmyadmin и выберите таблицу, в которую вы хотите вставить.

На вкладке «Операции», а затем в разделе «Параметры таблицы» измените механизм хранения с InnoDB на MyISAM.

У меня когда-то была похожая задача. Хорошо тебе провести время.

person Ian Mbae    schedule 10.04.2014
comment
поэтому вы говорите, что MyISAM лучше, чем InnoDB для вставок, вы можете прочитать этот ответ stackoverflow.com/a/1102425/2652018 - person Steel Brain; 01.08.2014
comment
Я читал это, но на собственном опыте убедился в обратном! Все, что я делаю для массивных вставок ['до 30 000'], упаковываю их все в один запрос, и это работает как шарм. - person Ian Mbae; 24.09.2014
comment
вам нужно достичь некоторых ориентиров, приятель, это работает по-другому. InnoDB идеально подходит для частой записи, а MyISAM идеально подходит для частого чтения. (InnoDB тоже качается при чтении). Это говорит об опыте использования и вставки более 50 миллионов строк (база данных геоимен) - person Steel Brain; 24.09.2014
comment
Спасибо, дружище, я посмотрю на это - person Ian Mbae; 25.09.2014