Pourquoi DELETE est-il tellement plus lent que SELECT, puis DELETE par id?

12

J'ai une table InnoDB assez occupée (200 000 lignes, je suppose quelque chose comme des dizaines de requêtes par seconde). En raison d'un bug, j'ai reçu 14 lignes contenant (les mêmes) adresses e-mail invalides et je voulais les supprimer.

J'ai simplement essayé DELETE FROM table WHERE email='invalid address'et obtenu "Délai d'attente de verrouillage dépassé" après environ 50 secondes. Ce n'est pas très surprenant, car la colonne de ligne n'est pas indexée.

Cependant, je l'ai ensuite fait SELECT id FROM table WHERE email='invalid address'et cela a pris 1,25 seconde. L'exécution DELETE FROM table WHERE id in (...), copier-coller les identifiants du résultat SELECT, a pris 0,02 seconde.

Que se passe-t-il? Quelqu'un peut-il expliquer pourquoi la SUPPRESSION avec la condition est si lente qu'elle expire, mais faire SELECT puis supprimer par id est si rapide?

Merci.

EDIT: Sur demande, j'ai publié la structure du tableau ainsi que certains explainrésultats. Je dois également noter qu'aucune clé étrangère ne fait référence à ce tableau.

Cependant, la situation me semble simple: j'ai un champ non indexé que je sélectionne. Cela nécessite de scanner toute la table, mais ce n'est pas terriblement grand. idest la clé primaire, donc la suppression par id est très rapide, comme il se doit.

mysql> show create table ThreadNotification2 \G
*************************** 1. row ***************************
       Table: ThreadNotification2
Create Table: CREATE TABLE `ThreadNotification2` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `alertId` bigint(20) DEFAULT NULL,
  `day` int(11) NOT NULL,
  `frequency` int(11) DEFAULT NULL,
  `hour` int(11) NOT NULL,
  `email` varchar(255) DEFAULT NULL,
  `highlightedTitle` longtext,
  `newReplies` bit(1) NOT NULL,
  `numReplies` int(11) NOT NULL,
  `postUrl` longtext,
  `sendTime` datetime DEFAULT NULL,
  `sent` bit(1) NOT NULL,
  `snippet` longtext,
  `label_id` bigint(20) DEFAULT NULL,
  `organization_id` bigint(20) DEFAULT NULL,
  `threadEntity_hash` varchar(255) DEFAULT NULL,
  `user_uid` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK3991E9D279251FE` (`organization_id`),
  KEY `FK3991E9D35FC0C96` (`label_id`),
  KEY `FK3991E9D3FFC22CB` (`user_uid`),
  KEY `FK3991E9D5376B351` (`threadEntity_hash`),
  KEY `scheduleSentReplies` (`day`,`frequency`,`hour`,`sent`,`numReplies`),
  KEY `sendTime` (`sendTime`),
  CONSTRAINT `FK3991E9D279251FE` FOREIGN KEY (`organization_id`) REFERENCES `Organization` (`id`),
  CONSTRAINT `FK3991E9D35FC0C96` FOREIGN KEY (`label_id`) REFERENCES `Label` (`id`),
  CONSTRAINT `FK3991E9D3FFC22CB` FOREIGN KEY (`user_uid`) REFERENCES `User` (`uid`),
  CONSTRAINT `FK3991E9D5376B351` FOREIGN KEY (`threadEntity_hash`) REFERENCES `ThreadEntity` (`hash`)
) ENGINE=InnoDB AUTO_INCREMENT=4461945 DEFAULT CHARSET=utf8
1 row in set (0.08 sec)

mysql> explain SELECT * FROM ThreadNotification2 WHERE email='invalid address';
+----+-------------+---------------------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table               | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+---------------------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | ThreadNotification2 | ALL  | NULL          | NULL | NULL    | NULL | 197414 | Using where |
+----+-------------+---------------------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.03 sec)


mysql> explain select * from ThreadNotification2 where id in (3940042,3940237,3941132,3941255,3941362,3942535,3943064,3944134,3944228,3948122,3953081,3957876,3963849,3966951);
+----+-------------+---------------------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table               | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+---------------------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | ThreadNotification2 | range | PRIMARY       | PRIMARY | 8       | NULL |   14 | Using where |
+----+-------------+---------------------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)



mysql> delete from ThreadNotification2 where email='invalid address';
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> select id from ThreadNotification2 where email='invalid address';
+---------+
| id      |
+---------+
| 3940042 |
| 3940237 |
| 3941132 |
| 3941255 |
| 3941362 |
| 3942535 |
| 3943064 |
| 3944134 |
| 3944228 |
| 3948122 |
| 3953081 |
| 3957876 |
| 3963849 |
| 3966951 |
+---------+
14 rows in set (1.25 sec)

mysql> delete from ThreadNotification2 where id in (3940042,3940237,3941132,3941255,3941362,3942535,3943064,3944134,3944228,3948122,3953081,3957876,3963849,3966951);
Query OK, 14 rows affected (0.02 sec)
itsadok
la source
2
Je suppose que vous devez absolument en publier un SHOW CREATE TABLEet probablement EXPLAIN...aussi.
Radu Murzea
@SoboLAN vraiment? Cela ressemble à un scénario si simple. J'ai mis à jour la question.
itsadok
Ouais mais ... tu avais raison en premier lieu. Si le champ emailn'est pas indexé, alors les deux DELETEet SELECTdevraient fonctionner également lentement. Ou: Vous dites que la table est fortement interrogée. Peut-être que lorsque vous avez essayé votre premier, DELETEil y avait quelqu'un d'autre qui effectuait une très longue transaction sur ces lignes ...
Radu Murzea
Une autre explication de DELETE FROM ThreadNotification2 WHERE email='invalid address';peut-être aiderait aussi ...
pconcepcion
@pconcepcion si vous écrivez EXPLAIN DELETE FROM...., cela ne fonctionnera pas. D'après ce que je sais, cela ne fonctionne que sur l' SELECTal.
Radu Murzea

Réponses:

6

Si le champ emailn'est pas indexé, alors les deux DELETEet SELECTdevraient fonctionner également lentement.

La seule possibilité à laquelle je peux penser est: vous dites que la table est très utilisée. Peut-être que quelqu'un d'autre a exécuté une très longue transaction (impliquant directement ou indirectement ces lignes spécifiques) pendant que vous tentiez d'exécuter le DELETE.

Je pense que vous devriez peut-être y insérer quelques fausses lignes et essayer de les supprimer. Faites cela 2 ou 3 fois. S'il y a une grande différence dans la durée du DELETE, alors la charge DB est probablement la raison.

PS: Ne faites cela que si les gens ne seront pas ennuyés par ces fausses lignes: D.

Radu Murzea
la source
2
Votre réponse est-elle "je ne sais pas pourquoi"?
Pacerier