Специальная настройка производительности MySQL для массовой вставки - PullRequest
9 голосов
/ 25 февраля 2011

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

Я управляю базой данных журналов, содержащей около 10 таблиц. Основная таблица, в которой хранятся фактические записи журнала, содержит около 30 полей, из которых 5 доступны для поиска. Я бы сказал, что база данных в последнее время приобрела умеренный размер, поскольку мы достигли 200 миллионов записей в этой таблице. В других таблицах хранятся общие данные, из которых наибольшее имеет 4 поля с возможностью поиска, почти 1 миллион записей. Все остальные таблицы содержат менее 100 тысяч записей каждая.

Вставки входят в шипы. Я получаю журналы с предыдущего дня в (довольно плохо отформатированных) CSV-файлах каждый день в 2 часа ночи, и у меня есть до 8 часов утра, чтобы вставить их (около 20 файлов, по 100 тысяч строк каждый) в базу данных. Тогда я получаю очень мало избранных (может быть, около 1000 в день) в течение рабочего дня. Затем промыть и повторить.

Запросы SELECT довольно просты, так как они в основном состоят из одного или двух объединений с одним или двумя операторами GROUP BY. Люди, которые ищут в этой базе данных, хотят немедленных результатов, поэтому у меня есть 5 многостолбцовых индексов в основной таблице, которые помогают в точных поисках, которые у меня есть, и в настоящее время производительность SELECT довольно хорошая. Пока ни один запрос не занял более 0,1 секунды. Есть некоторые отчеты, но на их создание уходит около 10 секунд, и это приемлемо.

В настоящее время у меня есть программа на C, которую я написал для чтения данных из CSV-файлов, их очистки и вставки в виде пакетов по 1000 строк на запрос INSERT. Эти INSERT не совсем глупы, потому что мне нужно получить общие данные, посмотреть, есть ли они уже в других таблицах, вставить их, если это не так, и кэшировать их, если это так. Он также дает мне данные о производительности в виде количества записей, которые он вставляет в секунду. Эта программа довольно быстрая, и без отправки данных в базу данных я получаю около 100 тысяч строк в секунду. Конечно, эта программа и база данных находятся на одном физическом компьютере.

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

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

Таблицы MyISAM: начинается со 1500 строк в секунду, логарифмически уменьшается до 700 строк в секунду к моменту вставки миллионной строки Таблицы InnoDB: так же, как MyISAM, только на 100 строк в секунду быстрее InnoDB со всеми индексами, отключенными в основной таблице: начинается с 2100 строк в секунду, уменьшается до 1000 строк в секунду. InnoDB С индексами, с файловой системой, смонтированной с обратной записью данных (ext3): так же, как InnoDB, только немного, но почти незаметно быстрее.

innodb_buffer_pool_size имеет значение 1000 МБ

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

Системный монитор сообщает мне, что основным потреблением ресурсов является дисковый ввод-вывод, который при вставке достигает почти 100%. Из-за этого мне нужен сверхбыстрый способ вставки данных. Мой теоретический предел - это шина SATA, но это все еще довольно далеко. Кажется, что использование памяти не так высоко - около 20% (или MySQL неправильно использует память)

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

Так что я действительно открыт для идей здесь.Кто-нибудь знает что-нибудь, что могло бы мне помочь?

Редактировать: В настоящее время я рассматриваю вопрос о вставке новых строк в таблицу MEMORY, а затем сделать SELECT INTO реальной таблицы.Надеюсь, он обновит и очистит индекс только один раз после того, как все строки были вставлены.Я попробую это в понедельник.Кто-нибудь пробовал что-то подобное раньше?

Ответы [ 4 ]

7 голосов
/ 28 февраля 2011

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

Вот что я сделал:

  1. Перепишите программу загрузки. Я сказал, что это было на C, но на самом деле это было на C ++. Изменив строку на char *, fstream с mmap и другие подобные вещи, я почти удвоил производительность. (И многие до сих пор утверждают, что C ++ работает быстрее или быстрее, чем C. Я бы даже не хотел попробовать это в C # / Java)

  2. Я нашел эту страницу: http://kevin.vanzonneveld.net/techblog/article/improve_mysql_insert_performance/ Это отличный ресурс (я не связан с ними), который объясняет почти все, что я собирался попробовать, со всеми различными результатами. Практически единственное, что может повысить производительность вставки, - это использовать LOAD DATA INFILE. Настраивая структуру таблицы, я мог вставлять ее почти в четыре раза! производительность моих вставок.

  3. Я переписал вставки, которые не могут быть выполнены с помощью LOAD DATA INFILE, для массовых вставок (несколько строк на команду вставки) с использованием сложных выражений внутри ON DUPLICATE KEY UPDATE вместо выполнения SELECT / INSERT для каждой строки. Это также дало очень хороший прирост производительности. Это также потребовало некоторых модификаций структур таблицы.

  4. При воссоздании базы данных, которая уже прошла более 2 миллиардов строк, создайте таблицы, которые получают вставки LOAD DATA INFILE без индексов, и воссоздайте их, когда закончите. Все мои тесты показали, что время вставки без индексов плюс время их создания короче, чем время вставки в таблицы с индексами. Разница не огромная, но заметная (примерно в 1,2 раза быстрее). Я предполагаю, что B-деревья также будут лучше сбалансированы таким образом.

  5. Использовать MyISAM. Мои предыдущие тесты не были настолько убедительными, но при использовании LOAD DATA INFILE InnoDB проигрывает каждый раз. Локально тестируя, я получил около 16000 записей / с в MyISAM / без индексов, 12000 записей / с в MyISAM / индексы, 9000 записей / с в InnoDB / без индексов и около 7500 в InnoDB / индексы. Версия MySQL 5.1.47.

  6. Для файлов для LOAD DATA INFILE создайте их в смонтированном разделе tmpfs. Это также значительно повышает производительность, особенно потому, что вам нужно записать файл и записать его на диск, чтобы MySQL мог его прочитать. Если это tmpfs невозможно, это можно сделать, используя именованные каналы.

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

Удачи, и спасибо всем за помощь.

3 голосов
/ 26 февраля 2011

2 миллиона строк за 6,5 часов?
Насколько велик набор данных, который вы храните?

Я использую следующие расчеты с обратной стороны, чтобы получить несколько полезную цифру:
Предполагая, что 1 один диск-диск, который проглатывает 35 МБ в секунду, вы должны быть в состоянии записать (35 * 6,5 * 3600) = ~ 800 gb в этот период времени.Вычисление в обратном направлении (800 ГБ / 2 mrows) дает средний размер строки 400 КБ.

Если эти цифры кажутся правильными, вам нужно увеличить аппаратное обеспечение, чтобы увеличить скорость.Если они полностью отключены, вероятно, существует другая проблема.

Кроме того, посмотрите способ сопоставления дискового ввода-вывода для выделенного сервера MySQL на ServerFault, чтобы определить способ измерения.I / O.

Вот несколько случайных предложений (на случай, если вы подозреваете какую-либо другую проблему)

  • Убедитесь, что вы исключили все построчные операциив процессе загрузки
  • Если большая часть данных CSV будет сохранена, рассмотрите массовую загрузку в промежуточные таблицы и обработайте данные в базе данных, используя обработку на основе набора.
  • Если большая часть данных отбрасывается, рассмотрите возможность перемещения / кэширования ваших справочных таблиц за пределами базы данных, чтобы иметь возможность фильтровать данные CSV в вашем C-коде
  • MySQL не имеет хеш-соединений, но полагаетсяна индексированных циклах.Убедитесь, что в этих других таблицах есть соответствующие индексы
  • Попробуйте предварительно отсортировать данные вне базы данных, чтобы они соответствовали индексу другой таблицы, используемой в процессе (чтобы повысить вероятность того, что соответствующие данные не будут удаленыкэша)
  • Прочтите разбиение и посмотрите, сможете ли вы заменить некоторые ваши индексы на умную схему разбиения вместо того, чтобы поддерживать все эти индексы.

Отредактировано
Исправлен расчет (400 КБ)

1 голос
/ 26 февраля 2011

Я бы попытался увеличить размер пула буферов innodb и посмотреть, что произойдет. Для Innodb я бы также отключил постоянную очистку с помощью innodb_flush_log_at_trx_commit = 0 (или = 2). Значение по умолчанию - 1, что является узким местом для рабочих нагрузок с интенсивной записью. 0 или 2 дадут вам 1 секунду задержки между сбросами. Вы также можете делать большие партии, используя транзакции (если вы явно не используете транзакции, то каждая вставка является собственной транзакцией).

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

Все вышеперечисленное относится к innodb.

1 голос
/ 25 февраля 2011

Вы нажали это при упоминании дискового ввода / вывода.Если ваш диск заполнен вставками, вы не сможете работать быстрее, если не обновитесь.Вы не упомянули, допустимо ли выполнять обновление дисков, но я бы посмотрел на использование SCSI или флэш-дисков.Даже если вы не достигнете ограничения шины SATA, ваш диск определенно является узким местом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...