2017-01-15 6 views
1

J'ai une base de données MySQL très grande (1 milliard de lignes) comme celle-ci:MySQL Cloisonnement (InnoDB) - Grande table

base de données

: produits ("name", "Caracteristics")

Les deux colonnes sont VARCHAR (50).

en fait, il n'a pas de clé sat, mais "nom" sera unique, donc je pense que je vais le modifier comme "nom" PRIMARY_KEY. (J'aurais dû faire cela avant .. maintenant je dois effectuer une requête supprimer double avant d'ajouter l'option primary_key je suppose)

Mon problème est, lors de l'exécution d'une requête simple sur la table, il prend des âges littéralement.

SELECT caracteristics WHERE name=blabla LIMIT 1; // prend des âges. Je pensais à partitionner la table existante.

Voici donc la question:

  • Est-ce une bonne idée de fixer mes problèmes de performance?
  • Comment puis-je y parvenir?
  • Est-ce que mon idée de ALTER TABLE pour définir la colonne 'name' comme PRIMARY_KEY est aussi une bonne idée?

  • aussi à propos de la requête en double, je l'ai trouvé ici, est-ce que je le fais correctement? (Ne veux pas gâcher ma table ...)

delete a 
from products a 
left join(
select max(name) maxname, caracteristics 
from products 
group by caracteristics) b 
on a.name = maxname and 
a.caracteristics= b.caracteristics 
where b.maxname IS NULL; 
+1

la suppression prendra toujours aussi. Je prépare une réponse. hmm et non, de toute façon cette suppression ne va pas faire ce que vous attendez – Sebas

+2

Mais il y a une réponse courte: index, index, index, index. Ne pensez pas au partitionnement (ou du moins ne pas résoudre ce problème). Indexes! INDEX! Et n'essayez pas d'exécuter votre 'delete'! Attendez la solution de Seba. – Solarflare

Répondre

3

vous pouvez également définir une directe clé primaire avec l'option Ignore comme ceci:

ALTER IGNORE TABLE `products` ADD PRIMARY KEY(name); 

cela supprimera tous les doublons de nom.

échantillon

MariaDB [l]> CREATE TABLE `products` (
    -> `name` varchar(50) NOT NULL DEFAULT '', 
    -> `caracteristics` varchar(50) DEFAULT NULL 
    ->) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
Query OK, 0 rows affected (0.02 sec) 

MariaDB [l]> INSERT INTO `products` (`name`, `caracteristics`) 
    -> VALUES 
    ->  ('val1', 'asdfasdfasdf'), 
    ->  ('val2', 'asdasDasd'), 
    ->  ('val3', 'aesfawfa'), 
    ->  ('val1', '99999999'); 
Query OK, 4 rows affected (0.01 sec) 
Records: 4 Duplicates: 0 Warnings: 0 

MariaDB [l]> select * from products; 
+------+----------------+ 
| name | caracteristics | 
+------+----------------+ 
| val1 | asdfasdfasdf | 
| val2 | asdasDasd  | 
| val3 | aesfawfa  | 
| val1 | 99999999  | 
+------+----------------+ 
4 rows in set (0.00 sec) 

MariaDB [l]> ALTER IGNORE TABLE `products` ADD PRIMARY KEY(name); 
Query OK, 4 rows affected (0.03 sec) 
Records: 4 Duplicates: 1 Warnings: 0 

MariaDB [l]> select * from products; 
+------+----------------+ 
| name | caracteristics | 
+------+----------------+ 
| val1 | asdfasdfasdf | 
| val2 | asdasDasd  | 
| val3 | aesfawfa  | 
+------+----------------+ 
3 rows in set (0.00 sec) 

MariaDB [l]> 

Test ADD PRIMARY KEY/INSERT IGNORE

Voici un test entre ajouter la clé primaire et insérer dans l'ignorer. et vous pouvez voir que ajouter la clé primaire (90 s/120 s) est un peu plus rapide dans cet exemple

MariaDB [l]> CREATE TABLE `bigtable10m` (
    -> `id` varchar(32) NOT NULL DEFAULT '' 
    ->) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
Query OK, 0 rows affected (0.02 sec) 

MariaDB [l]> 
MariaDB [l]> INSERT INTO `bigtable10m` 
    -> select lpad(seq,8,'0') from seq_1_to_10000000; 
Query OK, 10000000 rows affected (24.24 sec) 
Records: 10000000 Duplicates: 0 Warnings: 0 

MariaDB [l]> 
MariaDB [l]> SELECT * FROM `bigtable10m` LIMIT 10; 
+----------+ 
| id  | 
+----------+ 
| 00000001 | 
| 00000002 | 
| 00000003 | 
| 00000004 | 
| 00000005 | 
| 00000006 | 
| 00000007 | 
| 00000008 | 
| 00000009 | 
| 00000010 | 
+----------+ 
10 rows in set (0.00 sec) 

MariaDB [l]> 
MariaDB [l]> CREATE TABLE `bigtable30m` (
    -> `id` varchar(32) NOT NULL DEFAULT '' 
    ->) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
Query OK, 0 rows affected (0.02 sec) 

MariaDB [l]> 
MariaDB [l]> INSERT INTO `bigtable30m` SELECT * FROM `bigtable10m`; 
Query OK, 10000000 rows affected (28.49 sec) 
Records: 10000000 Duplicates: 0 Warnings: 0 

MariaDB [l]> INSERT INTO `bigtable30m` SELECT * FROM `bigtable10m`; 
Query OK, 10000000 rows affected (29.01 sec) 
Records: 10000000 Duplicates: 0 Warnings: 0 

MariaDB [l]> INSERT INTO `bigtable30m` SELECT * FROM `bigtable10m`; 
Query OK, 10000000 rows affected (32.98 sec) 
Records: 10000000 Duplicates: 0 Warnings: 0 

MariaDB [l]> 
MariaDB [l]> ALTER IGNORE TABLE `bigtable30m` ADD PRIMARY KEY(id); 
Query OK, 30000000 rows affected (1 min 32.34 sec) 
Records: 30000000 Duplicates: 20000000 Warnings: 0 

MariaDB [l]> 
MariaDB [l]> DROP TABLE `bigtable30m`; 
Query OK, 0 rows affected (0.52 sec) 

MariaDB [l]> 
MariaDB [l]> CREATE TABLE `bigtable30m` (
    -> `id` varchar(32) NOT NULL DEFAULT '' 
    ->) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
Query OK, 0 rows affected (0.03 sec) 

MariaDB [l]> 
MariaDB [l]> INSERT INTO `bigtable30m` SELECT * FROM `bigtable10m`; 
Query OK, 10000000 rows affected (37.29 sec) 
Records: 10000000 Duplicates: 0 Warnings: 0 

MariaDB [l]> INSERT INTO `bigtable30m` SELECT * FROM `bigtable10m`; 
Query OK, 10000000 rows affected (41.87 sec) 
Records: 10000000 Duplicates: 0 Warnings: 0 

MariaDB [l]> INSERT INTO `bigtable30m` SELECT * FROM `bigtable10m`; 
Query OK, 10000000 rows affected (30.87 sec) 
Records: 10000000 Duplicates: 0 Warnings: 0 

MariaDB [l]> 
MariaDB [l]> CREATE TABLE bigtable_unique (
    -> `id` varchar(32) NOT NULL DEFAULT '', 
    -> PRIMARY KEY (id) 
    ->); 
Query OK, 0 rows affected (0.02 sec) 

MariaDB [l]> 
MariaDB [l]> INSERT IGNORE bigtable_unique SELECT * FROM `bigtable30m`; 
Query OK, 10000000 rows affected, 65535 warnings (1 min 57.99 sec) 
Records: 30000000 Duplicates: 20000000 Warnings: 20000000 

MariaDB [l]> 
+0

upvoted pour m'apprendre quelque chose sur alter table – Sebas

+0

excellent! l'ajout de ALTER pourrait être plus rapide que la duplication de toutes les données je suppose! Merci pour votre réponse! – user3916429

+1

@ user3916429 en fait je me demande. Modifier la table forcera le moteur à élaguer les enregistrements. Mon instinct me dit que ça va être plus lent que de recréer la table à partir de zéro - mais s'il vous plaît donner des commentaires – Sebas

3

Je pense que partitionner n'est pas la façon dont vous devriez aller à ce problème particulier. Comment partageriez-vous? Sur quels critères?

Je pense que votre principale préoccupation est architecturale et devrait être corrigée avant toute autre chose: les enregistrements uniques ne sont pas uniques.

En raison de la volumétrie, je pense que toute solution prendra un certain temps à s'exécuter. Mais mon pari est que celui-ci est le plus rapide:

CREATE TABLE products_unique (
name VARCHAR(50) NOT NULL, 
characteristics VARCHAR(50), 
PRIMARY KEY (name) 
); 

INSERT IGNORE INTO products_unique SELECT * FROM products; 

RENAME TABLE products TO products_backup; 
RENAME TABLE products_unique TO products; 

Doublons est MANIFESTES arbitraire, mais je pense qu'il est ce que vous cherchez de toute façon. Si cela prend trop de temps, vous devriez essayer de l'exécuter pendant la nuit ... J'espère juste que le tampon de transaction n'explose pas sur vous, auquel cas nous devrons travailler sur une procédure stockée pour séparer les insertions en lots.

+0

merci pour votre temps. Je vais essayer le plus vite possible. et revenez avec les nouvelles. – user3916429

+0

C'est aussi une bonne réponse! –

0

Oui, c'est une bonne idée de corriger les problèmes de performances. C'est toujours la bonne réponse lorsque vous rencontrez des problèmes de performances, assez pour vous poser des questions sur les correctifs de performances.

Vous pouvez réaliser cela en modifiant le table et en faisant name un primary key, comme vous l'avez déjà réalisé.

Votre requête ne devrait pas être nécessaire. Vous devez créer un table temporaire à la place où vous souhaitez insert les valeurs que vous estimez nécessaires. Supposons que le nom de cette table est mytemptable.Puis:

insert into mytemptable(name, characteristics) 
select name, characteristics 
from products 
where not exists (select 1 
        from mytemptable t 
        where products.name = t.name); 

Retirez ensuite vos enregistrements de products en utilisant

delete from products; 

puis alter products, assurez-vous qu'il a name comme primary key puis

insert into products(name, characteristics) 
select name, characteristics 
from mytemptable; 

et enfin drop votre table temporaire.

Comme au sujet de votre requête:

Puisque vous supprimez des enregistrements, max(name) sera égal à tous les autres name s dans votre groupe si vous avez un seul possible name associé à une valeur characteristics donnée, ce qui est assez sûr de supposer .. Donc, si vous avez une valeur characteristics correspondant à un name, vous allez supprimer toutes les instances de name, donc oui, votre requête gâchera vos données.

+0

Vous ne pouvez pas sélectionner 'mytemptable' pendant que vous l'insérez. –

+0

@PaulSpiegel "La table cible de l'instruction INSERT peut apparaître dans la clause FROM de la partie SELECT de la requête (ce n'était pas possible dans certaines anciennes versions de MySQL.) Cependant, vous ne pouvez pas insérer dans une table et sélectionner même table dans une sous-requête. "Source: http://dev.mysql.com/doc/refman/5.7/en/insert-select.html –

+1

Veuillez lire le point de votre lien à la fin:" Lors de la sélection et de l'insertion Dans une table en même temps, MySQL crée une table temporaire pour contenir les lignes du SELECT, puis insère ces lignes dans la table cible. " Cela signifie que votre sous-requête sélectionne toujours dans une table vide. Ainsi, aucun doublon ne sera jamais trouvé, et vous copierez simplement la table. Toutefois, si vous utilisez le mot clé "TEMPORARY" pour créer "mytemptable", la requête ne fonctionnera pas du tout car les tables TEMPORARY ne peuvent pas être référencées deux fois dans la même instruction ". –