Команда DELETE не завершена в таблице строк 30000000

22

Я унаследовал базу данных и ищу, чтобы очистить и ускорить ее. У меня есть таблица, которая содержит 30 000 000 строк, многие из которых являются ненужными данными, вставленными из-за ошибки от имени нашего программиста. Прежде чем добавлять какие-либо новые, более оптимизированные индексы, я преобразовал таблицу из MyISAM в InnoDB и собираюсь удалить множество строк, содержащих нежелательные данные.

База данных MySQL 5.0, и у меня есть root-доступ к серверу. Сначала я запускал эти команды через Adminer, а затем phpMyAdmin, оба с одинаковыми результатами.

Я запускаю команду

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%'

По сути, удалите в этом столбце все, что начинается с тире -.

Он работает в течение 3-5 минут, а затем, когда я просматриваю список процессов, он исчез.

Я бегу,

SELECT *
FROM `tablename`
WHERE `columnname` LIKE '-%'

и он возвращает миллионы строк.

Почему мое заявление об удалении не завершается?

PS, я в курсе того, насколько устарел MySQL 5.0. Я работаю над переносом БД на MySQL 5.6 с InnoDB (может быть MariaDB 10 с XtraDB), но пока этого не произойдет, я жду ответа с БД как есть.

-

Редактирование удалено, смотрите мой ответ.

bafromca
источник

Ответы:

24

Посмотрите, пожалуйста, на архитектуру InnoDB (картинка от технического директора Percona Вадима Ткаченко)

InnoDB Сантехника

Строки, которые вы удаляете, записываются в журналы отмены. Файл ibdata1 должен расти прямо сейчас на время удаления. Согласно mysqlperformanceblog.comReasons for run-away main Innodb Tablespace :

  • Много транзакционных изменений
  • Очень длинные транзакции
  • Отстающий поток продувки

В вашем случае причина # 1 будет занимать один сегмент отката вместе с частью пространства отмены, поскольку вы удаляете строки. Эти строки должны находиться в ibdata1, пока удаление не будет завершено. Это пространство логически отбрасывается, но дисковое пространство не уменьшается.

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

Вы делаете это вместо этого:

CREATE TABLE tablename_new LIKE tablename;
INSERT INTO tablename_new SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%';
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

Вы могли бы сделать это сначала против версии таблицы MyISAM. Затем преобразуйте его в InnoDB.

RolandoMySQLDBA
источник
21

Я думаю, что мы, возможно, слишком усложнили ответ, который требовался в моем случае . Я не сомневаюсь, что и Роланд, и Рик Джеймс правы в создании временной таблицы, добавляя только строки, которые проходят через фильтр, NOT LIKE '-%'но решение для меня было «проще», потому что произошла важная ошибка, о которой я не знал до сих пор, и для что я прошу прощения.

Я запустил запрос в mysqlинтерактивном режиме и заметил сообщение об ошибке,

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
ERROR 1206 (HY000): The total number of locks exceeds the lock table size

Через Googleing я обнаружил, что решение заключается в увеличении с innodb_buffer_pool_sizeпомощью /etc/my.cnfфайла и перезагрузке демона mysql. Для моего сервера было установлено значение по умолчанию, 8Mи я увеличил его 1G(сервер имеет 32 ГБ, и это единственная таблица, которая в настоящее время является InnoDB).

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
Query OK, 23517226 rows affected (27 min 33.23 sec)

Тогда я смог запустить команду и удалить 23 миллиона записей за ~ 27 минут.

Для тех, кому интересно, что innodb_buffer_pool_sizeнужно установить, обратите внимание на то, сколько у вас ОЗУ, а затем взгляните на этот поток, который дает предполагаемую оценку в ГБ.

bafromca
источник
12

Предложение Роланда можно ускорить, выполнив обе вещи одновременно:

CREATE TABLE tablename_new LIKE tablename;
ALTER TABLE tablename_new ENGINE = InnoDB;
INSERT INTO tablename_new 
    SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%' ORDER BY primary_key;
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

Но вот блог, который объясняет, как делать большие УДАЛЕНИЯ в кусках, а не, казалось бы, вечно: http://mysql.rjweb.org/doc.php/deletebig Суть в том, чтобы пройти через таблицу через ПК, выполняя 1K строки сразу. (Конечно, есть больше деталей, о которых нужно знать.)

И этот блог рассматривает потенциальные ошибки при переходе на InnoDB: http://mysql.rjweb.org/doc.php/myisam2innodb

Рик Джеймс
источник
5

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

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%' LIMIT 1000000
kristianp
источник
Недостаток этого подхода: каждое удаление будет длиться все дольше. Это потому, что нужно пропустить все больше и больше строк, которые не соответствуют WHERE.
Рик Джеймс
Верно, но если этот процесс не происходит слишком часто, многократное сканирование таблицы не должно быть таким же плохим, как исходная решаемая проблема: запрос никогда не завершается из-за размера журнала отмены.
Кристианп
Действительная точка. (Я бы сделал LIMITпонижение; скажем, 10000.)
Рик Джеймс
4

Самое простое решение - просто не делать этого - делать меньшее удаление, которое легче обрабатывать.

В этом случае я бы порекомендовал попробовать последовательное удаление формы:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-a%'
jmoreno
источник
2

Может быть, вы могли бы сделать что-то вроде этого:

  • Добавьте новое поле с именем deleted.
  • Сделайте обновление как UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'.
  • Установите, cronчтобы удалить это в ночное время.
Майк Минаев
источник
Обновление может занять столько же времени, сколько и удаление.
Рик Джеймс