web-dev-qa-db-fra.com

La commande DELETE ne se termine pas sur la table de 30 000 000 lignes

J'ai hérité d'une base de données et cherche à la nettoyer et à l'accélérer. J'ai une table qui contient 30 000 000 lignes, dont beaucoup sont des données indésirables insérées en raison d'une erreur de la part de notre programmeur. Avant d'ajouter de nouveaux index plus optimisés, j'ai converti la table de MyISAM en InnoDB et je cherche à supprimer un grand nombre de lignes contenant des données indésirables.

La base de données est MySQL 5.0 et j'ai un accès root au serveur. J'ai d'abord exécuté ces commandes via Adminer puis phpMyAdmin, les deux avec les mêmes résultats.

La commande que j'exécute est,

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

Essentiellement, supprimez tout élément de cette colonne qui commence par un tiret -.

Cela dure environ 3 à 5 minutes, puis quand je vois la liste des processus, c'est parti.

Je cours alors,

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

et il renvoie des millions de lignes.

Pourquoi ma déclaration de suppression ne se termine-t-elle pas?

PS, je sais à quel point MySQL 5.0 est obsolète. Je travaille sur le déplacement de la base de données vers MySQL 5.6 w InnoDB (peut-être MariaDB 10 w XtraDB) mais jusqu'à ce que cela se produise, je cherche à répondre à cela avec la base de données telle quelle.

-

Modifier supprimé, voir ma réponse.

22
bafromca

Je pense que nous avons peut-être trop compliqué la réponse qui était requise dans mon cas . Je n'ai aucun doute que Roland et Rick James sont corrects avec leur création d'une table temporaire, n'injectant que des lignes qui passent le filtre NOT LIKE '-%' mais la solution pour moi était "plus facile" car il y avait une erreur importante que je n'étais pas au courant jusqu'à présent et je m'en excuse.

J'ai exécuté la requête dans l'invite interactive mysql et j'ai remarqué le message d'erreur,

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

En recherchant l'erreur sur Google, j'ai trouvé la solution devait augmenter innodb_buffer_pool_size via le /etc/my.cnf fichier et redémarrage du démon mysql. Pour mon serveur, il a été défini sur la valeur par défaut 8M et je l'ai augmenté à 1G (le serveur a 32 Go et c'est la seule table qui est actuellement InnoDB).

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

Ensuite, j'ai pu exécuter la commande et supprimer 23 millions d'enregistrements en ~ 27 minutes.

Pour les curieux de ce que innodb_buffer_pool_size doit être défini sur, notez combien RAM vous avez et ensuite jetez un œil à ce fil qui donne une estimation suggérée en Go.

21
bafromca

Veuillez consulter l'architecture d'InnoDB (photo de Percona CTO Vadim Tkachenko)

InnoDB Plumbing

Les lignes que vous supprimez sont écrites dans les journaux d'annulation. Le fichier ibdata1 devrait s'agrandir actuellement pour la durée de la suppression. Selon mysqlperformanceblog.com's Reasons for run-away main Innodb Tablespace :

  • Beaucoup de changements transactionnels
  • Transactions très longues
  • Filetage de purge retardé

Dans votre cas, la raison n ° 1 occuperait un segment d'annulation avec une partie de l'espace d'annulation puisque vous supprimez des lignes. Ces lignes doivent se trouver dans ibdata1 jusqu'à la fin de la suppression. Cet espace est logiquement rejeté mais l'espace disque ne rétrécit pas.

Vous devez tuer cette suppression dès maintenant. Une fois que vous avez supprimé la requête de suppression, elle annule les lignes supprimées.

Vous faites cela à la place:

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;

Vous auriez pu faire cela contre la version MyISAM du tableau en premier. Ensuite, convertissez-le en InnoDB.

24
RolandoMySQLDBA

La suggestion de Roland peut être accélérée en faisant les deux choses à la fois:

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;

Mais voici un blog qui explique comment faire de gros SUPPRIMER en morceaux, plutôt que de prendre apparemment pour toujours: http://mysql.rjweb.org/doc.php/deletebig The Gist is to walk through the table via le PK, faisant 1K lignes à la fois. (Bien sûr, il y a plus de détails à connaître.)

Et ce blog aborde les pièges potentiels dans la conversion vers InnoDB: http://mysql.rjweb.org/doc.php/myisam2innodb

12
Rick James

Mon premier réflexe serait de faire plusieurs suppressions plus petites en limitant le nombre de résultats de requête et en exécutant la requête plusieurs fois:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%' LIMIT 1000000
5
kristianp

La solution la plus simple est simplement de ne pas le faire - effectuez une suppression plus petite, qui peut être traitée plus facilement.

Dans ce cas, j'aurais recommandé d'essayer les suppressions séquentielles du formulaire:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-a%'
4
jmoreno

Vous pourriez peut-être faire quelque chose comme ça:

  • Ajoutez un nouveau champ appelé deleted.
  • Faites une mise à jour comme UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'.
  • Définissez cron pour le supprimer la nuit.
2
Mike Minaev