Ответы:
https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html
Время, необходимое для вставки строки, определяется следующими факторами, где числа указывают приблизительные пропорции:
- Подключение: (3)
- Отправка запроса на сервер: (2)
- Запрос синтаксического анализа: (2)
- Вставка строки: (1 × размер строки)
- Вставка индексов: (1 × количество индексов)
- Закрытие: (1)
Из этого должно быть очевидно, что отправка одного большого оператора избавит вас от 7 накладных расходов на оператор вставки, который в дальнейшем чтении текста также говорит:
Если вы одновременно вставляете много строк из одного и того же клиента, используйте операторы INSERT с несколькими списками VALUES, чтобы вставить несколько строк одновременно. Это значительно быстрее (во многих случаях быстрее), чем использование отдельных однострочных операторов INSERT.
Я знаю, что отвечаю на этот вопрос почти через два с половиной года после того, как он был задан, но я просто хотел предоставить некоторые точные данные из проекта, над которым я сейчас работаю, который показывает, что действительно делать несколько блоков 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, установленного на сервере.)
Основным фактором будет то, используете ли вы транзакционный движок и включена ли автоматическая фиксация.
Автокоммит включен по умолчанию, и вы, вероятно, хотите оставить его включенным; следовательно, каждая вставка, которую вы делаете, выполняет свою собственную транзакцию. Это означает, что если вы делаете одну вставку на строку, вы собираетесь совершать транзакции для каждой строки.
Предполагая один поток, это означает, что сервер должен синхронизировать некоторые данные на диск для КАЖДОЙ СТРОКИ. Необходимо подождать, пока данные достигнут постоянного места хранения (возможно, оперативной памяти с резервным питанием в вашем RAID-контроллере). Это по сути довольно медленно и, вероятно, станет ограничивающим фактором в этих случаях.
Я, конечно, предполагаю, что вы используете транзакционный движок (обычно innodb) И что вы не изменили настройки, чтобы уменьшить срок службы.
Я также предполагаю, что вы используете один поток для этих вставок. Использование нескольких потоков немного затрудняет работу, потому что в некоторых версиях MySQL есть рабочая групповая фиксация в innodb - это означает, что несколько потоков, выполняющих свои собственные коммиты, могут совместно использовать одну запись в журнал транзакций, что хорошо, потому что это означает меньшее количество синхронизаций в постоянном хранилище. ,
С другой стороны, в результате вы действительно хотите использовать многорядные вставки.
Существует предел, по которому он становится контрпродуктивным, но в большинстве случаев это не менее 10000 строк. Так что если вы упаковываете их до 1000 строк, вы, вероятно, в безопасности.
Если вы используете MyISAM, есть множество других вещей, но я не буду утомлять вас этим. Мир.
В целом, чем меньше обращений к базе данных, тем лучше (то есть быстрее, эффективнее), поэтому старайтесь кодировать вставки таким образом, чтобы минимизировать доступ к базе данных. Помните, что если вы не используете пул соединений, каждый доступ к базе данных должен создать соединение, выполнить sql, а затем разорвать соединение. Совсем немного накладных расходов!
Вы можете захотеть:
В зависимости от того, насколько хорошо ваш сервер масштабируется (это, безусловно, хорошо PostgreSQl
, Oracle
и MSSQL
), сделайте то же самое с несколькими потоками и несколькими подключениями.
Как правило, несколько вставок будет медленнее из-за издержек соединения. Выполнение нескольких вставок одновременно уменьшит стоимость накладных расходов на одну вставку.
В зависимости от того, какой язык вы используете, вы можете создать пакет на своем языке программирования / сценариев, прежде чем переходить к базе данных, и добавлять каждую вставку в пакет. Тогда вы сможете выполнить большой пакет, используя одну операцию подключения. Вот пример на Java.
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
Я бы сказал, встроенный способ пойти :)
Смешно, как плохо оптимизированы Mysql и MariaDB, когда дело доходит до вставок. Я тестировал mysql 5.7 и mariadb 10.3, никакой разницы в них нет.
Я проверил это на сервере с дисками NVME, 70 000 операций ввода-вывода в секунду, пропускной способностью 1,1 ГБ / сек, и это возможно в дуплексном режиме (чтение и запись).
Сервер также является высокопроизводительным сервером.
Дали 20 ГБ оперативной памяти.
База данных полностью пуста.
Скорость, которую я получаю, составляла 5000 вставок в секунду при выполнении многорядных вставок (пробовал с 1 МБ до 10 МБ кусков данных)
Теперь подсказка:
если я добавлю другой поток и вставлю в те же таблицы, у меня вдруг будет 2x5000 / сек. Еще одна нить и у меня 15000 всего / сек
Учтите это: когда вы выполняете вставку ОДНОГО потока, это означает, что вы можете последовательно записывать на диск (за исключением индексов). При использовании потоков вы фактически снижаете возможную производительность, потому что теперь нужно делать гораздо больше случайных обращений. Но проверка реальности показывает, что mysql настолько плохо оптимизирован, что потоки очень помогают.
Реальная производительность, возможная с таким сервером, вероятно, составляет миллионы в секунду, процессор простаивает, диск простаивает.
Причина совершенно ясна, что mariadb, как и mysql, имеет внутренние задержки.
id
, parent_id
) VALUES (1, NULL). Один из следующих наборов значений ссылается на эту строку. Если вы разделяете на части и этот набор попадает в другой блок, он может быть обработан перед первым, что приведет к сбою всего процесса. Есть идеи, как с этим бороться?
несколько вставок быстрее, но это есть. Еще один трюк - отключение проверок временных ограничений, делает вставки намного быстрее. Неважно, есть на вашем столе это или нет. Например, проверьте отключение внешних ключей и наслаждайтесь скоростью:
SET FOREIGN_KEY_CHECKS=0;
Конечно, вы должны включить его снова после вставки:
SET FOREIGN_KEY_CHECKS=1;
это обычный способ вставки огромных данных. целостность данных может нарушиться, поэтому вы должны позаботиться об этом перед отключением проверок внешнего ключа.