Отредактировано - MySQL. Большая таблица MyISAM (40 миллионов записей), имеющая очень медленный и огромный по размеру индекс на диске - PullRequest
3 голосов
/ 05 февраля 2010

Таблица содержит около 40 000 000 записей, имеющих:

CREATE TABLE `event` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `some_other_id_not_fk` int(10) unsigned default NOT NULL,
  `event_time` datetime NOT NULL,
  `radius` float default NULL,
  `how_heavy` smallint(6) default NULL,
  PRIMARY KEY  (`id`),
  KEY `event_some_other_id_not_fk` (`some_other_id_not_fk`),
  KEY `event_event_time` (`event_time`)
) ENGINE=MyISAM AUTO_INCREMENT=6506226 DEFAULT CHARSET=utf8 

Вы должны знать, что столбец some_other_id_not_fk не большой, он содержит только 7 различных чисел. Реальная боль - это столбец event_time datetime, так как он содержит чрезвычайно большое количество различных datetime, и в основном все разрешено: дубликаты, а также непредсказуемо большие интервалы времени без записей, чтобы «покрыть» их. Вы также должны знать, что пара (some_other_id_not_fk, event_time) должна иметь дубликаты :( Я знаю, что это вызывает еще больше проблем: (

У меня был некоторый опыт в оптимизации таблиц MySQL, но такая огромная боль никогда не появлялась на моем горизонте: /

Текущее состояние «вещей»:

  • Выбор на event_time между датой 1 и датой 2 (что мне нужно сделать) выполняется достаточно быстро. :)
  • Мои вставки медленные, я имею в виду действительно МЕДЛЕННО более 30 секунд и даже хуже: процедуры LOAD DATA, которые временно ОТКЛЮЧАЮТ и ВКЛЮЧАЮТ КЛЮЧИ, являются ОЧЕНЬ медленными (несколько часов), в основном при работе с ключами ENABLE.
  • Размер индекса на диске в 7 раз больше размера данных

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

Помогите, пожалуйста, кому-нибудь это удалось? Должно ли использование временной метки вместо datetime решить мою проблему? Или, может быть, мне следует добавить дополнительные столбцы для day, year, ... и т. Д. И индексировать их?

Ответы [ 5 ]

12 голосов
/ 05 февраля 2010
`id` bigint(20) unsigned NOT NULL auto_increment,

Тебе действительно нужен BIGINT? Вы, вероятно, можете сойти с INT. Если бы вы вставляли 1000 строк в секунду 24 часа в сутки, вам потребовалось бы 136 лет, чтобы исчерпать все значения в 32-разрядном целом числе без знака.

Это изменение уменьшит размер таблицы на 152,5 МБ для 40 миллионов строк и уменьшит размер индекса первичного ключа на 158,8 МБ для 40 миллионов строк.

`some_other_id_not_fk` int(10) unsigned default NOT NULL,

Вы заявляете, что это имеет только 7 различных значений. Это должен быть тип INT тогда? Не могли бы вы использовать вместо TINYINT? Это резко уменьшит размер индекса.

Это уменьшит размер вашей таблицы на 114,4 МБ для 40 миллионов строк и уменьшит размер индекса some_other_id_not_fk примерно на столько же.

`event_time` datetime NOT NULL,

Вам нужен DATETIME? DATETIME занимает 8 байтов, TIMESTAMP занимает 4 байта. Если вы можете использовать TIMESTAMP, это значительно сократит размер данных и индекса. Помните об ограничениях полей TIMESTAMP, таких как Y2K38 , и о том, как они ведут себя относительно часовых поясов и репликации.

Это изменение уменьшит размер таблицы на 152,5 МБ для 40 миллионов строк и уменьшит размер индекса первичного ключа на 158,8 МБ для 40 миллионов строк.

Эти три изменения значительно уменьшат размер ваших данных, а также индексов.

Общая экономия пространства

  • Таблица: 152,5 + 152,5 + 114,4 = 419,4 МБ
  • Индекс: 158,8 + 158,8 + ~ 115 = 432,6 МБ

Всего : 852 МБ

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

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

Кроме того, вы вставляете в эту таблицу под большой нагрузкой чтения? Имейте в виду, что SELECTs в MyISAM заблокирует INSERT.

Обновление

Большинство людей предлагают переместить ваше поле some_other_id_not_fk в индекс event_time, чтобы новый индекс был на (event_time, some_other_id_not_fk). Я буду рекомендовать то же самое, но с важным предостережением.

Этот индекс будет полезен для запросов, для которых вы фильтруете только по event_time, или если вы фильтруете по event_time и some_other_id_not_fk. не будет использоваться для фильтрации запросов только на some_other_id_not_fk - произойдет полное сканирование таблицы.

Более того, если ваши запросы всегда фильтруются по и event_time и some_other_id_not_fk, тогда не используют порядок индекса (event_time, some_other_id_not_fk). Вместо этого вам следует использовать индекс (some_other_id_not_fk, event_time).

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

2 голосов
/ 05 февраля 2010

Я думаю, что ваша интуиция о том, что тяжело, а что нет, отстала: индекс с большим количеством повторений нескольких различных вариантов намного хуже индекса с большим количеством различных значений и несколькими повторениями каждого. 1003 *

Мое предложение: сбросьте индекс на some_other_id_not_fk и оставьте (some_other_id_not_fk, event_time). Этот составной индекс должен быть «почти уникальным», что делает накладные расходы намного ниже. Если возможно, отпустите клавишу event_time, если только у вас нет запросов, которые используют это поле без some_other_id_not_fk.

edit : вы говорите, что вам нужно выбрать по временному интервалу, затем удерживайте (event_time, some_other_id_not_fk) и отбрасываете event_time и some_other_id_not_fk. если у вас есть запросы, использующие some_other_id_not_fk, а не event_time, оставьте как (event_time, some_other_id_not_fk), так и (some_other_id_not_fk, event_time). Дело в том, что нет никакого индекса с несколькими вариантами. хорошо иметь индекс с неиспользуемыми полями справа.

1 голос
/ 05 февраля 2010

Я думаю, что вам не нужен индекс для some_other_id_not_fk (как вы сказали, есть только 7 различных значений, поэтому селективность этого индекса составляет 40 000 000/7). Все, что вам нужно, это 1 индекс (event_time + [возможно] some_other_id_not_fk);

0 голосов
/ 06 февраля 2010

Я удалил все индексы и сделал индекс на (event_time, some_other_id_not_fk). Я получаю следующие показатели эффективности:

  • 1 ГБ Размер данных на диске, 1,2 ГБ Размер индекса на диске.

  • удалить из event, где event. event_time> STR_TO_DATE ('20091201000000', '% Y% m% d% H% i% s') и event. some_other_id_not_fk = 4 | Затрагиваемые строки: 353543 Время: 65,173 секунды

  • выберите * из event, где event. event_time> STR_TO_DATE ('20090401000000', '% Y% m% d% H% i% s') и event. event_time <= STR_TO_DATE ('20090401010000', '% Y% m% d% H% i% s') и <code>event. some_other_id_not_fk in (22,4,1,3) | 916 строк в наборе, время запроса: 0,030 секунд

  • Индекс включил вставку 350 000 новых записей в следующем формате: вставить в event VALUES (...), (...), ... | выполняется примерно за 30 секунд, даааааааа:))

  • индекс отключен - вставка - индекс включен - 350 000 новых записей в том же формате: вставить в event VALUES (...), (...), ... | выполняется примерно за 40 минут. :) Похоже, формат дампа mysql по умолчанию, отключение индекса перед вставкой и повторное включение его после, не всегда хорошо влияет на производительность, особенно при наличии индексов большого размера:)

Пока я доволен этим спектаклем.

Вчера вечером мне удалось создать индекс только для (event_time). Размер индекса был немного ниже, чем в первом примере. О 1.1Gb. Выполнение тех же запросов, что указаны выше:

  • удаление | немного быстрее, около 30 секунд
  • выбор | немного медленнее, примерно 0,1 секунды.
  • Я только что протестировал вставку индекса отключения-включения 350 000. Это было очень медленно снова | около 35 минут.

    Я отклонил это состояние базы данных, потому что я не был удовлетворен достаточной скоростью выбора, которая для меня является приоритетом N1.

hobodave, мне просто любопытно, вы думаете, что создание индекса (some_other_id_not_fk, event_time) вместо (event_time, some_other_id_not_fk) действительно изменит что-то драматическое в сторону улучшения? Мои запросы ВСЕГДА будут фильтроваться в обоих полях. Я НИКОГДА не получу запрос без фильтрации по some_other_id_not_fk. Но у меня могут быть запросы, которые фильтруют по IN (x, y, ...) большинства различных some_other_id_not_fk. Как я уже сказал, их не много.

Мои приоритеты:

  1. выберите скорость
  2. скорость вставки
  3. размер индекса на диске (так как таблица вырастет в несколько раз)
    ... все остальное

И мне также интересно, почему такой огромный размер индекса требует 1,2 ГБ для данных 1 ГБ? Индекс еще больше, чем данные. Моя логика подсказывает мне, что такого рода индексация дат может быть выполнена в гораздо меньшем индексе? Я прав? Есть ли что-то связанное с типом индекса, который, вероятно, BTREE?

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

0 голосов
/ 05 февраля 2010

У меня была похожая ситуация. Я создал таблицу с такой же структурой, назовем ее архивной таблицей. Я копировал данные из активной таблицы в нее каждый день в 3:00 и удалял ВСЕ оригинал.

Графики и другая статика были select отредактированы из архивной таблицы, текущие события записаны в активную.

Может быть, это не лучшая практика, но для меня это сработало.

Таблица разделов по времени: Разделение с датами в MySQL 5.1 (Робин Шумахер)

http://dev.mysql.com/tech-resources/articles/mysql_5.1_partitioning_with_dates.html

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