Я борюсь с массовым импортом довольно большой таблицы InnoDB, состоящей примерно из 10 миллионов строк (или 7 ГБ) (что для меня является самой большой таблицей, с которой я когда-либо работал).
Я провел некоторое исследование, как улучшить скорость импорта Inno, и на данный момент мои настройки выглядят так:
/etc/mysql/my.cnf/
[...]
innodb_buffer_pool_size = 7446915072 # ~90% of memory
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 5000
innodb_thread_concurrency=0
innodb_doublewrite = 0
innodb_log_file_size = 1G
log-bin = ""
innodb_autoinc_lock_mode = 2
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_instances=8
import is done via bash script, here is the mysql code:
SET GLOBAL sync_binlog = 1;
SET sql_log_bin = 0;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
LOAD DATA LOCAL INFILE '$filepath' INTO TABLE monster
COMMIT;
Данные предоставляются в CSV
файле.
В настоящее время я тестирую свои настройки с меньшими «тестовыми дампами» с 2 миллионами, 3 миллионами,… строками каждый и использую их time import_script.sh
для сравнения производительности.
Недостатком является то, что я получаю только общее время выполнения, поэтому мне приходится ждать завершения полного импорта, чтобы получить результат.
Мои результаты пока:
- 10 000 строк: <1 секунда
- 100 000 строк: 10 секунд
- 300 000 строк: 40 секунд
- 2 миллиона строк: 18 минут
- 3 миллиона строк: 26 минут
- 4 миллиона строк: (отменено через 2 часа)
Похоже, решения для «поваренной книги» не существует, и нужно самостоятельно определить оптимальное сочетание настроек.
Помимо предложений о том, что нужно изменить в моей настройке, я также был бы очень признателен за дополнительную информацию о том, как лучше оценить процесс импорта / получить более полное представление о том, что происходит и где может быть узкое место.
Я попытался прочитать документацию по изменяемым настройкам, но, опять же, я не знаю никаких побочных эффектов и могу ли я даже снизить производительность при неправильно выбранном значении.
На данный момент я хотел бы попробовать предложение из чата, чтобы использовать MyISAM
во время импорта и впоследствии изменить таблицу движка.
Я хотел бы попробовать это, но на данный момент мой DROP TABLE
запрос также занимает несколько часов, чтобы закончить. (Что является еще одним показателем, мой параметр меньше оптимального).
Дополнительная информация:
Машина, которую я сейчас использую, имеет 8 ГБ ОЗУ и твердотельный гибридный жесткий диск с 5400 об / мин.
Хотя мы также стремимся удалить устаревшие данные из таблицы, о которой идет речь, мне все же требуется несколько быстрый импорт в
а) тестирование automatic data cleanup feature
во время разработки и
б) в случае сбоя нашего сервера, мы хотели бы использовать наш второй сервер в качестве замены (который требует -данные данные, последний импорт занял более 24 часов)
mysql> SHOW CREATE TABLE monster\G
*************************** 1. row ***************************
Table: monster
Create Table: CREATE TABLE `monster` (
`monster_id` int(11) NOT NULL AUTO_INCREMENT,
`ext_monster_id` int(11) NOT NULL DEFAULT '0',
`some_id` int(11) NOT NULL DEFAULT '0',
`email` varchar(250) NOT NULL,
`name` varchar(100) NOT NULL,
`address` varchar(100) NOT NULL,
`postcode` varchar(20) NOT NULL,
`city` varchar(100) NOT NULL,
`country` int(11) NOT NULL DEFAULT '0',
`address_hash` varchar(250) NOT NULL,
`lon` float(10,6) NOT NULL,
`lat` float(10,6) NOT NULL,
`ip_address` varchar(40) NOT NULL,
`cookie` int(11) NOT NULL DEFAULT '0',
`party_id` int(11) NOT NULL,
`status` int(11) NOT NULL DEFAULT '2',
`creation_date` datetime NOT NULL,
`someflag` tinyint(1) NOT NULL DEFAULT '0',
`someflag2` tinyint(4) NOT NULL,
`upload_id` int(11) NOT NULL DEFAULT '0',
`news1` tinyint(4) NOT NULL DEFAULT '0',
`news2` tinyint(4) NOT NULL,
`someother_id` int(11) NOT NULL DEFAULT '0',
`note` varchar(2500) NOT NULL,
`referer` text NOT NULL,
`subscription` int(11) DEFAULT '0',
`hash` varchar(32) DEFAULT NULL,
`thumbs1` int(11) NOT NULL DEFAULT '0',
`thumbs2` int(11) NOT NULL DEFAULT '0',
`thumbs3` int(11) NOT NULL DEFAULT '0',
`neighbours` tinyint(4) NOT NULL DEFAULT '0',
`relevance` int(11) NOT NULL,
PRIMARY KEY (`monster_id`),
KEY `party_id` (`party_id`),
KEY `creation_date` (`creation_date`),
KEY `email` (`email`(4)),
KEY `hash` (`hash`(8)),
KEY `address_hash` (`address_hash`(8)),
KEY `thumbs3` (`thumbs3`),
KEY `ext_monster_id` (`ext_monster_id`),
KEY `status` (`status`),
KEY `note` (`note`(4)),
KEY `postcode` (`postcode`),
KEY `some_id` (`some_id`),
KEY `cookie` (`cookie`),
KEY `party_id_2` (`party_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=13763891 DEFAULT CHARSET=utf8
источник
SHOW CREATE TABLE yourtable\G
чтобы показать нам структуру таблицы этой таблицы с 10 миллионами строк.innodb_doublewrite = 0
), ваша установка MySQL не является безопасной при сбое: если у вас сбой питания (не сбой MySQL), ваши данные могут быть незаметно повреждены.Ответы:
Во-первых, вам нужно знать, что вы делаете с InnoDB, когда вносите миллионы строк в таблицу InnoDB. Давайте посмотрим на архитектуру InnoDB.
В левом верхнем углу есть иллюстрация буферного пула InnoDB. Обратите внимание, что есть раздел, посвященный буферу вставки. Что это делает? Он предназначен для переноса изменений во вторичные индексы из пула буферов в буфер вставки внутри табличного пространства системы (он же ibdata1). По умолчанию innodb_change_buffer_max_size имеет значение 25. Это означает, что до 25% пула буферов можно использовать для обработки вторичных индексов.
В вашем случае у вас есть 6,935 ГБ для пула буферов InnoDB. Для обработки ваших вторичных индексов будет использовано максимум 1,734 ГБ.
Теперь посмотри на свой стол. У вас есть 13 вторичных индексов. Каждая обрабатываемая строка должна генерировать запись вторичного индекса, связывать ее с первичным ключом строки и отправлять их в виде пары из буфера вставки в пуле буферов в буфер вставки в ibdata1. Это происходит 13 раз с каждым рядом. Умножьте это на 10 миллионов, и вы почти почувствуете приближение узкого места.
Не забывайте, что импорт 10 миллионов строк в одной транзакции соберет все в один сегмент отката и заполнит пространство UNDO в ibdata1.
SUGGESTIONS
ПРЕДЛОЖЕНИЕ № 1
Мое первое предложение для импорта этой довольно большой таблицы было бы
ПРЕДЛОЖЕНИЕ № 2
Избавьтесь от дублирующих индексов. В вашем случае у вас есть
Оба индекса начинаются с того
party_id
, что вы можете увеличить обработку вторичного индекса как минимум на 7,6%, избавившись от одного индекса из 13. Вам необходимо в конечном итоге запуститьПРЕДЛОЖЕНИЕ № 3
Избавьтесь от индексов, которые вы не используете. Посмотрите код вашего приложения и посмотрите, используют ли ваши запросы все индексы. Возможно, вы захотите взглянуть на использование pt-index, чтобы оно подсказывало, какие индексы не используются.
ПРЕДЛОЖЕНИЕ № 4
Вы должны увеличить innodb_log_buffer_size до 64M, поскольку по умолчанию это 8M. Больший буфер журнала может увеличить производительность операций ввода-вывода при записи InnoDB.
Эпилог
Поместив первые два предложения, сделайте следующее:
party_id
индексаВозможно, следующее может помочь
Импортируйте данные в
monster
. Затем запустите этоДАЙТЕ ЭТО ПОПРОБУЙТЕ !!!
АЛЬТЕРНАТИВА
Вы можете создать таблицу с
monster_csv
именем MyISAM без индексов и сделать это:Импортируйте ваши данные в
monster_csv
. Затем используйте mysqldump для создания другого импортаФайл mysqldump
data.sql
будет расширять команды INSERT, импортируя 10 000–20 000 строк одновременно.Теперь просто загрузите mysqldump
Наконец, избавьтесь от таблицы MyISAM
источник
monster
таблицу) менее чем за 20 минут, когда у меня не было ключей для таблиц InnoDB. Добавление ключей заняло ок. еще 20 мин. Я бы сказал, что это в значительной степени решает мою проблему в этом случае. Большое спасибо!Я хотел написать комментарий (так как это не окончательный ответ), но он стал слишком длинным:
Я дам вам несколько общих советов, и мы можем подробно рассказать о каждом из них, если вы хотите:
Помните, что некоторые из них не защищены или не рекомендуются для импорта (нормальной работы).
источник
SET SESSION tx_isolation='READ-UNCOMMITTED';
(полезно только при импорте с несколькими параллельными потоками) и комментария @ypercube о вставке в пакеты. У вас есть полный пример здесь: mysqlperformanceblog.com/2008/07/03/… Убедитесь, что вы получаете все преимущества в последних версиях InnoDB: mysqlperformanceblog.com/2011/01/07/…Большинство хороших советов было дано до сих пор, но без большого количества объяснений лучших. Я дам более подробную информацию.
Во-первых, откладывание создания индекса - это хорошо, достаточно подробностей в других ответах. Я не вернусь на это.
Большой файл журнала InnoDB вам очень поможет (если вы используете MySQL 5.6, так как в MySQL 5.5 его невозможно увеличить). Вы вставляете 7 ГБ данных, я бы рекомендовал общий размер журнала не менее 8 ГБ (оставьте значение
innodb_log_files_in_group
по умолчанию (2) и увеличьте егоinnodb_log_file_size
до 4 ГБ). Эти 8 ГБ не являются точными: они должны быть не меньше размера импорта в журнале REDO и, возможно, в два или четыре раза больше этого размера. Причины, по которым размер журнала InnoDB увеличивают, заключаются в том, что, когда журнал становится почти заполненным, InnoDB начнет активно сбрасывать свой буферный пул на диск, чтобы избежать заполнения журнала (когда журнал заполнен, InnoDB не может выполнять запись в базу данных до некоторой страницы пула буферов записываются на диск).Вам поможет файл журнала InnoDB большего размера, но вы также должны вставить его в порядке первичного ключа (отсортируйте файл перед вставкой). Если вы вставите в порядке первичного ключа, InnoDB заполнит одну страницу, а затем еще одну, и так далее. Если вы не вставите в порядке первичного ключа, ваша следующая вставка может оказаться на странице, которая заполнена и приведет к разделению страницы. Этот раздел страницы будет дорогим для InnoDB и замедлит ваш импорт.
У вас уже есть буферный пул, настолько большой, насколько позволяет ваша оперативная память, и если ваша таблица не помещается в нем, вы ничего не можете сделать, кроме как покупать больше оперативной памяти. Но если ваша таблица помещается в буферный пул, но больше 75% вашего буферного пула, вы можете попробовать увеличить ее
innodb_max_dirty_pages_pct
до 85 или 95 во время импорта (значение по умолчанию - 75). Этот параметр конфигурации сообщает InnoDB о необходимости активной очистки пула буферов, когда процент грязных страниц достигает этого предела. Увеличив этот параметр (и, если вам повезет, с размером данных), вы можете избежать агрессивного ввода-вывода во время импорта и отложить этот ввод позже.Возможно (и это предположение) импорт ваших данных во многих небольших транзакциях поможет вам. Я не знаю точно, как создается журнал REDO, но если он буферизируется в ОЗУ (и на диске, когда потребуется слишком много ОЗУ), в то время как транзакция выполняется, у вас могут возникнуть ненужные операции ввода-вывода. Вы можете попробовать это: как только ваш файл отсортирован, разбейте его на несколько частей (попробуйте с 16 МБ и другими размерами) и импортируйте их один за другим. Это также позволит вам контролировать ход вашего импорта. Если вы не хотите, чтобы ваши данные были частично видны другому читателю во время импорта, вы можете импортировать, используя другое имя таблицы, создать индексы позже, а затем переименовать таблицу.
Про ваш гибридный диск SSD / 5400RPM я не знаю, как и как это оптимизировать. 5400RPM выглядит медленно для базы данных, но, возможно, SSD избегает этого. Возможно, вы заполняете часть SSD вашего диска последовательными записями в журнал REDO, и SSD ухудшает производительность. Я не знаю.
Плохие советы, которые вы не должны использовать (или будьте осторожны), заключаются в следующем: не используйте многопоточность: будет очень трудно оптимизировать, чтобы избежать разбиения страниц в InnoDB. Если вы хотите использовать многопоточность, вставьте в разные таблицы (или в разные разделы одной и той же таблицы).
Если вы рассматриваете многопоточность, возможно, у вас есть компьютер с несколькими сокетами (NUMA). В этом случае убедитесь, что вы избежали проблемы с безумной заменой MySQL .
Если вы используете MySQL 5.5, обновитесь до MySQL 5.6: он имеет возможность увеличения размера журнала REDO и имеет лучшие алгоритмы очистки буферного пула.
Удачи в импорте.
источник