web-dev-qa-db-fra.com

Un tableau avec 80 millions d’enregistrements et l’ajout d’un index prend plus de 18 heures (ou indéfiniment)! Maintenant quoi?

Un bref récapitulatif de ce qui s'est passé. Je travaille avec 71 millions d’enregistrements (pas beaucoup par rapport aux milliards d’enregistrements traités par d’autres). Sur un autre thread , quelqu'un a suggéré que la configuration actuelle de mon cluster ne convient pas à mon besoin. Ma structure de table est:

CREATE TABLE `IPAddresses` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `ipaddress` bigint(20) unsigned default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM;

Et j'ai ajouté les 71 millions de disques et ensuite fait un:

ALTER TABLE IPAddresses ADD INDEX(ipaddress);

Cela fait 14 heures et l'opération n'est toujours pas terminée. Lors de la recherche sur Google, j'ai trouvé qu'il existe une approche bien connue pour résoudre ce problème - le partitionnement. Je comprends que je dois partitionner ma table maintenant en fonction de l'adresse IP, mais puis-je le faire sans recréer la table entière? Je veux dire, à travers une déclaration ALTER? Dans l'affirmative, il y avait une exigence selon laquelle la colonne sur laquelle partitionner devrait être une clé primaire. J'utiliserai l'id de cette adresse ip pour construire une table différente, donc l'adresse ip n'est pas ma clé primaire. Comment partitionner ma table avec ce scénario?

29
Legend

Ok s'avère que ce problème était plus qu'un simple: créer une table, l'indexer et oublier le problème :) Voici ce que j'ai fait juste au cas où quelqu'un d'autre ferait face au même problème (j'ai utilisé un exemple d'adresse IP mais cela fonctionne pour d'autres types de données aussi):

Problème: votre table contient des millions d'entrées et vous devez ajouter un index très rapidement

Usecase: Envisagez de stocker des millions d'adresses IP dans une table de recherche. L'ajout d'adresses IP ne devrait pas être un gros problème, mais la création d'un index sur celles-ci prend plus de 14 heures. 

Solution: Partitionnez votre table avec La stratégie de partitionnement g de MySQL

Cas n ° 1: Lorsque la table souhaitée n'est pas encore créée

CREATE TABLE IPADDRESSES(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  ipaddress BIGINT UNSIGNED,
  PRIMARY KEY(id, ipaddress)
) ENGINE=MYISAM
PARTITION BY HASH(ipaddress)
PARTITIONS 20;

Cas n ° 2: Lorsque la table souhaitée est déjà créée. Il semble y avoir un moyen d’utiliser ALTER TABLE pour ce faire, mais je n’ai pas encore trouvé de solution adéquate. Au lieu de cela, il existe une solution légèrement inefficace:

CREATE TABLE IPADDRESSES_TEMP(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  ipaddress BIGINT UNSIGNED,
  PRIMARY KEY(id)
) ENGINE=MYISAM;

Insérez vos adresses IP dans ce tableau. Et créez ensuite la table avec les partitions:

CREATE TABLE IPADDRESSES(
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  ipaddress BIGINT UNSIGNED,
  PRIMARY KEY(id, ipaddress)
) ENGINE=MYISAM
PARTITION BY HASH(ipaddress)
PARTITIONS 20;

Et puis finalement

INSERT INTO IPADDRESSES(ipaddress) SELECT ipaddress FROM IPADDRESSES_TEMP;
DROP TABLE IPADDRESSES_TEMP;
ALTER TABLE IPADDRESSES ADD INDEX(ipaddress)

Et voilà ... l'indexation sur la nouvelle table m'a pris environ 2 heures sur une machine à 3,2 GHz avec 1 Go RAM :) J'espère que cela vous aidera.

37
Legend

Créer des index avec MySQL est lent, mais pas si lent. Avec 71 millions de disques, cela devrait prendre quelques minutes, pas 14 heures. Les problèmes possibles sont:

  • vous n'avez pas configuré la taille du tampon de tri ni d'autres options de configuration

regardez ici: http://dev.mysql.com/doc/refman/5.5/fr/server-system-variables.html#sysvar_myisam_sort_buffer_size

Si vous essayez de générer un index de 1 Go avec un tampon de tri de 8 Mo, cela nécessitera beaucoup de passes. Mais si le tampon est plus grand que le cache de votre CPU, il sera plus lent. Il faut donc tester et voir ce qui fonctionne le mieux.

  • quelqu'un a un verrou sur la table
  • votre système IO est nul
  • votre serveur échange
  • etc

comme d'habitude, vérifiez iostat, vmstat, les journaux, etc. Émettez une table de verrouillage sur votre table pour vérifier si quelqu'un a un verrou dessus.

FYI sur mon bureau 64 bits créer un index sur 10M BIGINT aléatoires prend 17s ...

7
peufeu

J'ai eu le problème où je voulais accélérer ma requête en ajoutant un index. La table ne comptait qu'environ 300 000 enregistrements, mais cela a aussi pris beaucoup trop de temps. Lorsque j'ai vérifié les processus du serveur mysql, il s'est avéré que la requête que j'essayais d'optimiser fonctionnait toujours en arrière-plan. 4 fois! Après que j'ai tué ces requêtes, l'indexation a été faite en un tournemain. Peut-être que le même problème s'applique à votre situation.

5
Giel Berkers

Vous utilisez MyISAM, qui sera bientôt obsolète. Une alternative serait InnoDB.

"InnoDB est un moteur de stockage sûr pour les transactions (compatible ACID) pour MySQL, doté de fonctionnalités de validation, d'annulation et de récupération sur incident pour la protection des données utilisateur. les lectures augmentent la concurrence et les performances multi-utilisateurs InnoDB stocke les données des utilisateurs dans des index clusterisés afin de réduire les E/S des requêtes courantes basées sur des clés primaires Pour maintenir l'intégrité des données, InnoDB prend également en charge les contraintes d'intégrité référentielle FOREIGN KEY avec des tables d'autres moteurs de stockage MySQL, même dans la même instruction. "\

http://dev.mysql.com/doc/refman/5.0/en/innodb.html

Selon:

http://dev.mysql.com/tech-resources/articles/storage-engine/part_1.html

, vous devriez pouvoir passer d’un moteur à l’autre en utilisant une simple commande de modification qui vous permet une certaine souplesse. Il indique également que chaque table de votre base de données peut être configurée indépendamment.

3
Michael Eakins

Dans votre table vous avez déjà inséré 71 milliards de disques. maintenant si vous voulez créer des partitions sur la colonne de clé primaire de votre table, vous pouvez utiliser l'option alter table. Un exemple est donné pour votre référence.

CREATE TABLE t1 (
    id INT,
    year_col INT
);

ALTER TABLE t1
    PARTITION BY HASH(id)
    PARTITIONS 8;
0
seema