Запрос на удаление MySQL слишком медленный и выполняется вечно

#mysql #performance

Вопрос:

У меня есть таблица MySQL (TABLE1) с 400 тысячами записей

 CREATE TABLE `TABLE1` (
  `ID` bigint(20) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(255) NOT NULL,
  `VALUE` varchar(255) NOT NULL,
  `UID` varchar(255) NOT NULL,
  `USER_ID` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `ukey1` (`VALUE`,`NAME`,`UID`),
  UNIQUE KEY `ukey2` (`UID`,`NAME`,`VALUE`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `TABLE2` (
  `ID` bigint(20) NOT NULL AUTO_INCREMENT,
  `UID` varchar(255) DEFAULT NULL,
  `TABLE3ID` bigint(20) NOT NULL
  PRIMARY KEY (`ID`),
  KEY `FKEY` (`TABLE3ID`),
  CONSTRAINT `FKEY` FOREIGN KEY (`TABLE3ID`) REFERENCES `TABLE3` (`ID`),
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

CREATE TABLE `TABLE3` (
  `ID` bigint(20) NOT NULL AUTO_INCREMENT,
  `TYPEID` bigint(20) NOT NULL,
  PRIMARY KEY (`ID`) 
) ENGINE=InnoDB  DEFAULT CHARSET=utf8

 

Следующий запрос выполняется очень медленно, занимает несколько часов и, наконец, завершается ошибкой

 delete from TABLE1 t1 
inner join TABLE2 t2 on t1.UID=t2.UID
inner join TABLE3 t3 on t2.TABLE3ID=t3.ID  
where t3.TYPEID in (234,3434) t1.USER_ID is not null and t1.USER_ID <> '12345';
 

Визуальное объяснение показывает следующее, и добавление индекса в UID не помогает. Как оптимизировать производительность этого запроса?

введите описание изображения здесь

  • Я попытался добавить индекс в таблицу 1.UID
  • Преобразование в подзапрос
  • Такой простой запрос, как SELECT * FROM TABLE3 where UID="SOMEUID" получение данных, занимает более 800 мс

Комментарии:

1. Используйте JOIN «а», а не «чем WHERE UID IN (subquery) «. Я обнаружил, что MySQL реализует последнее очень неэффективно.

2. Пожалуйста, добавьте точную версию MySQL и подзапрос тоже. Спасибо

3. У вас есть какие-нибудь показатели на столе? Было бы полезно иметь индекс (UID); индекс (UID, USER_ID) может быть еще лучше.

4. @AleksG Индексы указаны в вопросе. Он сказал, что добавление индекса в UID не помогло.

5. В UID уже есть индекс, так как это префикс ukey2 индекса.

Ответ №1:

Измените его на СОЕДИНЕНИЕ.

 DELETE t1
FROM TABLE1 AS t1
JOIN (SELECT uid FROM ...) AS t2 ON t1.uid = t2.uid
WHERE USER_ID is not null and USER_ID <> '12345';
 

Я обнаружил, что MySQL иногда реализует WHERE uid IN (subquery) очень плохо. Вместо того, чтобы получать все результаты подзапроса и искать их в индексе таблицы, он сканирует таблицу и выполняет подзапрос для каждой строки, а затем проверяет, есть ли uid в этом результате.

Комментарии:

1. Спасибо, На самом деле, это было ЗДОРОВО. Я опустил это для краткости. Обновил вопрос

2. Основной проблемой, по-видимому, является ТАБЛИЦА 1 с 300 тысячами записей, и в таблице есть столбец UID varchar(255)

Ответ №2:

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

( uid1,uid2,…uid45000)

Каково значение этих значений между скобками ? Вам нужно сравнить в списке все значения UID или некоторые из них?

потому что вы можете избежать размещения всех UID вручную, как это.

удалите из ТАБЛИЦЫ 1, где UID в (ВЫБЕРИТЕ T. UID ИЗ ТАБЛИЦЫ 1 как T, где T. UID не равен NULL, и ИДЕНТИФИКАТОР ПОЛЬЗОВАТЕЛЯ <> ‘12345’);

Прежде чем сделать это, пожалуйста, проверьте, что вы хотите между скобками, и сначала выполните команду в тестовой среде с фиктивными значениями

Примите во внимание, что у вас в таблице есть типы varchars в поле UIDS, и именно по этой причине эта операция занимает намного больше времени, чем если бы вы использовали целочисленные значения

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

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

Ответ №3:

Я бы разделил ваш список фильтров uid на куски (100 по кускам или другим, нужно проверить) и повторил или многопоточно по нему