Таблица с 80 миллионами записей и добавлением индекса занимает более 18 часов (или навсегда)! Что теперь? - PullRequest
30 голосов
/ 12 сентября 2010

Краткое резюме того, что произошло.Я работаю с 71 миллионами записей (не так много по сравнению с миллиардами записей, обработанных другими).В другом потоке кто-то предположил, что текущая настройка моего кластера не подходит для моих нужд.Моя структура таблицы:

CREATE TABLE `IPAddresses` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `ipaddress` bigint(20) unsigned default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM;

И я добавил 71 миллион записей, а затем сделал:

ALTER TABLE IPAddresses ADD INDEX(ipaddress);

Прошло 14 часов, а операция все еще не завершена.На Google, я обнаружил, что существует известный подход для решения этой проблемы - разделение.Я понимаю, что теперь мне нужно разбить таблицу на основе IP-адреса, но могу ли я сделать это без воссоздания всей таблицы?Я имею в виду, через заявление ALTER?Если да, то было одно требование о том, что столбец, на который нужно разбить раздел, должен быть первичным ключом.Я буду использовать идентификатор этого ipaddress при создании другой таблицы, поэтому ipaddress не является моим основным ключом.Как мне разбить мою таблицу с учетом этого сценария?

Ответы [ 5 ]

37 голосов
/ 17 сентября 2010

Хорошо, получается, что эта проблема была чем-то большим, чем просто создание таблицы, индексирование ее и забытие проблемы :) Вот что я сделал на случай, если кто-то еще столкнется с той же проблемой (я использовал пример IP-адреса, но работает и для других типов данных):

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

Вариант использования: Рассмотрите возможность хранения миллионов IP-адресов в справочной таблице. Добавление IP-адресов не должно быть большой проблемой, но создание индекса для них занимает более 14 часов.

Решение : Разделите вашу таблицу, используя Разделение MySQL в g стратегии

Случай № 1: Когда требуемая таблица еще не создана

CREATE TABLE IPADDRESSES(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  ipaddress BIGINT UNSIGNED,
  PRIMARY KEY(id, ipaddress)
) ENGINE=MYISAM
PARTITION BY HASH(ipaddress)
PARTITIONS 20;

Случай № 2: Когда требуемая таблица уже создана. Кажется, есть способ использовать ALTER TABLE, чтобы сделать это, но я еще не нашел правильного решения для этого. Вместо этого есть немного неэффективное решение:

CREATE TABLE IPADDRESSES_TEMP(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  ipaddress BIGINT UNSIGNED,
  PRIMARY KEY(id)
) ENGINE=MYISAM;

Вставьте ваши IP-адреса в эту таблицу. А затем создайте фактическую таблицу с разделами:

CREATE TABLE IPADDRESSES(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  ipaddress BIGINT UNSIGNED,
  PRIMARY KEY(id, ipaddress)
) ENGINE=MYISAM
PARTITION BY HASH(ipaddress)
PARTITIONS 20;

И, наконец,

INSERT INTO IPADDRESSES(ipaddress) SELECT ipaddress FROM IPADDRESSES_TEMP;
DROP TABLE IPADDRESSES_TEMP;
ALTER TABLE IPADDRESSES ADD INDEX(ipaddress)

И все ... индексирование новой таблицы заняло у меня около 2 часов на машине с частотой 3,2 ГГц и 1 ГБ ОЗУ :) Надеюсь, это поможет.

7 голосов
/ 30 апреля 2011

Создание индексов с MySQL идет медленно, но не так медленно.С 71 миллионами записей это должно занять пару минут, а не 14 часов.Возможные проблемы:

  • вы не настроили размеры буфера сортировки и другие параметры конфигурации

посмотрите здесь: http://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_myisam_sort_buffer_size

Если вы попытаетесь сгенерироватьиндекс 1 ГБ с буфером сортировки 8 МБ, он будет проходить много проходов.Но если буфер больше, чем ваш кэш процессора, он будет работать медленнее.Поэтому вы должны проверить и посмотреть, что работает лучше.

  • у кого-то есть блокировка на столе
  • ваша система ввода-вывода отстой
  • ваш сервер меняет
  • и т. Д.

как обычно, проверьте iostat, vmstat, logs и т. Д. Запустите LOCK TABLE на своем столе, чтобы проверить, не заблокирован ли кто-либо на нем.

FYI на моем столеСоздание 64-битного десктопа на 10М случайных BIGINT занимает 17 с ...

5 голосов
/ 23 января 2015

У меня была проблема, когда я хотел ускорить мой запрос, добавив индекс.В таблице было только около 300 000 записей, но это также заняло слишком много времени.Когда я проверил процессы сервера mysql, оказалось, что запрос, который я пытался оптимизировать, все еще работал в фоновом режиме.4 раза!После того, как я убил эти запросы, индексация была сделана в один миг.Возможно, та же проблема относится к вашей ситуации.

3 голосов
/ 17 сентября 2010

Вы используете MyISAM, который скоро устареет. Альтернативой будет InnoDB.

"InnoDB - это безопасный для транзакций (совместимый с ACID) механизм хранения для MySQL, который имеет функции фиксации, отката и восстановления после сбоя для защиты пользовательских данных. Блокировка на уровне строк InnoDB (без эскалации до грубых блокировок гранулярности) и Oracle- согласованные по стилю считывания без блокировки увеличивают многопользовательский параллелизм и производительность. InnoDB хранит пользовательские данные в кластеризованных индексах, чтобы уменьшить количество операций ввода-вывода для общих запросов на основе первичных ключей. Для поддержания целостности данных InnoDB также поддерживает ограничения ссылочной целостности FOREIGN KEY. Вы можете свободно смешивать таблицы InnoDB с таблицами из других механизмов хранения MySQL, даже в пределах одного и того же оператора. "\

http://dev.mysql.com/doc/refman/5.0/en/innodb.html

Согласно:

http://dev.mysql.com/tech-resources/articles/storage-engine/part_1.html

, вы должны иметь возможность переключаться между различными движками, используя простую команду alter, которая дает вам некоторую гибкость. В нем также говорится, что каждая таблица в вашей БД может быть настроена независимо.

0 голосов
/ 14 июня 2013

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

CREATE TABLE t1 (
    id INT,
    year_col INT
);

ALTER TABLE t1
    PARTITION BY HASH(id)
    PARTITIONS 8;
...