2010-03-27 6 views
0

Au travail, nous construisons de grandes applications Web multi-pages, composées principalement de radio et de cases à cocher. L'objectif principal de chaque application est de rassembler des données, mais lorsque les utilisateurs reviennent sur une page qu'ils ont déjà visitée, nous leur communiquons leurs réponses précédentes. Dans le pire des cas, nous pourrions avoir jusqu'à 900 variables distinctes et environ 1,5 million d'utilisateurs.Comment puis-je améliorer ma requête de sélection pour stocker de grands ensembles de données versionnés?

Pour plusieurs raisons, il est logique d'utiliser une approche d'insertion uniquement pour stocker les données (par opposition à la mise à jour sur place) afin de pouvoir capturer des données historiques sur les interactions répétées avec des variables. Le résultat net est que nous pourrions avoir plusieurs réponses par utilisateur et par variable.

Notre table pour recueillir les réponses ressemble à ceci:

CREATE TABLE [dbo].[results](
    [id] [bigint] IDENTITY(1,1) NOT NULL, 
    [userid] [int] NULL, 
    [variable] [varchar](8) NULL, 
    [value] [tinyint] NULL, 
    [submitted] [smalldatetime] NULL)

Où id sert de clé primaire.

Pratiquement toutes les demandes des résultats d'une série d'instructions d'insertion (une par variable soumise), et nous courons un select pour produire des réponses précédentes pour la page suivante (quelque chose comme ça):

SELECT t.id, t.variable, t.value 
    FROM results t WITH (NOLOCK) 
    WHERE t.userid = '2111846' AND 
    (t.variable='internat' OR t.variable='veteran' OR t.variable='athlete') AND 
    t.id IN (SELECT MAX(id) AS id 
     FROM results WITH (NOLOCK) 
     WHERE userid = '2111846' AND (t.variable='internat' OR t.variable='veteran' OR t.variable='athlete') 
     GROUP BY variable)

qui, dans ce cas, retournerait les réponses les plus récentes pour les variables "internat", "vétéran", et "athlète" pour l'utilisateur 2111846.

Nous avons suivi les conseils des outils d'accord de base de données dans l'indexation des tables, et contre nos données, il s'agit de la version la plus performante de la requête select que nous avons été en mesure de proposer. Même ainsi, il semble y avoir une dégradation significative des performances car la table approche 1 million d'enregistrements (et nous pourrions en avoir environ 150x). Nous avons mis en place une solution assez élégante pour répartir les données entre plusieurs tables, ce qui fonctionne très bien, mais je suis ouvert à tout conseil sur la façon de construire une meilleure version de la requête select. Nous utilisons fréquemment cette structure pour stocker beaucoup de points de données indépendants, et nous apprécions les avantages qu'elle procure. Donc, la question est, comment puis-je améliorer les performances de la requête de sélection? Je suppose que l'instruction select imbriquée est une mauvaise idée, mais je n'ai pas encore trouvé une alternative qui fonctionne aussi bien.

Merci d'avance. NB: Puisque nous mettons l'accent sur la création de sur-lectures dans ce cas, et puisque nous ne le mettons jamais à jour, il ne semble y avoir aucune pénalité (et un avantage) pour utiliser la directive NOLOCK dans ce cas.

Répondre

3

N'utilisez pas de conseils NOLOCK. Utilisez snapshot isolation à la place, activez simplement READ_COMMITED_SNAPSHOT ON sur la base de données.

Aussi, vous dites que vous voulez 'le plus récent', mais vous sélectionnez le MAX par ID, qui est une clé IDENTITY. Ne devriez-vous pas utiliser un datetime pour 'recent'? Une identité ne donnera que l'ordre d'insertion "récent", ce qui peut ne pas avoir de signification pour le modèle de domaine.

Pour retourner les données que vous voulez la meilleure façon est de créer une clé cluster appropriée:

CREATE TABLE [dbo].[results](
    [id] [bigint] IDENTITY(1,1) NOT NULL, 
    [userid] [int] NULL, 
    [variable] [varchar](8) NULL, 
    [value] [tinyint] NULL, 
    [submitted] [smalldatetime] NULL); 

create clustered index cdxResults on results ([userid], variable, id DESC); 

Si vous avez une table des types de variables possibles, la requête peut être fortement optimisé en utilisant MAX dans un opérateur POSTULER:

SELECT t.id, t.variable, t.value 
FROM ( 
    SELECT 'internat' as variable 
    UNION ALL SELECT 'veteran' 
    UNION ALL SELECT 'athlete' 
) as v 
CROSS APPLY (
    SELECT TOP(1) id, variable, value 
    FROM results 
    WHERE userid = @userid 
    AND variable = v.variable 
    ORDER BY id DESC 
) as t 

Cette requête se déroulera 3 cherche dans l'index cluster, le temps de réponse sera toujours constante O (1), hors de propos de la taille des données. C'est à dire. en même temps pour 100 lignes dans le tableau, 1 million ou 1 milliard.

+0

Merci, c'est génial. –

Questions connexes