Действительно ли сортировка InnoDB НАСТОЛЬКО медленная?

У меня были все мои таблицы в myISAM, но блокировка на уровне таблицы начинала убивать меня, когда у меня были длительные задания на обновление. Я преобразовал свои основные таблицы в InnoDB, и теперь многие из моих запросов выполняются более 1 минуты, тогда как на myISAM они выполнялись почти мгновенно. Обычно они застревают на шаге Sorting result. Я сделал что-то неправильно?

Например :

SELECT * FROM `metaward_achiever` 
 INNER JOIN `metaward_alias` ON (`metaward_achiever`.`alias_id` = `metaward_alias`.`id`) 
 WHERE `metaward_achiever`.`award_id` = 1507  
 ORDER BY `metaward_achiever`.`modified` DESC 
 LIMIT 100  

Сейчас это занимает около 90 секунд. Вот описание:

+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+
| id | select_type | table             | type   | possible_keys                                         | key                        | key_len | ref                             | rows  | Extra                       |
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+
|  1 | SIMPLE      | metaward_achiever | ref    | metaward_achiever_award_id,metaward_achiever_alias_id | metaward_achiever_award_id | 4       | const                           | 66424 | Using where; Using filesort | 
|  1 | SIMPLE      | metaward_alias    | eq_ref | PRIMARY                                               | PRIMARY                    | 4       | paul.metaward_achiever.alias_id |     1 |                             | 
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+

Кажется, что теперь ТОННЫ моих запросов застревают на этапе «Результат сортировки»:

mysql> show processlist;
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
| Id     | User | Host      | db   | Command | Time | State          | Info                                                                                                 |
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
| 460568 | paul | localhost | paul | Query   |    0 | NULL           | show processlist                                                                                     | 
| 460638 | paul | localhost | paul | Query   |    0 | Sorting result | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
| 460710 | paul | localhost | paul | Query   |   79 | Sending data   | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
| 460722 | paul | localhost | paul | Query   |   49 | Updating       | UPDATE `metaward_alias` SET `modified` = '2009-09-15 12:43:50', `created` = '2009-08-24 11:55:24', ` | 
| 460732 | paul | localhost | paul | Query   |   25 | Sorting result | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
5 rows in set (0.00 sec)

Почему это простое обновление зависает на 49 секунд?

Если поможет, вот схемы:

| metaward_alias | CREATE TABLE `metaward_alias` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `string_id` varchar(255) DEFAULT NULL,
  `shortname` varchar(100) NOT NULL,
  `remote_image` varchar(500) DEFAULT NULL,
  `image` varchar(100) NOT NULL,
  `user_id` int(11) DEFAULT NULL,
  `type_id` int(11) NOT NULL,
  `md5` varchar(32) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `string_id` (`string_id`),
  KEY `metaward_alias_user_id` (`user_id`),
  KEY `metaward_alias_type_id` (`type_id`)
) ENGINE=InnoDB AUTO_INCREMENT=858381 DEFAULT CHARSET=utf8 | 

| metaward_award | CREATE TABLE `metaward_award` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `string_id` varchar(20) NOT NULL,
  `owner_id` int(11) NOT NULL,
  `name` varchar(100) NOT NULL,
  `description` longtext NOT NULL,
  `owner_points` int(11) NOT NULL,
  `url` varchar(500) NOT NULL,
  `remote_image` varchar(500) DEFAULT NULL,
  `image` varchar(100) NOT NULL,
  `parent_award_id` int(11) DEFAULT NULL,
  `slug` varchar(110) NOT NULL,
  `true_points` double DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `string_id` (`string_id`),
  KEY `metaward_award_owner_id` (`owner_id`),
  KEY `metaward_award_parent_award_id` (`parent_award_id`),
  KEY `metaward_award_slug` (`slug`),
  KEY `metaward_award_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=122176 DEFAULT CHARSET=utf8 | 

| metaward_achiever | CREATE TABLE `metaward_achiever` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `award_id` int(11) NOT NULL,
  `alias_id` int(11) NOT NULL,
  `count` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `metaward_achiever_award_id` (`award_id`),
  KEY `metaward_achiever_alias_id` (`alias_id`)
) ENGINE=InnoDB AUTO_INCREMENT=77175366 DEFAULT CHARSET=utf8 | 

А это в моем my.cnf

innodb_file_per_table
innodb_buffer_pool_size = 2048M
innodb_additional_mem_pool_size = 16M
innodb_flush_method=O_DIRECT

person Paul Tarjan    schedule 15.09.2009    source источник


Ответы (6)


Это большой набор результатов (66 424 строки), который MySQL должен отсортировать вручную. Попробуйте добавить индекс в metaward_achiever.modified.

В MySQL 4.x есть ограничение, которое позволяет MySQL использовать только один индекс для каждой таблицы. Поскольку он использует индекс в столбце metaward_achiever.award_id для выбора WHERE, он также не может использовать индекс в metaward_achiever.modified для сортировки. Я надеюсь, что вы используете MySQL 5.x, что, возможно, улучшило это.

Вы можете увидеть это, выполнив объяснение этого упрощенного запроса:

SELECT * FROM `metaward_achiever` 
 WHERE `metaward_achiever`.`award_id` = 1507  
 ORDER BY `metaward_achiever`.`modified` DESC 
 LIMIT 100

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

Вы также можете создать составной индекс как с metaward_achiever.award_id, так и с metaward_achiever. Если MySQL не использует его, то вы можете намекнуть на это или удалить его только по award_id.

В качестве альтернативы, если вы можете избавиться от metaward_achiever.id и сделать metaward_achiever.award_id своим первичным ключом и добавить ключ к metaward_achiever.modified, или еще лучше сделать metaward_achiever.award_id в сочетании с metaward.modified вашим первичным ключом, тогда вы действительно будете хороший.

Вы можете попробовать оптимизировать сортировку файлов, изменив настройки. К сожалению, у меня нет опыта в этом, так как наш администратор базы данных обрабатывает настройку, но вы можете проверить этот замечательный блог: http://www.mysqlperformanceblog.com/

Вот статья о сортировке файлов, в частности: http://s.petrunia.net/blog/? р=24

person Marcus Adams    schedule 15.09.2009
comment
Сортировка файлов с myISAM быстрее, чем с InnoDB? Разве они не должны быть одинаковыми? - person Paul Tarjan; 16.09.2009
comment
@ Пол - правильно. В некоторых случаях могут быть некоторые предполагаемые различия, потому что предварительно отсортированные данные сортируются быстрее (и InnoDB физически поддерживает строки, упорядоченные по первичному ключу), но внутренне это всегда работает одинаково, поскольку не обрабатывается на уровне механизма хранения. - person Morgan Tocker; 24.07.2010

Как написано в Если вы перейдете с MyISAM в Innodb ? (что совсем недавно):

Innodb нуждается в настройке В заключение о миграции с MyISAM на Innodb я должен упомянуть о настройке Innodb. Innodb нуждается в настройке. Действительно. MyISAM для многих приложений может хорошо работать со значениями по умолчанию. Я видел сотни гигабайтных баз данных, запущенных с MyISAM с настройками по умолчанию, и это работало разумно. Innodb нужны ресурсы, и он не будет хорошо работать со значениями по умолчанию. Настройка MyISAM по умолчанию редко дает увеличение более чем в 2-3 раза, в то время как для таблиц Innodb оно может достигать 10-50 раз, в частности, для рабочих нагрузок с интенсивной записью. Подробнее см. здесь.

Итак, о настройках MySQL Innodb автор написал в Innodb Performance Optimization. Основы:

Наиболее важные из них:

innodb_buffer_pool_size 70-80% памяти — это безопасная ставка. Я установил его на 12G на коробке 16GB.

ОБНОВЛЕНИЕ. Если вам нужны дополнительные сведения, ознакомьтесь с подробным руководством на странице настройка буферного пула innodb

innodb_log_file_size — это зависит от ваших потребностей в скорости восстановления, но 256M кажется хорошим балансом между разумным временем восстановления и хорошей производительностью.

innodb_log_buffer_size=4M 4M подходит для большинства случаев, если только вы не передаете большие BLOB-объекты в Innodb, в этом случае увеличьте его немного.

innodb_flush_log_at_trx_commit=2 Если вы не беспокоитесь о ACID и можете потерять транзакции в течение последней секунды или двух в случае полного сбоя ОС, установите это значение. Это может сильно повлиять, особенно на множество коротких транзакций записи.

innodb_thread_concurrency=8 Даже с текущими исправлениями Innodb Scalability Fix с ограниченным параллелизмом помогает. Фактическое число может быть больше или меньше в зависимости от вашего приложения и по умолчанию, которое равно 8, является достойным началом.

innodb_flush_method=O_DIRECT Избегайте двойной буферизации и уменьшайте давление подкачки. В большинстве случаев этот параметр повышает производительность. Однако будьте осторожны, если у вас нет кэш-памяти RAID с резервным питанием от батареи, так как при записи IO может пострадать.

innodb_file_per_table — если у вас не слишком много таблиц, используйте эту опцию, поэтому у вас не будет неконтролируемого роста основного табличного пространства innodb, которое вы не сможете восстановить. Эта опция была добавлена ​​в MySQL 4.1 и теперь достаточно стабильна для использования.

Также проверьте, может ли ваше приложение работать в режиме изоляции READ-COMMITED. Если да, установите его по умолчанию как transaction-isolation=READ-COMMITTED. Этот вариант имеет некоторые преимущества в производительности, особенно в блокировке 5.0 и даже больше, чтобы прийти с MySQL 5.1 и репликацией на уровне строк.

Для протокола: люди, стоящие за mysqlperformanceblog.com, провели тест, сравнивающий Falcon, MyISAM и InnoDB. На самом деле предполагалось, что эталонный тест будет выделять Falcon, но InnoDB выиграла день, превзойдя как Falcon, так и MyISAM по количеству запросов в секунду почти в каждом тесте: Тестирование InnoDB, MyISAM и Falcon — часть 1.

person Pascal Thivent    schedule 15.09.2009
comment
Спасибо вам за публикацию. У меня уже установлены многие из этих параметров (см. конец вопроса), и экземпляр mysql действительно занимает около 50% моей памяти, но все еще ужасно работает с InnoDB и MyISAM. - person Paul Tarjan; 16.09.2009

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

Тот, который вызывал у меня самые заметные проблемы с производительностью, был innodb_buffer_pool_size. Это должно быть установлено на 50-80% памяти вашей машины. По умолчанию это часто всего несколько МБ. Увеличьте его сильно, и вы увидите заметный прирост производительности.

Также взгляните на innodb_additional_mem_pool_size.

Начните здесь, а также поищите в Google "innodb настройка производительности".

person zombat    schedule 15.09.2009
comment
У меня есть эти настройки: innodb_file_per_table innodb_buffer_pool_size = 2048M innodb_additional_mem_pool_size = 16M innodb_flush_method=O_DIRECT - person Paul Tarjan; 15.09.2009

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

SELECT * FROM (SELECT * FROM `metaward_achiever` 
               WHERE `metaward_achiever`.`award_id` = 1507) a
INNER JOIN `metaward_alias` ON (a.`alias_id` = `metaward_alias`.`id`) 
ORDER BY a.`modified` DESC 
LIMIT 100

Или что-то в этом роде (непроверенный синтаксис выше).

person Steven Huwig    schedule 15.09.2009
comment
ваш запрос занимает всего 10 секунд, но myISAM был в миллисекундах. Почему? - person Paul Tarjan; 15.09.2009

Сортировка выполняется сервером базы данных, а не механизмом хранения в MySQL.

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

Единственная причина, по которой MyISAM / InnoDB может отличаться, заключается в том, что порядок возвращаемых строк может повлиять на то, как уже отсортированы данные - MyISAM может вернуть данные в «более отсортированном» порядке в некоторых случаях ( наоборот).

Тем не менее, сортировка 60 тыс. строк не займет много времени, так как это очень маленький набор данных. Вы уверены, что у вас достаточно большой буфер сортировки?

Использование файловой сортировки на диске вместо встроенной в память намного медленнее. Однако двигатель не должен иметь никакого значения для этого. filesort — это не функция движка, а основная функция MySQL. На самом деле, filesort довольно много работает, но обычно он не такой медленный.

person MarkR    schedule 20.09.2009
comment
+1 Я прокомментировал это выше, но пропустил ваш комментарий. Право на! - person Morgan Tocker; 24.07.2010

Попробуйте добавить ключ для полей: metaward_achiever.alias_id, metaward_achiever.award_id и metaward_achiever.modified это очень поможет. И не используйте ключи в полях varchar, это увеличит время для вставок и обновлений. Также кажется, что у вас есть 77 миллионов записей в таблице достижений, вы можете позаботиться об оптимизации innodb. Есть много хороших наставников о том, как установить для него ограничения памяти.

person Cem Kalyoncu    schedule 15.09.2009
comment
У меня есть 2 ключа: KEY metaward_achiever_award_id (award_id), KEY metaward_achiever_alias_id (alias_id). Должны ли вы действительно помещать ключи в поле даты и времени? Что касается ключей varchar, я действительно не слишком часто обновляю эти таблицы, но почему я застрял с этим 49-секундным обновлением? Я много выбираю поля varchar, поэтому я подумал, что ключ поможет. - person Paul Tarjan; 15.09.2009
comment
Попробуйте проиндексировать поля вместе: metaward_achiever_combined (award_id, alias_id, modified). Сделал чудеса для моего времени поиска. - person Ryall; 08.08.2012