Nano Hash - криптовалюты, майнинг, программирование

MySQL INSERT SELECT Большой набор данных из 420 миллионов записей

У меня есть большой набор данных примерно из 420 миллионов записей, и я смог своевременно загрузить их во временную таблицу примерно за 15 минут, используя оператор LOAD DATA INFILE. Мне нужна эта временная таблица для хранения данных, потому что я выполняю некоторую очистку перед загрузкой в ​​конечный пункт назначения.

Временная таблица определяется как:

CREATE TABLE `temporary_data` (
  `t_id` smallint(10) unsigned NOT NULL,
  `s_name` varchar(512) NOT NULL,
  `record_type` varchar(512) NOT NULL,
  `record_value` varchar(512) NOT NULL
) ENGINE=MyISAM;

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

CREATE TABLE `my_data` (
  `s_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `t_id` smallint(10) unsigned NOT NULL,
  `s_name` varchar(63) NOT NULL,
  PRIMARY KEY (`s_id`),
  UNIQUE KEY `IDX_MY_DATA_S_NAME_T_ID` (`t_id`,`s_name`) USING BTREE,
  KEY `IDX_MY_DATA_S_NAME` (`s_name`) USING BTREE,
  CONSTRAINT `FK_MY_DATA_MY_PARENT` FOREIGN KEY (`t_id`) REFERENCES `my_parent` (`t_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

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

INSERT IGNORE INTO my_data (t_id, s_name)
SELECT t_id, s_name
FROM temporary_data;

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

Некоторые подходы, которые я рассматривал:

  1. Отключение индексов. Возможно, мне удастся отключить/удалить индекс IDX_MY_DATA_S_NAME, но я полагаюсь на уникальный индекс (IDX_MY_DATA_S_NAME_T_ID), чтобы данные оставались чистыми. Это ежедневный процесс, который будет выполняться автоматически, и неизбежно будут некоторые дубликаты. Кроме того, кажется, что перестроение индекса для такого большого набора данных, когда я снова включу индекс, займет столько же времени.
  2. Использовать DATA OUTFILE: экспортировать и повторно импортировать очищенные данные непосредственно в my_data. Я где-то видел, что это рекомендуется, но, подумав об этом, индексы/PK все равно будут спорным моментом при повторной вставке.
  3. Поменяйте местами таблицы. Замена my_data на temporary_data звучит привлекательно, но эта таблица имеет множество взаимосвязей внешних ключей для поля s_id, поэтому я хочу быть уверенным, что этот подход стоит хлопот с отключением внешних ключей и повторной включение их. Дочерние таблицы будут содержать значительно меньше записей, чем my_data, поэтому в этом отношении повторное включение внешних ключей может быть незначительным.
  4. ЗАГРУЗИТЬ ДАННЫЕ В ФАЙЛ напрямую: загружать данные непосредственно в my_data, используя условные операторы в части оператора SET, чтобы сделать все поля NULL, если они не соответствуют критериям очистки, которые я первоначально применил к temporary_data перед загрузкой в my_data. Это хакерство, но оно основано на предположении, что LOAD DATA INFILE будет быстрее, чем INSERT... SELECT даже перед лицом индексации, и будет только одна строка нулей для удаления после ее запуска из-за уникального ограничения на таблицу .

Ни одна из этих идей не похожа на ужасно великую. Если у кого-то есть какие-либо советы, я весь слух.


  • Что показывает show status like '%inno%wait%';? 26.10.2018
  • Innodb_buffer_pool_wait_free 0 Innodb_log_waits 0 Innodb_row_lock_current_waits 0 Innodb_row_lock_waits 0 26.10.2018
  • Я использую отдельный файл для каждого режима таблицы и вижу, что файл .ibd продолжает расти. show processlist показывает, что он также выполняется в состоянии sending data. 26.10.2018
  • Ознакомьтесь с советами и методами здесь . 27.10.2018
  • Спасибо за ссылку. Я обязательно это проверю. 28.10.2018

Ответы:


1

Избавьтесь от s_id, возможно, он уже не используется. Затем продвигайте UNIQUE(t_id, s_name) to be thePRIMARY KEY`. Это сокращает количество тестов для каждой вставленной строки.

Рассмотрите возможность отключения FOREIGN KEYs; в конце концов, им нужно выполнить проверку, которая может оказаться избыточной.

INSERT IGNORE INTO my_data (t_id, s_name)
    SELECT t_id, s_name
    FROM temporary_data
    ORDER BY t_id, s_name;  -- Add this

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

Вы дополняете таблицу? Или заменить его? Если заменить, есть гораздо лучший подход.

Более...

Вы заметили, что INSERT IGNORE теряет значение AUTO_INCREMENT для каждой не вставленной строки? Попробуем другой подход...

INSERT INTO my_data (t_id, s_name)
    SELECT t.t_id, t.s_name
        FROM temporary_data AS t
        LEFT JOIN my_data AS m  USING(t_id, s_name)
        WHERE m.s_id IS NULL
        ORDER BY t.t_id, t.s_name;

ORDER BY позволяет избежать скачков во время INSERT.
LEFT JOIN ограничивает активность "новыми" строками.
Никакие значения AUTO_INCREMENT не будут сожжены.

Сколько строк будет вставляться каждый раз? Если миллионы, то лучше разбить на куски. См. мое обсуждение по фрагментации. Это может быть быстрее, чем строить огромную цепочку отмен, чтобы в конечном итоге выбросить.

Дальнейшее обсуждение -- Учитывая

my_data:  PRIMARY KEY(s_id)  -- and s_id is AUTO_INCREMENT
my_data:  INDEX(t_id, s_name)
INSERT...SELECT...ORDER BY (t_id, s_name)  -- same as index

Они эффективны:

  • Поскольку ORDER BY и вторичный индекс одинаковы, добавления к индексу будут выполняться эффективно.
  • При этом новые значения AUTO_INCREMENT будут генерироваться последовательно в «конце» таблицы.

Единственное, было бы лучше, если бы (t_id, s_name) были уникальными. Тогда мы могли бы полностью избавиться от s_id и изменить два индекса на этот:

PRIMARY KEY(t_id, s_name)

Это было бы проблемой, если бы другие таблицы ссылались на s_id. Возможный обходной путь — сохранить s_id и иметь

PRIMARY KEY(t_id, s_name)
INDEX(s_id)   -- sufficient for AUTO_INCREMENT

Я недостаточно знаю общую картину и другие вопросы, чтобы судить, в каком направлении двигаться. Итак, мое первоначальное предложение (до «Дальнейшего обсуждения») было «консервативным».

27.10.2018
  • К сожалению, я увеличиваю. Это ежедневная работа ETL, которую я создаю для загрузки 420 миллионов записей. Цель состоит в том, чтобы добавлять новые и никогда не удалять их. Он закончился примерно через 12 часов. Я не могу избавиться от s_id, потому что он используется в качестве внешнего ключа примерно для семи других таблиц, которые будут намного более разреженными, чем то, что хранится в my_data. Я разработал его таким образом, чтобы не хранить кучу пустых полей для необязательных данных, которые появятся позже, тем самым сэкономив немного места. Я попробую новый запрос с предложением ORDER BY и посмотрю, поможет ли это. Я также работаю над переходом на SSD. 28.10.2018
  • @Adam - я добавил больше к своему ответу. @Adam 28.10.2018
  • Вы бы по-прежнему рекомендовали сохранить пункт ORDER BY t.t_id, t.s_name;, если ПК останется s_id? Несмотря на то, что составной уникальный индекс t_id и s_name не является PK, принудительная вставка их в одном и том же порядке каждый раз заставит его вставляться в порядке PK, верно? 19.11.2018
  • @Adam Адам - ​​смотрите мое дальнейшее обсуждение. 19.11.2018
  • Спасибо за вашу помощь в этом. Интересно, что когда я сейчас запускаю запрос с ORDER BY, я вижу его в списке процессов, создающем индекс сортировки из-за предложения ORDER BY, и запуск df -h /mnt/* в моем ящике Digital Ocean показывает размер тома. У меня MySQL tmpdir постоянно увеличивается. Затем примерно через 20-30 минут список процессов показывает, что команда сейчас находится в спящем состоянии, а пространство tmpdir освобождено. Результаты не вставляются и ошибки нет. Он просто говорит, что процесс спит. 20.11.2018
  • @Adam - Sleep означает, что соединение все еще существует (возможно, в пуле), но в данный момент соединение не выполняет никакого SQL. Это нормально и безвредно. Постепенное увеличение и резкое сокращение tmpdir используемого пространства означает, что запрос был достаточно сложным, чтобы нуждаться во временной таблице на диске. В некоторых ситуациях этого можно избежать. Возможно снижение 512 позволит избежать необходимости хранения временных файлов на диске. 20.11.2018
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..