2010-09-09 2 views
13

Я работаю с огромной таблицей, содержащей 250 миллионов рядов. Схема проста.MySQL Вставка производительности ухудшается на большой таблице

CREATE TABLE MyTable (
     id BIGINT PRIMARY KEY AUTO_INCREMENT, 
     oid INT NOT NULL, 
     long1 BIGINT NOT NULL, 
     str1 VARCHAR(30) DEFAULT NULL, 
     str2 VARCHAR(30) DEFAULT NULL, 
     str2 VARCHAR(200) DEFAULT NULL, 
     str4 VARCHAR(50) DEFAULT NULL, 
     int1 INT(6) DEFAULT NULL, 
     str5 VARCHAR(300) DEFAULT NULL, 
     date1 DATE DEFAULT NULL, 
     date2 DATE DEFAULT NULL, 
     lastUpdated TIMESTAMP NOT NULL, 
     hashcode INT NOT NULL, 
     active TINYINT(1) DEFAULT 1, 
     KEY oid(oid), 
     KEY lastUpdated(lastUpdated), 
     UNIQUE KEY (hashcode, active), 
     KEY (active) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 MAX_ROWS=1000000000; 

Производительность вставки значительно снизилась. До 150 миллионов строк в таблице, для ввода 10 000 строк использовалось 5-6 секунд. Теперь он вырос в 2-4 раза. Файл ibdata от Innodb вырос до 107 ГБ. Параметры конфигурации Innodb следующие.

innodb_buffer_pool_size = 36G # Machine has 48G memory 
innodb_additional_mem_pool_size = 20M 
innodb_data_file_path = ibdata1:10M:autoextend 
innodb_log_file_size = 50M 
innodb_log_buffer_size = 20M 
innodb_log_files_in_group=2 
innodb_flush_log_at_trx_commit = 1 
innodb_lock_wait_timeout = 50 
innodb_thread_concurrency = 8 
innodb_flush_method = O_DIRECT 
expire_logs_days = 4 

Время ожидания ввода-вывода увеличилось, как видно, с top. Я попытался изменить метод flush на O_DSYNC, но это не помогло. Диск вырезается из аппаратной конфигурации RAID 10. В более ранней установке с одним диском IO не было проблемой.

Разделяет ли только таблицу? Может ли разделение одного файла 100G на «маленькие» файлы? Существуют ли какие-либо переменные, которые необходимо настроить для RAID?

Обновление: Это тестовая система. У меня есть свобода вносить какие-либо изменения.

ответ

13

Вы не сказали, была ли это тестовой системой или производством; Я предполагаю, что это производство.

Скорее всего, у вас есть таблица размером, где ее индексы (или вся партия) больше не вписываются в память.

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

Разделение похоже на наиболее очевидное решение, но разбиение на разделы MySQL может не соответствовать вашему прецеденту.

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

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

Также рассмотрите плагин innodb и сжатие, это заставит ваш innodb_buffer_pool пойти дальше.

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

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

+1

Спасибо, Mark.Это тестовая система. –

+0

Ваш отзыв о размере индекса полезен. Я работаю над индексированием. –

2

Как отмечал MarkR выше, вставка производительности ухудшается, когда индексы больше не могут вписываться в ваш буферный пул. InnoDB имеет случайный механизм сокращения ввода-вывода (называемый буфером вставки), который предотвращает некоторые из этих проблем, но он не будет работать с вашим индексом UNIQUE. Индекс on (hashcode, active) должен быть проверен на каждой вставке, убедитесь, что не вставлены повторяющиеся записи. Если хеш-код не «следует» первичному ключу, эта проверка может быть случайной IO.

У вас есть возможность изменить схему?

Лучше всего это:

(а) Сделать хэш кто-то последовательно, или сортировать по хэшу-кода перед объемной вставкой (это сам по себе поможет, так как случайные чтения будет уменьшено).

(b) Сделать (hashcode, active) первичный ключ - и вставить данные в отсортированном порядке. Я предполагаю, что ваше приложение, вероятно, читает hashcode - и поиск первичного ключа происходит быстрее.

4

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

Это то, что твиттер ударил в то время назад и понял, что ему нужно осколочно - см. http://github.com/twitter/gizzard.

Это все зависит от ваших вариантов использования, но вы можете также перейти от MySQL к Кассандре, как она работает очень хорошо для записи ресурсоемких приложений. (Http://cassandra.apache.org)

1

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

tokutek.com

http://tokutek.com/downloads/tokudb-performance-brief.pdf

0

Я буду вторым @ комментарии MarkR по поводу снижения индексов. Еще одна вещь, на которую вы должны обратить внимание, - увеличить ваш innodb_log_file_size. Это увеличивает время восстановления после сбоя, но должно помочь. Помните, что перед перезагрузкой сервера вам необходимо удалить старые файлы.

Общие InnoDB настройки советы: http://www.mysqlperformanceblog.com/2007/11/01/innodb-performance-optimization-basics/

Вы также должны быть осведомлены о LOAD DATA INFILE для выполнения вставки. Это намного быстрее.

0

Увеличение от innodb_log_file_size = 50M до innodb_log_file_size = 500M

И в innodb_flush_log_at_trx_commit должно быть 0, если вы принесете 1 сек потери данных.

Смежные вопросы