Что быстрее: несколько одиночных INSERT или одна многострочная INSERT?

184

Я пытаюсь оптимизировать одну часть моего кода, которая вставляет данные в MySQL. Должен ли я связать INSERT, чтобы сделать одну огромную многорядную INSERT или несколько отдельных INSERT быстрее?

dusoft
источник

Ответы:

287

https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html

Время, необходимое для вставки строки, определяется следующими факторами, где числа указывают приблизительные пропорции:

  • Подключение: (3)
  • Отправка запроса на сервер: (2)
  • Запрос синтаксического анализа: (2)
  • Вставка строки: (1 × размер строки)
  • Вставка индексов: (1 × количество индексов)
  • Закрытие: (1)

Из этого должно быть очевидно, что отправка одного большого оператора избавит вас от 7 накладных расходов на оператор вставки, который в дальнейшем чтении текста также говорит:

Если вы одновременно вставляете много строк из одного и того же клиента, используйте операторы INSERT с несколькими списками VALUES, чтобы вставить несколько строк одновременно. Это значительно быстрее (во многих случаях быстрее), чем использование отдельных однострочных операторов INSERT.

mbarkhau
источник
27
Как этот ответ применяется, если несколько одиночных INSERT находятся в одной транзакции базы данных?
Ущипнуть
2
Сколько строк я могу вставить за один раз, используя один оператор вставки. это позволяет мне вставлять 10000 строк одновременно?
Нареш Рамолия
10
@Pinch Использование транзакции при выполнении ~ 1,5 тыс. Загрузок (вставка / обновление) уменьшило время, затрачиваемое на операцию, с ~ 1,5 до ~ 0,2 с. Или, другими словами, он сделал это на 86% быстрее по сравнению с однорядными вставками. Черт.
fgblomqvist
1
Примечание. Кажется, что в MSSQL все сильно отличается: stackoverflow.com/questions/8635818/…
marsze
Как насчет использования Prepared Statement для вставки повторяющихся множественных одиночных вставок?
Приябагус
151

Я знаю, что отвечаю на этот вопрос почти через два с половиной года после того, как он был задан, но я просто хотел предоставить некоторые точные данные из проекта, над которым я сейчас работаю, который показывает, что действительно делать несколько блоков VALUE на одну вставку - НАМНОГО быстрее, чем последовательные одиночные операторы INSERT блока VALUE.

Код, который я написал для этого теста в C #, использует ODBC для чтения данных в память из источника данных MSSQL (~ 19 000 строк, все считываются до начала любой записи), а также соединителя MySql .NET (Mysql.Data. *) Для Вставьте данные из памяти в таблицу на сервере MySQL с помощью подготовленных операторов. Он был написан таким образом, чтобы позволить мне динамически регулировать количество блоков VALUE на подготовленную INSERT (т. Е. Вставлять n строк за раз, где я мог отрегулировать значение n до запуска.) Я также выполнил тест несколько раз для каждого n.

Выполнение отдельных блоков VALUE (например, по 1 строке за раз) заняло 5,7 - 5,9 секунд. Другие значения следующие:

2 строки за раз: 3,5 - 3,5 секунды
5 строк за раз: 2,2 - 2,2 секунды
10 строк за раз: 1,7 - 1,7 секунды
50 строк за раз: 1,17 - 1,18 секунды
100 строк за раз: 1,1 - 1,4 секунды
500 строк за раз: 1,1 - 1,2 секунды
1000 строк за раз: 1,17 - 1,17 секунды

Так что да, даже просто объединение 2 или 3 записей обеспечивает резкое улучшение скорости (время выполнения сокращается с коэффициентом n) до тех пор, пока вы не достигнете где-то между n = 5 и n = 10, и в этот момент улучшение заметно падает, и где-то в диапазоне от n = 10 до n = 50 улучшение становится незначительным.

Надеюсь, что это поможет людям решить (а), следует ли использовать идею множественной подготовки, и (б) сколько блоков VALUE нужно создать для каждого оператора (при условии, что вы хотите работать с данными, которые могут быть достаточно большими, чтобы протолкнуть запрос за максимальный размер запроса для MySQL, который, я считаю, по умолчанию составляет 16 МБ во многих местах, возможно, больше или меньше в зависимости от значения max_allowed_packet, установленного на сервере.)

Джон Клоске
источник
1
Запрос на уточнение: ваше время «количество секунд в строке» или «общее количество секунд».
EngrStudent
3
Общее количество секунд - таким образом, количество секунд в строке делится на ~ 19 000 строк. Хотя это небольшое число, поэтому, возможно, число строк в секунду - лучший показатель, если вы ищете легко сопоставимое число.
Джон Клоске,
Между прочим, есть некоторый пример кода .NET для подхода, который я описал выше для моего соответствующего ответа: stackoverflow.com/questions/25377357/…
Джон Клоске
18

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

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

Предполагая один поток, это означает, что сервер должен синхронизировать некоторые данные на диск для КАЖДОЙ СТРОКИ. Необходимо подождать, пока данные достигнут постоянного места хранения (возможно, оперативной памяти с резервным питанием в вашем RAID-контроллере). Это по сути довольно медленно и, вероятно, станет ограничивающим фактором в этих случаях.

Я, конечно, предполагаю, что вы используете транзакционный движок (обычно innodb) И что вы не изменили настройки, чтобы уменьшить срок службы.

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

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

Существует предел, по которому он становится контрпродуктивным, но в большинстве случаев это не менее 10000 строк. Так что если вы упаковываете их до 1000 строк, вы, вероятно, в безопасности.

Если вы используете MyISAM, есть множество других вещей, но я не буду утомлять вас этим. Мир.

MarkR
источник
1
Есть ли какая-то причина, по которой это становится контрпродуктивным после определенного момента? Я тоже видел это раньше, но не знал почему.
Дхрув Гайрола
1
Знаете ли вы, есть ли смысл в пакетной вставке MySQL при использовании транзакций . Мне просто интересно, могу ли я избавить себя от необходимости генерировать многозначную команду SQL, если моя базовая библиотека (Java JDBC - mysql-connector-java-5.1.30) на самом деле не фиксирует, пока я не скажу это.
RTF
@RTF Я думаю, что вам нужно будет выполнить небольшой тест, чтобы определить это поведение в вашей ситуации, поскольку оно сильно зависит от реализации, но во многих случаях транзакции должны обеспечивать аналогичный прирост производительности.
Жасмин Хегман,
9

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

RC.
источник
7

В целом, чем меньше обращений к базе данных, тем лучше (то есть быстрее, эффективнее), поэтому старайтесь кодировать вставки таким образом, чтобы минимизировать доступ к базе данных. Помните, что если вы не используете пул соединений, каждый доступ к базе данных должен создать соединение, выполнить sql, а затем разорвать соединение. Совсем немного накладных расходов!

ennuikiller
источник
Что делать, если постоянное соединение используется?
dusoft
6
Там еще над головой. Время прохождения (для каждой отдельной вставки и обратно) будет быстро ощутимо, если вы делаете тысячи вставок.
RC.
4

Вы можете захотеть:

  • Убедитесь, что авто-фиксация выключена
  • Открытое соединение
  • Отправить несколько пакетов вставок в одной транзакции (размер около 4000-10000 строк? Вы видите)
  • Закрыть соединение

В зависимости от того, насколько хорошо ваш сервер масштабируется (это, безусловно, хорошо PostgreSQl, Oracleи MSSQL), сделайте то же самое с несколькими потоками и несколькими подключениями.

Antibarbie
источник
3

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

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

Крис Уильямс
источник
3

MYSQL 5.5 Один SQL-оператор вставки занял ~ 300 до ~ 450 мс. в то время как приведенная ниже статистика предназначена для встроенных множественных вставок.

(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time  : 00:00:00:000
Total Time     : 00:00:03:343

Я бы сказал, встроенный способ пойти :)

A_01
источник
0

Смешно, как плохо оптимизированы Mysql и MariaDB, когда дело доходит до вставок. Я тестировал mysql 5.7 и mariadb 10.3, никакой разницы в них нет.

Я проверил это на сервере с дисками NVME, 70 000 операций ввода-вывода в секунду, пропускной способностью 1,1 ГБ / сек, и это возможно в дуплексном режиме (чтение и запись).
Сервер также является высокопроизводительным сервером.
Дали 20 ГБ оперативной памяти.
База данных полностью пуста.

Скорость, которую я получаю, составляла 5000 вставок в секунду при выполнении многорядных вставок (пробовал с 1 МБ до 10 МБ кусков данных)

Теперь подсказка:
если я добавлю другой поток и вставлю в те же таблицы, у меня вдруг будет 2x5000 / сек. Еще одна нить и у меня 15000 всего / сек

Учтите это: когда вы выполняете вставку ОДНОГО потока, это означает, что вы можете последовательно записывать на диск (за исключением индексов). При использовании потоков вы фактически снижаете возможную производительность, потому что теперь нужно делать гораздо больше случайных обращений. Но проверка реальности показывает, что mysql настолько плохо оптимизирован, что потоки очень помогают.

Реальная производительность, возможная с таким сервером, вероятно, составляет миллионы в секунду, процессор простаивает, диск простаивает.
Причина совершенно ясна, что mariadb, как и mysql, имеет внутренние задержки.

Джон
источник
@Craftables вам нужна внешняя разработка, это не может быть сделано в MySQL. Потоки означают, что вы используете несколько соединений с сервером, вы разделяете запрос на несколько частей (например, разбивая его на четные части по первичному ключу). Мне удалось увеличить производительность с помощью этого метода на очень больших столах в 10000 раз. Запросы, которые будут выполняться в течение 40000 секунд, могут завершиться за 2-3 минуты, если вы используете несколько потоков и ваш mysql высоко оптимизирован.
Джон
@John Интересно и может иметь несколько действительно хороших приложений ... но ... Если вы разбиваете запрос на несколько частей, как вы обрабатываете транзакции? А также рассмотрим следующий сценарий: Таблица x имеет столбец parent_id, который относится к той же таблице id. Где-то внутри ваших данных у вас есть INSERT INTO x ( id, parent_id) VALUES (1, NULL). Один из следующих наборов значений ссылается на эту строку. Если вы разделяете на части и этот набор попадает в другой блок, он может быть обработан перед первым, что приведет к сбою всего процесса. Есть идеи, как с этим бороться?
zozo
@zozo это полезно для массовых вставок и массовых запросов. В любом случае, транзакции могут ухудшить производительность, поскольку они включают в себя большое количество буферизации данных. Но вы также можете использовать транзакции в многопоточных вставках или запросах.
Джон
-2

несколько вставок быстрее, но это есть. Еще один трюк - отключение проверок временных ограничений, делает вставки намного быстрее. Неважно, есть на вашем столе это или нет. Например, проверьте отключение внешних ключей и наслаждайтесь скоростью:

SET FOREIGN_KEY_CHECKS=0;

Конечно, вы должны включить его снова после вставки:

SET FOREIGN_KEY_CHECKS=1;

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

MSS
источник
1
Понятия не имею, почему ppl проголосовал за это по двум причинам: 1. Это не имеет ничего общего с вопросом 2. Это действительно плохая идея (за некоторыми исключениями - например, демпинг или структурные временные изменения - но в целом плохая). Проверки проводятся по причине: они предназначены для обеспечения согласованности данных. Они замедляют работу, потому что они гарантируют, что вы не вставляете или иным образом не изменяете данные, которые вы не должны. Попробуйте оптимизировать запросы правильным образом; в любой критической бизнес-среде это будет означать смерть приложения, поскольку независимо от того, насколько вы осторожны, в какой-то момент произойдет сбой.
zozo
1
возможно, но этот вариант чрезвычайно эффективен при импорте больших таблиц и очень практичен, и он может дать некоторым людям представление о том, как они могут сделать вставку данных намного быстрее.
MSS