2017-03-14 5 views
1

J'ai une requête qui ajoute plusieurs valeurs de colonne dans la clause WHERE. Je ne peux pas précalculer cet ajout dans une seule colonne car la combinaison de colonnes à utiliser varie d'une requête à l'autre. Mon problème est que ma table est très grande (plusieurs centaines de millions de lignes) et les performances très mauvaises.Performances MySQL de la requête ajoutant des colonnes dans la clause where

table Exemple:

+---------+------------+--------+--------+--------+--------+ 
| tableId | categoryId | value1 | value2 | value3 | value4 | 
+---------+------------+--------+--------+--------+--------+ 
|  1 |   1 |  1 |  0 |  5 |  7 | 
|  2 |   1 |  8 |  1 |  7 |  0 | 
|  3 |   1 |  10 |  5 |  0 |  20 | 
|  4 |   2 |  0 |  15 |  0 |  22 | 
|  5 |   2 |  20 |  0 |  11 |  0 | 
+---------+------------+--------+--------+--------+--------+ 

Exemple requêtes:

SELECT * FROM myTable WHERE categoryId = 1 AND (value1 + value2 + value3 + value4) > 9; 
SELECT * FROM myTable WHERE categoryId = 1 AND (value1 + value3 + value4) > 5; 

Quelle serait la meilleure stratégie pour améliorer les performances de ces questions? (edit: J'ai déjà un index sur categoryId, cela n'aide pas)

L'utilisation d'un index aide-t-elle pour de telles requêtes? Devrais-je alors créer tous les index possibles pour toutes les combinaisons possibles de colonnes? Les index obtenus ne seraient-ils pas très très grands? Ou peut-être créer une table de liens, avec des champs de valeur booléenne spécifiant quelles colonnes ont été utilisées? Mais cela se traduirait par une table avec plusieurs milliards de lignes, pas sûr que ce soit mieux ...

+---------+-----------+-----------+-----------+-----------+----------+ 
| tableId | useValue1 | useValue2 | useValue3 | useValue4 | valueSum | 
+---------+-----------+-----------+-----------+-----------+----------+ 
|  1 |   1 |   1 |   1 |   1 |  13 | 
|  1 |   1 |   1 |   1 |   0 |  6 | 
|  1 |   1 |   1 |   0 |   0 |  1 | 
|  1 |   1 |   1 |   0 |   1 |  8 | 
|  1 |   1 |   0 |   1 |   1 |  13 | 
|  1 |   1 |   0 |   1 |   0 |  6 | 
|  1 |   1 |   0 |   0 |   0 |  1 | 
|  1 |   1 |   0 |   0 |   1 |  8 | 
|  1 |   0 |   1 |   1 |   1 |  12 | 
|  1 |   0 |   1 |   1 |   0 |  5 | 
|  1 |   0 |   1 |   0 |   0 |  0 | 
|  1 |   0 |   1 |   0 |   1 |  7 | 
|  1 |   0 |   0 |   1 |   1 |  12 | 
|  1 |   0 |   0 |   1 |   0 |  5 | 
|  1 |   0 |   0 |   0 |   1 |  7 | 
+---------+-----------+-----------+-----------+-----------+----------+ 

Avec un Indice

ALTER TABLE linkTable INDEX(tableId, useValue1, useValue2, useValue3, useValue4, valueSum); 

D'autres idées?

+0

Comment peut-on comprendre cela sans que vos données soient en réalité !!!! – e4c5

+0

@ e4c5: Je suis d'accord et je pourrais faire un autre post questionnant l'ensemble du flux de travail, les calculs, les sorties et le stockage. Mais c'est à quoi je dois faire face aujourd'hui :) Que penses-tu de ma deuxième idée? – FBB

+0

Les colonnes énumérées sont symptomatiques d'une mauvaise conception, ce qui peut affecter les performances. – Strawberry

Répondre

-1

Vous pouvez placer vos requêtes dans des catégories. Pour chaque catégorie, vous pouvez conserver une colonne pré-calculée. Vous pouvez sélectionner le champ associé de la table par rapport à la combinaison de calculs requise. Bien sûr, il est possible si vous pouvez classer vos requêtes.

+1

Cela ferait 15 combinaisons à partir d'aujourd'hui, plus dans le futur. J'essayais d'éviter une telle conception:/ – FBB

0

@ e4c5 a raison de dire qu'aucun des index ne va aider avec la requête en cours. Vous pouvez commencer par ajouter les indices suivants et de modifier la requête avec des conditions supplémentaires pour que les indices se habituent:

ALTER TABLE myTable 
ADD INDEX(categoryId, value1), 
ADD INDEX(categoryId, value2), 
ADD INDEX(categoryId, value3), 
ADD INDEX(categoryId, value4); 

et mettre à jour la requête comme ceci:

SELECT * FROM myTable WHERE categoryId = 1 AND (value1 <= 9) AND (value2 <= 9) AND (value3 <= 9) AND (value4 <= 9) AND (value1 + value2 + value3 + value4) > 9; 
SELECT * FROM myTable WHERE categoryId = 1 AND (value1 <= 5) AND (value3 <= 5) AND (value4 <= 5) AND (value1 + value3 + value4) > 5; 

Les conditions supplémentaires contribue à limiter le nombre de lignes à traiter. Ajouter des index sur plus de colonnes accélérerait encore cela, mais je suggère d'essayer ceci en premier.

+0

Mysql utilise seulement un index par table – e4c5

+0

oui il fait @ ec45. La condition supplémentaire ci-dessus doit utiliser l'index qui implique le moins de lignes –

0

Je vais devoir faire quelques suppositions jusqu'à ce que je vois ... SHOW CREATE TABLE

Si vous avez ceci:

tableId INT UNSIGNED AUTO_INCREMENT NOT NULL, 
categoryId INT UNSIGNED NOT NULL, 
... 
PRIMARY KEY(tableId), 

changer ensuite à

tableId INT UNSIGNED AUTO_INCREMENT NOT NULL, -- same 
categoryId INT UNSIGNED NOT NULL,    -- same 
... 
PRIMARY KEY(categoryId, tableId), -- different, see Note 1 
INDEX(tableId)      -- different, see Note 2 

Note 1. L'index (le PK) commençant par categoryId aidera les requêtes que vous avez présentées. De plus, en étant au début du PK, il va "regrouper" toutes les lignes nécessaires pour un SELECT ensemble, minimisant ainsi les E/S dans votre énorme table.

Note 2.Oui, il est correct d'avoir seulement INDEX(...) pour le AUTO_INCREMENT.

Autre astuce ... Comme BIGINT est toujours de 8 octets et INT est de 4 octets; avez-vous vraiment besoin de cette grosse colonne? La réduction de la taille des colonnes permettra de réduire les E/S, ce qui accélérera considérablement les requêtes. MEDIUMINT UNSIGNED est seulement 3 octets et a une portée de 0..16M; etc.

0

Sur la base des réponses à my follow-up question about the overall database design, les conclusions sont les suivantes:

  • Tous mes types de données et les index sont corrects.
  • Mon design avec des colonnes énumérées n'est pas très élégant, mais adapté à une base de données basée sur une ligne telle que MySQL et donne les meilleures performances sur ce type de moteur. Pour résoudre ce problème de performances, je devrais passer à une base de données basée sur des colonnes, en utilisant une meilleure conception comme décrit dans les commentaires de mon autre question (où les données à agréger seraient dans une même colonne mais plusieurs lignes).