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

22

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 lorsque je consulte la liste des processus, elle disparaît.

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.

bafromca
la source

Réponses:

24

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

Plomberie InnoDB

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.comReasons 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.

RolandoMySQLDBA
la source
21

Je pense que nous avons peut-être trop compliqué la réponse qui était requise dans mon cas . Je ne doute pas 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 ne connaissais pas jusqu'à présent et pour que je m'excuse.

J'ai exécuté la requête dans l' mysqlinvite interactive 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é que la solution était d'augmenter innodb_buffer_pool_sizevia le /etc/my.cnffichier et de redémarrer le démon mysql. Pour mon serveur, il a été défini sur la valeur par défaut 8Met 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 savoir ce qui innodb_buffer_pool_sizedevrait être réglé, notez la quantité de RAM dont vous disposez, puis jetez un œil à ce fil qui donne une estimation suggérée en Go.

bafromca
la source
12

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 L'essentiel est de parcourir la table via le PK, en 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 à InnoDB: http://mysql.rjweb.org/doc.php/myisam2innodb

Rick James
la source
5

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
kristianp
la source
Un inconvénient de cette approche: chaque suppression prendra de plus en plus de temps. En effet, il doit ignorer de plus en plus de lignes qui ne correspondent pas à WHERE.
Rick James
Certes, mais si ce processus ne se produit pas trop souvent, plusieurs analyses de table complètes ne devraient pas être aussi graves que le problème d'origine étant résolu, à savoir que la requête ne se termine jamais en raison de la taille du journal d'annulation.
kristianp
Point valide. (Je ferais le LIMITplus bas; disons 10000.)
Rick James
4

La solution la plus simple consiste simplement à 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%'
jmoreno
la source
2

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%'.
  • Réglez-le cronpour le supprimer la nuit.
Mike Minaev
la source
La mise à jour peut durer aussi longtemps que la suppression.
Rick James