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

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

10 ответов


http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html

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

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

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

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


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

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

выполнение отдельных блоков значений (например, 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 улучшение становится незначительным.

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


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

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

предполагая один поток, это означает, что сервер должен синхронизировать некоторые данные на диск для каждой строки. Нужно подождать. данные для достижения постоянного места хранения (надеюсь, аккумуляторная ОЗУ в вашем RAID-контроллере). Это по своей сути довольно медленно и, вероятно, станет сдерживающим фактором в этих случаях.

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

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

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

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

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


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


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


вы хотите :

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

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


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

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

Я бы сказал, что inline-это путь:)


В общем случае несколько вставок будут медленнее из-за накладных расходов на подключение. Делать множественные вставки сразу уменьшит цену накладных расходов в вставку.

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


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

SET FOREIGN_KEY_CHECKS=0;

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

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

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

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

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

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