Performances de SQL Server Linked Server: pourquoi les requêtes distantes sont-elles si chères?

14

J'ai deux serveurs de base de données, connectés via des serveurs liés. Les deux sont des bases de données SQL Server 2008R2 et la connexion au serveur lié est établie via un lien "SQL Server" normal, en utilisant le contexte de sécurité de la connexion actuelle. Les serveurs liés sont tous les deux dans le même centre de données, donc la connexion ne devrait pas être un problème.

J'utilise la requête suivante pour vérifier quelles valeurs de la colonne identifiersont disponibles à distance, mais pas localement.

SELECT 
    identifier 
FROM LinkedServer.RemoteDb.schema.[TableName]

EXCEPT

SELECT DISTINCT
    identifier 
FROM LocalDb.schema.[TableName] 

Sur les deux tables se trouvent des index non clusterisés sur la colonne identifier. Localement, il y a environ 2,6 millions de lignes, à distance seulement 54. Pourtant, quand on regarde le plan de requête, 70% du temps d'exécution est consacré à "exécuter une requête distante". En outre, lorsque vous étudiez le plan de requête complet, le nombre de lignes locales estimées est à la 1place de 2695380(qui est le nombre de lignes estimées lorsque vous sélectionnez uniquement la requête suivante EXCEPT). Plan d'exécution Lors de l'exécution de cette requête, cela prend en effet beaucoup de temps.

Cela me fait me demander: pourquoi est-ce? Est-ce que l'estimation est "juste" loin ou les requêtes distantes sur des serveurs liés sont-elles vraiment si chères?

vstrien
la source
2
BTW: C'est le "nombre estimé d'exécutions" que vous devriez rechercher pour la recherche d'index. Le nombre estimé de lignes est la sortie de lignes par exécution qui ne sera pas liée au nombre de lignes dans la table elle-même, sauf si le plan a une analyse complète.
Martin Smith

Réponses:

9

Le plan que vous avez en ce moment me semble être le plan le plus optimal.

Je ne suis pas d'accord avec l'affirmation dans les autres réponses selon laquelle il envoie les 2,6 millions de lignes au serveur distant.

Le plan me semble que pour chacune des 54 lignes renvoyées par la requête distante, il effectue une recherche d'index dans votre table locale pour déterminer si elle correspond ou non. C'est à peu près le plan optimal.

Le remplacement par une jointure de hachage ou une jointure de fusion serait contre-productif étant donné la taille de la table et l'ajout d'une #temptable intermédiaire ajoute simplement une étape supplémentaire qui ne semble pas vous donner d'avantage.

Martin Smith
la source
6

La connexion à une ressource distante coûte cher. Période.

L'une des opérations les plus coûteuses dans n'importe quel environnement de programmation est les E / S réseau (bien que les E / S disque aient tendance à les éclipser).

Cela s'étend aux serveurs liés distants. Le serveur appelant le serveur lié distant doit d'abord établir une connexion, puis une requête doit être exécutée sur le serveur distant, les résultats retournés et la connexion fermée. Tout cela prend du temps sur le réseau.


Vous devez également structurer votre requête de manière à transférer le minimum de données sur le câble. Ne vous attendez pas à ce que la base de données optimise pour vous.

Si je devais écrire cette requête, je sélectionnerais les données distantes dans une variable de table (ou dans une table temporaire), puis l'utiliserais conjointement avec la table locale. Cela garantit que seules les données qui doivent être transférées le seront.

La requête que vous exécutez peut facilement envoyer 2,6 millions de lignes au serveur distant afin de traiter la EXCEPTclause.

Oded
la source
Ok, il a donc des coûts de démarrage élevés pour établir la connexion. La requête doit être envoyée, traitée à distance (aucun réseau nécessaire pour celui-ci), et enfin les résultats renvoyés et traités. Mais cela ne prendra pas quelques minutes pour envoyer des données via une connexion réseau, n'est-ce pas?
vstrien
@vstrien - C'est possible. Dépend de la connexion réseau, de la latence, de la saturation et d'autres facteurs. Le fait est que ce n'est pas déterministe.
@vstrien - Ajout de plus d'informations dans ma réponse. Je crois que la requête telle qu'elle est écrite enverra les lignes locales au serveur distant pour traitement.
2
D'où déduisez-vous le fait qu'il envoie les 2,6 millions de lignes au serveur distant? Je n'ai pas beaucoup d'expérience avec les plans avec les opérateurs de requête distants, mais il semble que les 54 lignes sortent de l'opérateur de requête distant, alors il fait l'anti semi-jointure contre la table locale.
Martin Smith
2
@Lieven - Peut-être logique, mais ne pense pas que ce soit correct d'après le plan indiqué.
Martin Smith
1

Je ne suis pas un expert mais si vous utilisez Union, Except ou Intersect, vous n'avez pas besoin d'utiliser "Distinct". Selon les valeurs de LocalDb.schema. [TableName], les performances de la requête peuvent être améliorées.

SELECT 
    identifier 
FROM LinkedServer.RemoteDb.schema.[TableName]

EXCEPT

SELECT 
    identifier 
FROM LocalDb.schema.[TableName]
joakon
la source
0

Oded a raison, le problème de performances est dû à l'envoi des lignes 2,6 millions à votre serveur distant.

Pour résoudre ce problème, vous pouvez forcer l'envoi des données distantes (54 lignes) en utilisant une table temporaire ou en mémoire.

Utilisation d'une table temporaire

SELECT  identifier 
INTO    #TableName
FROM    LinkedServer.RemoteDb.schema.[TableName]

SELECT  identifier
FROM    #TableName
EXCEPT
SELECT  DISTINCT identifier 
FROM    LocalDb.schema.[TableName] 

DROP    #TableName
Lieven Keersmaekers
la source
L'utilisation d'une table temporaire peut aider dans tous les cas avec les estimations de cardinalité, bien qu'une boucle imbriquée semble raisonnable pour seulement 54 lignes.
Martin Smith
L'utilisation d'une table temporaire fonctionne correctement avec 54 lignes; mais dans les cas avec de grandes tables des deux côtés, ce n'est plus possible. Quelle serait votre solution pour deux "énormes" tables de taille égale? Créer un UserTable, dans une autre base de données?
vstrien
1
@vstrien - il n'y a pas vraiment de bonne solution pour deux énormes tables de taille égale. Peut-être que la création d'une vue partitionnée distribuée vous intéresse, mais je n'en ai aucune expérience.
Lieven Keersmaekers
0

Je pense que vous feriez mieux de répliquer la table distante sur le serveur à partir duquel vous interrogez, puis d'exécuter tout votre SQL localement.

Alen
la source