Problèmes de performances SQL avec requête distante sur un serveur lié

8

Ce sproc

create proc dbo.Get_Accounts as
begin
  declare @current_date datetime
  set @current_date = dbo.fn_currdate()

  select [fields]
  into dbo.current_accounts
  from linkedserver.database.dbo.accounts
  where date = @current_date
end

échoue continuellement après 10 minutes avec le message d'erreur suivant:

Serveur: Msg 7399, niveau 16, état 1, fournisseur OLE DB de ligne 1 «SQLOLEDB» a signalé une erreur. Exécution terminée par le fournisseur car une limite de ressources a été atteinte. [Le fournisseur OLE / DB a renvoyé un message: le délai a expiré] La trace d'erreur OLE DB [Le fournisseur OLE / DB 'SQLOLEDB' ICommandText :: Execute a renvoyé 0x80040e31: exécution terminée par le fournisseur car une limite de ressources a été atteinte.].

Cependant, lorsque j'exécute la même requête à partir de la même base de données (pas sur celle distante) dans une fenêtre de requête interactive avec la date codée en dur:

  select [fields]
  into dbo.current_accounts
  from linkedserver.database.dbo.accounts
  where date = '1/20/2012'

Il revient en 30 secondes.

Le serveur local est SQLSERVER 2008, la télécommande est SQLSERVER 2000.

Nous avons fait ce qui suit en vain:

  • Recréé le proc stocké.
  • sp_recompile sur le proc stocké
  • mettre à jour les statistiques sur dbo.accounts
  • supprimé et recréé les index sur dbo.accounts
  • supprimé l'index sur dbo.accounts et essayez
  • DBCC FREEPROCCACHE & DBCC DROPCLEANBUFFERS sur les serveurs locaux et distants
  • Redémarrage du serveur distant (pas une option facile sur le serveur local)

Des questions

  • Quelqu'un peut-il expliquer ce comportement bizarre?
  • Des suggestions sur d'autres options pour le corriger?
Bob Probst
la source

Réponses:

11

Vous pouvez activer l' indicateur de trace 7300 qui pourrait vous donner un message d'erreur plus détaillé

Combien de lignes une requête représentative renvoie-t-elle? Quelle est la vitesse / la fiabilité de la connexion réseau entre les deux serveurs?

Il est possible qu'un grand ensemble de données prenne trop de temps à transférer (en plus du temps de requête réel). Vous pouvez augmenter la valeur du délai d'attente.

Vous pouvez essayer de reconfigurer le paramètre de délai d'expiration comme suit:

Définissez le délai de connexion à distance sur 300 secondes:

sp_configure 'remote login timeout', 300
go 
reconfigure with override 
go 

Définissez le délai d'expiration de la requête distante sur 0 (attente infinie):

sp_configure 'remote query timeout', 0 
go 
reconfigure with override 
go 

Mise à jour : SQL Server 2012 SP1 et ultérieur : utilisateurs avecSELECT autorisés auront accès, DBCC SHOW_STATISTICSce qui améliorera les performances en lecture seule sur les serveurs liés. Réf: https://msdn.microsoft.com/en-us/library/ms174384(v=sql.110).aspx

Mise à jour : vous avez raison de dire que ce n'est pas la taille des données ou la vitesse de connexion. Cela sonna une cloche dans ma mémoire brumeuse et je me souvins où je l'avais vu: lent dans l'application, rapide dans SSMS? (Un problème avec les serveurs liés). Ce n'est pas le reniflement de paramètres, ce sont les statistiques elles-mêmes qui manquent (en raison des autorisations), ce qui entraîne l'utilisation d'un mauvais plan de requête:

Vous pouvez voir que les estimations sont différentes. Lorsque j'ai couru en tant qu'administrateur système, l'estimation était de 1 ligne, ce qui est un nombre correct, car il n'y a pas de commandes dans Northwind où l'ID de commande dépasse 20000. Mais lorsque j'ai couru en tant qu'utilisateur ordinaire, l'estimation était de 249 lignes. Nous reconnaissons ce nombre particulier comme 30% des 830 commandes, ou l'estimation pour une opération d'inégalité lorsque l'optimiseur n'a aucune information. Auparavant, cela était dû à une valeur de variable inconnue, mais dans ce cas, aucune variable ne peut être inconnue. Non, ce sont les statistiques elles-mêmes qui manquent.

Tant qu'une requête accède uniquement aux tables du serveur local, l'optimiseur peut toujours accéder aux statistiques de toutes les tables de la requête; il n'y a pas de vérification d'autorisation supplémentaire. Mais c'est différent avec des tables sur un serveur lié. Lorsque SQL Server accède à un serveur lié, aucun protocole secret n'est utilisé uniquement pour la communication inter-serveur. Non, SQL Server utilise à la place l'interface OLE DB standard pour les serveurs liés, soit d'autres instances SQL Server, Oracle, des fichiers texte ou votre source de données brassée à la maison, et se connecte comme n'importe quel autre utilisateur. La façon exacte dont les statistiques sont récupérées dépend de la source de données et du fournisseur OLE DB en question. Dans ce cas, le fournisseur est SQL Server Native Client qui récupère les statistiques en deux étapes. (Vous pouvez le voir en exécutant Profiler sur le serveur distant). Tout d'abord, le fournisseur exécute la procédure sp_table_statistics2_rowset qui renvoie des informations sur les statistiques de colonne, ainsi que leur cardinalité et leurs informations de densité. Dans la deuxième étape, le fournisseur exécute DBCC SHOW_STATISTICS, une commande qui renvoie les statistiques de distribution complètes. (Nous examinerons de plus près cette commande plus loin dans cet article.) Voici le problème: pour exécuter DBCC SHOW_STATISTICS, vous devez être membre du rôle serveur sysadmin ou de l'un des rôles de base de données db_owner ou db_ddladmin.

Et c'est pourquoi j'ai obtenu des résultats différents. Lors de l'exécution en tant que sysadmin, j'ai obtenu les statistiques de distribution complètes qui indiquaient qu'il n'y avait pas de lignes avec l'ID de commande> 20000, et l'estimation était une ligne. (Rappelez-vous que l'optimiseur ne suppose jamais zéro ligne de statistiques.) Mais lors de l'exécution en tant qu'utilisateur ordinaire, DBCC SHOW_STATISTICS a échoué avec une erreur d'autorisation. Cette erreur n'a pas été propagée, mais à la place, l'optimiseur a accepté l'absence de statistiques et a utilisé des hypothèses par défaut. Puisqu'il a obtenu des informations de cardinalité, il a appris que la table distante a 830 lignes, d'où l'estimation de 249 lignes.

Chaque fois que vous rencontrez un problème de performances où une requête qui inclut l'accès à un serveur lié est lente dans l'application, mais s'exécute rapidement lorsque vous la testez à partir de SSMS, vous devez toujours rechercher si des autorisations insuffisantes sur la base de données distante pourraient en être la cause. (N'oubliez pas que l'accès au serveur lié peut ne pas être évident dans la requête, mais peut être masqué dans une vue.) Si vous déterminez que les autorisations sur la base de données distante sont le problème, quelles actions pourriez-vous entreprendre?

  • Vous pouvez ajouter les utilisateurs au rôle db_ddladmin, mais comme cela leur donne le droit d'ajouter et de supprimer des tables, ce n'est pas recommandé.

  • Par défaut, lorsqu'un utilisateur se connecte à un serveur distant, il se connecte en tant que lui-même, mais vous pouvez configurer un mappage de connexion avec sp_addlinkedsrvlogin, de sorte que les utilisateurs mappent vers un compte proxy qui appartient à db_ddladmin. Notez que ce compte proxy doit être une connexion SQL, donc ce n'est pas une option si le serveur distant n'a pas d'authentification SQL activée. Cette solution est également quelque peu douteuse du point de vue de la sécurité, bien que ce soit mieux la suggestion précédente.

  • Dans certains cas, vous pouvez réécrire la requête avec OPENQUERY pour forcer l'évaluation sur le serveur distant. Cela peut être particulièrement utile si la requête comprend plusieurs tables distantes. (Mais cela peut également se retourner contre vous, car l'optimiseur obtient désormais encore moins d'informations statistiques du serveur distant.)

  • Vous pouvez bien sûr utiliser la batterie complète d'indices et de guides de plan pour obtenir le plan que vous souhaitez.

  • Enfin, vous devez vous demander si cet accès au serveur lié est nécessaire. Peut-être que les bases de données pourraient être sur le même serveur? Les données peuvent-elles être répliquées? Une autre solution?

Blé Mitch
la source
Il renvoie environ 140k enregistrements. mais comme cela fonctionne très bien lorsque la valeur de la date est codée en dur, je ne peux pas penser à un problème d'E / S ou de réseau qui affecterait extrêmement la version paramétrée. Mon instinct dit que la requête est transmise au serveur distant et l'optimiseur distant choisit en quelque sorte un mauvais plan de requête lorsqu'il ne peut pas comprendre le paramètre. Mais la réindexation et la purge du cache / des tampons devraient corriger cela (je suppose). J'examinerai les délais d'attente pour voir si nous pouvons au moins revenir. Merci
1
Excellente réponse et a expliqué exactement le problème que je rencontrais, merci. J'ajouterais que selon le MSDN , à partir de SQL2012 SP1, les utilisateurs SELECTautorisés auront accès, DBCC SHOW_STATISTICSce qui améliorera les performances en lecture seule sur les serveurs liés sans avoir à compromettre la sécurité.
Steve Pettifer
2

Que se passe-t-il lorsque vous essayez ceci (c'est-à-dire indiquer explicitement ce qui doit être exécuté sur le serveur distant)?:

select [fields]
into dbo.current_accounts
from OPENQUERY(linkedserver, 'SELECT [fields] FROM database.dbo.accounts where date = ''1/20/2012''');

Je soupçonne que dans votre cas ci-dessus, SQL Server tire simplement la table entière du serveur distant puis exécute la requête localement (j'ai vu cela se produire plusieurs fois dans le passé). Je préfère être explicite (soit en utilisant OPENQUERY soit en créant un SP sur le serveur distant) donc il n'y a aucun risque de confusion.

Gareth
la source
1

Comme il s'agit d'un problème de ressource, le pool de mémoire en dehors du serveur SQL utilisé pour charger des pilotes externes et le CLR peut être proche de sa limite. La valeur par défaut est 256 Mo. Pour contourner ce problème, je vous suggère d'aller dans le gestionnaire de configuration de SQL Server, onglet avancé et d'ajouter l'option -g à la fin des paramètres de démarrage.ie; -g1024 puis de redémarrer le service SQL Server. Je fais généralement cela car nous utilisons un nombre élevé de serveurs liés. http://msdn.microsoft.com/en-us/library/ms190737.aspx

nopol
la source
1

J'ai deux idées qui pourraient aider. Je vais également vous dire que j'ai eu des problèmes de chance avec l'exécution de requêtes sur des serveurs liés. Donc ma première recommandation est de l'éviter si vous le pouvez.

Ma première idée est d'installer la procédure stockée dans la boîte SQL Server 2000, en la faisant référence au serveur local. Vous pouvez ensuite exécuter la procédure stockée à distance.

exec linkedserver.database.dbo.Get_Accounts

Si vous pouvez suivre cette voie, cela devrait améliorer considérablement les performances.

Ma deuxième idée est d'obtenir le plan de requête estimé lors de l'exécution de la procédure stockée. Cela vous montre-t-il ce qui prend autant de temps? Un problème potentiel est que le compte que vous utilisez sur le serveur lié peut ne pas avoir suffisamment d'autorité pour obtenir les statistiques de la table (vous avez besoin de plus d'autorité pour le serveur lié que pour le serveur local). Et cela peut ralentir considérablement les requêtes. Vous pouvez en savoir plus sur ce problème particulier ici .

Jeff Siver
la source