2017-09-08 2 views
0

J'ai une grande table contenant plus de 10 millions d'enregistrements et elle continuera à grandir. J'effectue une requête d'agrégation (nombre de valeur particulière) sur les enregistrements des dernières 24 heures. Le temps pris par cette requête continuera à augmenter avec le nombre d'enregistrements dans la table.Conserver le sous-ensemble d'enregistrements séparément pour les performances de requête mysql

Je peux limiter le temps pris en conservant ces enregistrements de 24 heures dans une table séparée et effectuer l'agrégation sur cette table. Est-ce que mysql fournit des fonctionnalités pour gérer ce type de scénario?

Tableau schéma et requête pour référence:

CREATE TABLE purchases (
    Id int(11) NOT NULL AUTO_INCREMENT, 
    ProductId int(11) NOT NULL, 
    CustomerId int(11) NOT NULL, 
    PurchaseDateTime datetime(3) NOT NULL, 
    PRIMARY KEY (Id), 
    KEY ix_purchases_PurchaseDateTime (PurchaseDateTime) USING BTREE, 
    KEY ix_purchases_ProductId (ProductId) USING BTREE, 
    KEY ix_purchases_CustomerId (CustomerId) USING BTREE 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

select COALESCE(sum(ProductId = v_ProductId), 0), 
     COALESCE(sum(CustomerId = v_CustomerId), 0) 
    into v_ProductCount, v_CustomerCount 
    from purchases 
    where PurchaseDateTime > NOW() - INTERVAL 1 DAY 
     and ( ProductId = v_ProductId 
      or CustomerId = v_CustomerId); 
+0

Répondu mais après avoir relu votre question, peut-être qu'une forme de partition fournirait une solution strictement basée sur une table. Cela nécessiterait une analyse comparative de toutes les alternatives, car je ne suis pas sûr des implications de performance au sommet de ma tête. Voir cette réponse pour d'autres conseils: https://stackoverflow.com/questions/12200359/how-to-partition-mysql-table-by-day –

Répondre

1

Construire et maintenir une Summary table séparée. Avec le partitionnement, vous pourriez obtenir une petite amélioration, ou vous pourriez ne pas obtenir d'amélioration. Avec un tableau récapitulatif, vous pourriez obtenir un facteur de 10 amélioration.

La table récapitulative peut avoir une résolution d'une journée ou vous pouvez avoir besoin d'une heure. S'il vous plaît fournir SHOW CREATE TABLE pour ce que vous avez actuellement, afin que nous puissions discuter plus de détails.

(Il n'y a pas de mécanisme intégré pour ce que vous voulez.)

+0

Il dépend aussi TRÈS de l'INDEXing qui est en place. Un indice approprié permettra d'accélérer les choses beaucoup –

+0

Ce qui suit est le schéma de la table: 'CREER achats TABLE ( Id int (11) NOT AUTO_INCREMENT NULL, ProductId int (11) NOT NULL, CustomerId int (11) NOT NULL, datetime PurchaseDateTime (3) NOT NULL, PRIMARY KEY (Id), KEY ix_purchases_PurchaseDateTime (PurchaseDateTime) UTILISATION BTREE, KEY ix_purchases_ProductId (ProductId) UTILISATION BTREE, KEY ix_purchases_CustomerId (CustomerId) UTILISATION BTREE ) MOTEUR = DEFAULT InnoDB charset = latin1 Comment puis-je créer un tableau récapitulatif pour les dernières 24 heures? – ctor

+0

S'il vous plaît nous montrer le «SELECT» que vous utilisez actuellement pour l'agrégation. –

0

Plan A

Je s'arrêter

 and ( ProductId = v_ProductId 
      or CustomerId = v_CustomerId) 

depuis le reste de la requête simplement traiter avec ça quand même.

Je voudrais ajouter

INDEX(PurchaseDateTime, ProductId, CustomerId) 

qui serait « couverture » - à savoir, l'ensemble SELECT peut être effectuée dans BTree de l'indice. Il serait également «regroupé» dans le sens où tous les données nécessaires seraient stockées consécutivement dans l'index. Oui, le datetime est délibérément le premier. (OR est une nuisance pour optimiser. Je ne fais pas confiance à l'Optimiseur de faire « l'union indice de fusion ».)

Plan B

Si vous prévoyez toucher très peu de lignes (en raison de v_ProductId et v_CustomerId), alors ce qui suit peut être plus rapide, en dépit d'être plus complexe:

SELECT COALESCE(sum(ProductId = v_ProductId), 0) 
    INTO v_ProductCount 
    FROM purchases 
    WHERE PurchaseDateTime > NOW() - INTERVAL 1 DAY 
     AND ProductId = v_ProductId; 
SELECT COALESCE(sum(CustomerId = v_CustomerId), 0) 
    INTO v_CustomerCount 
    FROM purchases 
    WHERE PurchaseDateTime > NOW() - INTERVAL 1 DAY 
     AND CustomerId = v_CustomerId; 

avec les deux:

INDEX(ProductId, PurchaseDateTime), 
INDEX(CustomerId, PurchaseDateTime) 

Oui, l'ordre des colonnes est délibérément différent.

Original Question

Ces deux approches sont mieux que votre suggestion originale d'une table séparée. Ces isoler les données dans une partie d'un index (ou deux index), ayant ainsi l'effet de "séparé". Et ceux-ci font la tâche avec moins d'effort de votre part.

+0

Cependant, le temps d'exécution de ces requêtes augmentera au fur et à mesure que la table se développera, car la durée de recherche de l'index augmentera. Il n'est donc pas préférable de conserver les enregistrements des dernières 24 heures (ou 2 jours) de façon à ce que la durée de consultation de l'index de cette table reste constante pendant que la table d'origine se développe. – ctor

+0

Une requête ponctuelle dans une table de million de lignes prend X millisecondes; dans une table de billions de lignes, cela ne prend que 2X millisecondes. Autrement dit, le ralentissement est très mineur. Avez-vous essayé les index et reforumlations que j'ai suggérés? Est-ce que ce sont les seuls 'SELECT 'qui vous préoccupent? –