Question

J'ai donc ce tableau d'audit (pistes d'actions sur une table dans ma base de données):

CREATE TABLE `track_table` (
  `id` int(16) unsigned NOT NULL,
  `userID` smallint(16) unsigned NOT NULL,
  `tableName` varchar(255) NOT NULL DEFAULT '',
  `tupleID` int(16) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `action` char(12) NOT NULL DEFAULT '',
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableName`,`tupleID`,`date_insert`),
  KEY `actionDate` (`action`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

et je dois commencer à archiver les éléments obsolètes. Le tableau a augmenté à environ 50 millions de lignes, de sorte que le moyen le plus rapide que je pouvais supprimer les lignes était de supprimer une table à un moment (basé sur tableName).

Cela fonctionne assez bien, mais sur quelques-uns des tableaux qui sont en écriture lourde, il ne sera pas complète. Ma requête supprime tous les éléments qui ont une action delete associée à une combinaison tupleID / tableName:

DELETE FROM track_table WHERE tableName='someTable' AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableName='someTable' AND action='DELETE' AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
)

Je laisse cela fonctionne sur mon serveur pendant 3 jours et il n'a jamais terminé la plus grande table. La sortie expliquer (si je passe la suppression pour sélectionner:

| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

4 millions de lignes ne doivent pas prendre 3 jours pour supprimer, je pense. J'ai mon jeu de innodb_buffer_pool_size 3Go, et le serveur est pas configuré pour utiliser one_file_per_table. Quels autres moyens puis-je améliorer les performances InnoDB suppression? (Running MySQL 5.1.43 sur Mac OSX)

Était-ce utile?

La solution

Vous pourriez supprimer des données par lots.

Dans SQL Server, la syntaxe est des lignes de delete top X d'une table. Vous faire alors dans une boucle, avec une transaction pour chaque lot (si vous avez plus d'une instruction, bien sûr), afin de garder les transactions à court et à maintenir les verrous pour de courtes périodes.

Dans la syntaxe MySQL: DELETE FROM userTable LIMIT 1000

Il existe des restrictions qui ne peuvent pas utiliser (LIMIT en supprime de jointures, par exemple), mais dans ce cas, vous pourriez être en mesure de le faire de cette façon.

Il y a un danger supplémentaire à l'utilisation LIMIT avec DELETE en matière de réplication; les lignes supprimées sont parfois pas supprimés dans le même ordre sur l'esclave comme il a été supprimé sur le maître.

Autres conseils

Essayez d'utiliser une approche de table temporaire. Essayez quelque chose comme ceci:

Etape 1) CREATE TABLE track_table_new LIKE track_table;

Étape 2) INSERT INTO track_table_new SELECT * FROM track_table WHERE action='DELETE' AND date_insert >= DATE_SUB(CURDATE(), INTERVAL 30 day);

Étape 3) ALTER TABLE track_table RENAME track_table_old;

Etape 4) ALTER TABLE track_table_new RENAME track_table;

Etape 5) DROP TABLE track_table_old;

Je ne comprend pas le champ tuple à l'étape 2. S'il vous plaît voir si cela produit l'effet désiré. Si c'est ce que vous voulez, vous voudrez peut-être un fossé le champ tuple tout à fait, sauf si vous utilisez le champ tuple pour d'autres raisons.

Suppression de lignes non désirées dans le lot doit garder un autre pratique de fonctionnement. Mais votre suppression de fonctionnement a des conditions, afin d'assurer qu'il y ait un indice approprié sur des colonnes sur les conditions.

Parce que MySQL ne supporte pas la fonction complète de balayage libre d'index, vous pouvez essayer d'ajuster la séquence pour KEY actionDate (action, date_insert) à KEY actionDate (date_insert, action). Avec le préfixe « date_insert », MySQL devrait utiliser cet index pour balayer les lignes qui sont avant votre état datetime.

Avec cet indice, vous pouvez écrire SQL comme:

DELETE
FROM track_table
WHERE tableName='someTable'
    AND action='DELETE'
    AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
LIMIT 1000 -- Your size of batch
| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

-Fist, de votre expliquer les key_len si grand => vous avez besoin de rétrograder la taille aussi petite que possible. Pour votre requête, je pense que la meilleure façon est le changement champ type d'action de données de char (12) à tinyint, de sorte que le regard de la cartographie des données comme:

1: -> DELETE
2: -> UPDATE
3: -> INSERT
...

et vous pouvez changer table_id à la place nom_table aussi. le DDL pour la meilleure performance possible:

CREATE TABLE `track_table` (
  `id` int(11) unsigned NOT NULL,
  `userID` smallint(6) unsigned NOT NULL,
  `tableid` smallint(6) UNSIGNED NOT NULL DEFAULT 0,
  `tupleID` int(11) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `actionid` tinyin(4) UNSIGNED NOT NULL DEFAULT 0,
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableid`,`tupleID`,`date_insert`),
  KEY `actionDate` (`actionid`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `actions` (
  `id` tinyint(4) unsigned NOT NULL 
  `actionname` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `table_name` (
  `id` tinyint(4) unsigned NOT NULL 
  `tablename` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

la requête peut courir ressembler à:

DELETE FROM track_table WHERE tableid=@tblid AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableid=@tblid AND actionid=@actionid AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
).

Mais le moyen le plus rapide utilisait la partition. de sorte que vous pouvez déposer une partition. À l'heure actuelle, ma table ont obtenu plus que les lignes de 40mil. et mettre à jour toutes les heures (400k lignes de mise à jour à chaque fois), et i peut laisser tomber les cloisons curr_date et recharger les données dans la table. la commande de chute très rapide (<100 ms). Espérons que cela aide.

Licencié sous: CC-BY-SA avec attribution
Non affilié à dba.stackexchange
scroll top