Application interrogeant des tables vides

10

Mon entreprise utilise une application qui présente des problèmes de performances assez importants. Il y a un certain nombre de problèmes avec la base de données elle-même que je suis en train de résoudre, mais beaucoup de problèmes sont purement liés à l'application.

Dans mon enquête, j'ai découvert qu'il y avait des millions de requêtes frappant la base de données SQL Server qui interrogent des tables vides. Nous avons environ 300 tables vides et certaines de ces tables sont interrogées jusqu'à 100-200 fois par minute. Les tableaux n'ont rien à voir avec notre domaine d'activité et font essentiellement partie de l'application d'origine que le fournisseur n'a pas supprimée lorsqu'ils ont été engagés par mon entreprise pour produire une solution logicielle pour nous.

Outre le fait que nous soupçonnons que notre journal d'erreurs d'application est inondé d'erreurs liées à ce problème, le fournisseur nous assure qu'il n'y a aucun impact sur les performances ou la stabilité pour l'application ou le serveur de base de données. Le journal des erreurs est inondé dans la mesure où nous ne pouvons pas voir plus de 2 minutes d'erreurs pour effectuer des diagnostics.

Le coût réel de ces requêtes va évidemment être faible en termes de cycles CPU, etc. Mais quelqu'un peut-il suggérer quel serait l'effet sur SQL Server et l'application? Je soupçonne que les mécanismes réels d'envoi d'une demande, de confirmation, de traitement, de retour et d'accusé de réception de la demande auraient eux-mêmes un impact sur les performances.

Nous utilisons SQL Server 2008 R2, Oracle Weblogic 11g pour l'application.

@ Frisbee - Bref, j'ai créé une table contenant le texte de la requête qui a frappé les tables vides dans la base de données de l'application, puis je l'ai interrogé pour tous les noms de tables que je sais vides et j'ai obtenu une très longue liste. Le résultat le plus élevé a été de 2,7 millions d'exécutions sur 30 jours de disponibilité, sachant que l'application est généralement utilisée de 8h00 à 18h00, de sorte que ces chiffres sont plus concentrés sur les heures opérationnelles. Plusieurs tables, plusieurs requêtes, probablement certaines relavent via des jointures, d'autres non. Le résultat le plus élevé (2,7 millions à l'époque) était un simple choix dans une seule table vide avec une clause where, pas de jointure. Je m'attendrais à ce que des requêtes plus importantes avec des jointures aux tables vides puissent inclure des mises à jour des tables liées, mais je vais vérifier cela et mettre à jour cette question dès que possible.

Mise à jour: il y a 1000 requêtes avec un nombre d'exécutions compris entre 1043 et 4622614 (sur 2,5 mois). Je vais devoir creuser plus pour savoir d'où vient le plan mis en cache. C'est juste pour vous donner une idée de l'étendue des requêtes. La plupart sont raisonnablement complexes avec plus de 20 jointures.

@ srutzky- oui, je crois qu'il y a une colonne de date liée au moment où le plan a été compilé, ce qui serait intéressant, donc je vais vérifier cela. Je me demande si les limites de threads sont un facteur lorsque le serveur SQL se trouve sur un cluster VMware? Bientôt un Dell PE 730xD dédié, heureusement.

@Frisbee - Désolé pour la réponse tardive. Comme vous l'avez suggéré, j'ai exécuté une sélection * de la table vide 10 000 fois sur 24 threads à l'aide de SQLQueryStress (donc en fait 240 000 itérations) et j'ai immédiatement frappé 10 000 requêtes par lot. Ensuite, j'ai réduit à 1000 fois plus de 24 threads et atteint un peu moins de 4 000 demandes de lot / s. J'ai également essayé 10 000 itérations sur seulement 12 threads (donc 1 20000 itérations au total), ce qui a produit un maintien de 6 505 lots / s. L'effet sur le processeur était en fait notable, environ 5 à 10% de l'utilisation totale du processeur lors de chaque test. Les temps d'attente sur le réseau étaient négligeables (comme 3 ms avec le client sur mon poste de travail) mais l'impact du processeur était là, c'est certain, ce qui est assez concluant en ce qui me concerne. Il semble se résumer à l'utilisation du processeur et à un peu d'E / S de fichiers de base de données inutiles. Le nombre total d'exécutions / seconde s'élève à un peu moins de 3000, ce qui est plus qu'en production, mais je ne teste qu'une seule des dizaines de requêtes comme celle-ci. L'effet net de centaines de requêtes atteignant des tables vides à un taux compris entre 300 et 4000 fois par minute ne serait donc pas négligeable en ce qui concerne le temps CPU. Tous les tests effectués contre un PE 730xD inactif avec double baie flash et 256 Go de RAM, 12 cœurs modernes. Ceci est la sortie de SQLSentry

@ srutzky- bonne pensée. SQLQueryStress semble utiliser le regroupement de connexions par défaut, mais j'ai quand même regardé et j'ai constaté que oui, la case pour le regroupement de connexions est cochée. Mise à jour à suivre

@ srutzky- Le pool de connexions n'est apparemment pas activé sur l'application - ou s'il l'est, il ne fonctionne pas. J'ai fait une trace de profileur et j'ai constaté que les connexions ont EventSubClass "1 - Non groupé" pour les événements de connexion d'audit.

RE: Connection Pooling - Vérifié les weblogics et trouvé le pool de connexions activé. Ran plus de traces contre les signes en direct et trouvés de regroupement ne se produisant pas correctement / pas du tout: entrez la description de l'image ici

Et voici à quoi cela ressemble lorsque j'exécute une seule requête sans jointure sur une table remplie; les exceptions indiquent "Une erreur liée au réseau ou spécifique à l'instance s'est produite lors de l'établissement d'une connexion à SQL Server. Le serveur n'a pas été trouvé ou n'était pas accessible. Vérifiez que le nom de l'instance est correct et que SQL Server est configuré pour autoriser les connexions à distance. (fournisseur: fournisseur de canaux nommés, erreur: 40 - Impossible d'ouvrir une connexion à SQL Server) "Notez le compteur de demandes par lots. Le ping du serveur pendant la génération des exceptions entraîne une réponse ping réussie.

entrez la description de l'image ici

Mise à jour - deux exécutions de test consécutives, même charge de travail (sélectionnez * fromEmptyTable), regroupement activé / non activé. Un peu plus d'utilisation du processeur et beaucoup d'échecs et ne dépasse jamais 500 requêtes par lot. Les tests montrent 10 000 lots / s et aucune défaillance avec le regroupement activé, et environ 400 lots / s, puis beaucoup d'échecs dus à la désactivation du regroupement. Je me demande si ces échecs sont liés à un manque de disponibilité de connexion?

entrez la description de l'image ici

@ srutzky- Sélectionnez Count (*) dans sys.dm_exec_connections;

  • Pooling activé: 37 de manière cohérente, même après l'arrêt du test de charge

  • Pooling désactivé: 11-37 selon que des exceptions se
    produisent ou non sur SQLQueryStress, c'est-à-dire que lorsque ces creux apparaissent sur le
    graphique Batch / sec, les exceptions se produisent sur SQLQueryStress et le
    nombre de connexions tombe à 11, puis revient progressivement à 37 lorsque les lots commencent à culminer et que les exceptions ne se produisent pas. Très, très intéressant.

Le nombre maximal de connexions sur les deux instances de test / live est défini sur 0 par défaut.

Après avoir vérifié les journaux des applications et ne pas trouver de problèmes de connectivité, il ne reste que quelques minutes de journalisation disponibles en raison du grand nombre et de la taille des erreurs, à savoir: beaucoup d'erreurs de trace de pile. Un collègue du support des applications indique qu'un nombre important d'erreurs HTTP se produisent en raison de la connectivité. Il semblerait basé sur cela, que pour une raison quelconque l'application ne regroupe pas correctement les connexions et par conséquent, le serveur manque à plusieurs reprises de connexions. J'examinerai davantage les journaux d'application. Je me demande s'il existe un moyen de prouver que cela se produit dans la production du côté de SQL Server?

@ srutzky- Merci. Je vérifierai la configuration weblogic demain et mettrai à jour. Je pensais cependant aux 37 simples connexions - si SQLQueryStress fait 12 threads à 10 000 itérations = 120 000 instructions de sélection non regroupées, cela ne devrait-il pas signifier que chaque sélection crée une connexion distincte à l'instance SQL?

@ srutzky- Weblogics sont configurés pour regrouper les connexions, donc cela devrait fonctionner correctement. Le regroupement de connexions est configuré comme ceci, sur chacun des 4 blogues à charge équilibrée:

  • Capacité initiale: 10
  • Capacité maximale: 50
  • Capacité minimum: 5

Lorsque j'augmente le nombre de threads exécutant la sélection à partir d'une requête de table vide, le nombre de connexions culmine autour de 47. Lorsque le pool de connexions est désactivé, je constate systématiquement une baisse du nombre maximal de demandes de lot / s (de 10 000 à environ 400). Ce qui se produira à chaque fois, c'est que les «exceptions» sur SQLQueryStress se produisent peu de temps après que les lots / s soient entrés dans un creux. C'est lié à la connectivité, mais je ne peux pas comprendre exactement pourquoi cela se produit. Lorsqu'aucun test n'est en cours, #connections descend à environ 12.

Avec la mise en commun des connexions désactivée, j'ai du mal à comprendre pourquoi les exceptions se produisent, mais peut-être qu'il s'agit d'une toute autre question stackExchange / question pour Adam Machanic?

@srutzky Je me demande alors pourquoi les exceptions se produisent sans regroupement activé, même si SQL Server ne manque pas de connexions?

Peter
la source
1
Peter, en gardant à l'esprit les mises à jour les plus récentes concernant le pool de connexions, il semble que vous deviez maintenant réexécuter vos tests avec SQLQueryStress mais avec le pool de connexions désactivé . Ce serait un reflet plus précis des effets du fonctionnement de l'application, et je pense que cela montrera une augmentation de l'utilisation du CPU et même de la RAM.
Solomon Rutzky
1
Peter, avez-vous un nombre maximum de connexions définies pour le serveur? Je suppose que sans la mise en commun, vous rencontrez un problème de trop de connexions. Je me demande si votre application obtient jamais cette erreur. De plus, si possible pour réexécuter ce dernier test une fois de plus (avec et sans regroupement activé), pendant que le test est en cours d'exécution pour chacune de ces deux configurations, exécutez a SELECT COUNT(*) FROM sys.dm_exec_connections;pour voir si la valeur est très différente entre le regroupement activé ou ne pas. Sur la base de ces erreurs, je pense qu'il y aurait beaucoup plus de connexions lorsque la mise en commun est désactivée.
Solomon Rutzky
1
Peter, 37 connexions semble être un maximum terriblement bas. Étant donné que la limite de connexion est définie sur 0 (c'est-à-dire illimitée), la mémoire système est-elle liée? En outre, le regroupement de connexions doit être activé par défaut, mais il est contrôlé par le client. L'application est-elle une application .NET? N'a pas besoin d'être pour utiliser le pool de connexions, mais aiderait à savoir afin de trouver la cause de cela. Et pouvez-vous voir quelle chaîne de connexion est utilisée? Précise-t-il Pooling=falseou Max Pool Size?
Solomon Rutzky
1
Peter, chacun des 12 threads crée sa propre connexion par requête, séquentiellement pour les 10 000 itérations. Ainsi, sans regroupement, la connexion peut être détruite dès que le code ferme la connexion. Le regroupement conservera la connexion pour la réutilisation. Il est donc logique que le nombre de connexions soit cohérent lors de l'utilisation du pooling. Je ne sais pas pourquoi 37 sans plus d'informations. Combien de connexions y a-t-il quand aucun test n'est en cours? La suppression de ce nombre donnera une meilleure indication du nombre créé par le test.
Solomon Rutzky
1
Le regroupement de connexions est géré par client et non par serveur. WebLogics et SQLQueryStress doivent donc chacun avoir leurs propres pools de connexions (en termes de tailles min_pool et max_pool, etc.). Concernant "Avec le pool de connexions désactivé, je vois un nombre de requêtes par lot max inférieur / sec": cela a du sens car il faut plus de temps pour chaque connexion de l'application pour authentifier et initialiser la session, etc. C'est exactement pourquoi le pool de connexions existe: - ).
Solomon Rutzky

Réponses:

7

Je soupçonne que les mécanismes réels d'envoi d'une demande, de confirmation, de traitement, de retour et d'accusé de réception de la demande auraient eux-mêmes un impact sur les performances.

Oui, et il y a même des facteurs supplémentaires, mais il est impossible de dire dans quelle mesure ces éléments affectent réellement votre système sans analyser le système.

Cela étant dit, vous demandez ce qui pourrait être un problème, et il y a certaines choses à mentionner, même si certaines d'entre elles ne sont pas actuellement un facteur dans votre situation particulière. Vous dites que:

Nous avons environ 300 tables vides et certaines de ces tables sont interrogées jusqu'à 100-200 fois par minute.

  • Les tables vides qui ne sont pas interrogées ne sont pas un problème. Mais je suppose que vous pourriez aussi signifier qu'ils sont tous interrogés, juste que certains sont beaucoup plus touchés que d'autres.
  • L'analyse des requêtes et la génération du plan d'exécution ne devraient pas être un gros problème si le texte de la requête soumis reste le même d'un appel à l'autre. SQL Server hache le texte de la requête et le recherche dans le cache du plan. S'il est trouvé, il ne recommencera pas les étapes d'analyse ou de compilation (jusqu'à ce que le plan soit supprimé du cache).
  • Toute table, vide ou non vide, nécessitera au moins un verrou "partagé" pour indiquer que la ressource est utilisée. Cela empêche les opérations qui nécessitent des verrous exclusifs (ajout / modification / suppression de colonnes, etc.) d'effectuer la modification pendant l'utilisation de la ressource. Le verrouillage et le déverrouillage, même s'ils sont effectués en moins de 1 milliseconde car il n'y a pas de données, nécessitent toujours des ressources système (mémoire et CPU) pour gérer ces opérations de verrouillage.
  • Même si aucun jeu de résultats ne revient à l'application à partir de SQL Server, il y a toujours la même quantité de trafic réseau vers SQL Server, que la requête donne des résultats ou non. Le texte de la requête ou le nom de la procédure stockée doit être envoyé. Et même si aucun résultat ne revient, SQL Server doit toujours envoyer des paquets réseau contenant la structure de l'ensemble de résultats en plus des paquets indiquant au client qu'un ensemble de résultats démarre (même si aucune ligne n'est trouvée), puis que l'ensemble de résultats est fin et doit être fermé. Et il pourrait y avoir des messages supplémentaires provenant des instructions d'impression et / ou du nombre de lignes.
  • La connexion à SQL Server nécessite une certaine quantité de ressources système. Il faut du CPU et de la mémoire pour gérer l'authentification (ainsi que les paquets réseau dans les deux sens) et cela prend également du temps. C'est pourquoi le pool de connexions existe: pour réduire ces dépenses.
  • Même avec le regroupement de connexions réduisant l'utilisation des ressources système, SQL Server doit toujours maintenir ces connexions et cela nécessite de la mémoire et un minimum de CPU.
  • Même sans lignes et donc avec un temps d'exécution très rapide, la requête était toujours exécutée. Même s'il y avait 10 ou 10 000 lignes et celles-ci ont été extraites du pool de tampons (c'est-à-dire la mémoire) car elles étaient utilisées fréquemment, un thread doit toujours faire ce travail. Et un thread qui travaille sur cette requête inutile ne fonctionne pas sur une requête utile réelle.

Il pourrait même y en avoir plus, mais cela devrait aider à comprendre les choses. Et gardez à l'esprit que, comme la plupart des problèmes de performances, tout est une question d'échelle. Tous les éléments mentionnés ci-dessus ne posent aucun problème s'ils sont touchés une fois par minute. C'est comme tester une modification sur votre poste de travail ou dans la base de données de développement: cela fonctionne toujours avec seulement 10 à 100 lignes dans les tables. Déplacez ce code en production et cela prend 10 minutes pour s'exécuter, et quelqu'un est obligé de dire: "eh bien, cela fonctionne sur ma boîte" ;-). Cela signifie que c'est uniquement en raison du volume important d'appels que vous voyez un problème, mais c'est la situation qui existe.

Donc, même pour 1 million de requêtes à 0 ligne inutiles, cela revient à:

  • 2 millions de verrous supplémentaires (chaque verrou doit être déverrouillé, non?). c'est surtout un coût de temps consacré à une opération inutile plutôt qu'à une opération utile.
  • plus de trafic réseau qui pourrait vous rapprocher de la saturation (pas sûr de la probabilité, mais quand même)
  • plus de connexions maintenues qui occupent plus de mémoire. De combien de RAM physique inutilisée disposez-vous? cette mémoire serait mieux utilisée pour exécuter des requêtes et / ou un cache de plan de requête. Le pire des cas serait que vous manquez de mémoire physique et SQL Server doit commencer à utiliser la mémoire virtuelle (swap), car cela ralentit les choses (consultez votre journal des erreurs SQL Server pour voir si vous recevez des messages sur la mémoire paginée).

    Et juste au cas où quelqu'un mentionne, "eh bien, il y a une mise en commun des connexions". Oui, cela aide certainement à réduire le nombre de connexions nécessaires. Mais avec des requêtes pouvant atteindre 200 fois par minute, cela représente beaucoup d'activité simultanée et des connexions doivent encore exister pour les requêtes légitimes. Faites un SELECT * FROM sys.dm_exec_connections;pour voir combien de connexions actives vous maintenez.

  • indépendamment de toute autre chose, c'est toujours au moins 1 million de fois par jour qu'un thread qui aurait pu faire quelque chose d'utile n'était pas disponible.

Si je ne me trompe pas sur ce que j'ai déclaré ici, il me semble que, même à petite échelle, il s'agit d'un type d'attaque DDoS sur votre système car il envahit le réseau et votre serveur SQL avec de fausses demandes. , ce qui empêche les demandes réelles d'accéder à SQL Server ou d'être traitées par SQL Server.

Solomon Rutzky
la source
1

Si les tables sont frappées 100 à 200 fois par minute, elles sont (espérons-le) en mémoire. La charge sur le serveur est très très faible. À moins que vous n'ayez un processeur ou une mémoire élevé sur le serveur de base de données, il s'agit probablement d'un problème.

Oui, les requêtes prennent des verrous partagés mais, espérons-le, ne bloquent aucun verrou de mise à jour et ne sont bloqués par aucun verrou de mise à jour. Avez-vous des mises à jour, des insertions ou des suppressions sur ces tableaux? Sinon, je laisserais tomber - si vous rencontrez des problèmes de performances, il doit y avoir un plus gros poisson à frire du point de vue du serveur de base de données.

J'ai exécuté un test sur 100 000 comptes sélectionnés (*) sur une table vide et il a fonctionné en 32 secondes et les requêtes ont été effectuées sur un réseau. Donc 1/3 milliseconde. À moins que votre réseau ne soit surchargé, cela n'a même pas d'impact sur le client. Si vous rencontrez des problèmes de performances majeurs, ces requêtes vides de 1/3 millisecondes ne sont pas ce qui tue l'application.

Et ceux-ci pourraient être juste une partie d'une jointure gauche saisissant des données de type statique qui ne fait pas partie de l'application actuelle. Il pourrait être enchaîné avec d'autres requêtes, ce n'est donc pas un aller-retour supplémentaire. Si oui, c'est bâclé, mais cela ne cause même pas plus de trafic.

Revenons donc aux déclarations réelles. Voyez-vous des mises à jour, des ajouts ou des suppressions sur ces tables?

Oui, de nombreuses tables vides et requêtes sur des tables vides indiquent un codage bâclé. Mais si vous rencontrez des problèmes de performances majeurs, ce n'est pas la cause, sauf si vous avez également des opérations d'écriture vraiment bâclées avec ces tables.

paparazzo
la source
Combien d'autres utilisateurs étaient sur le serveur SQL exécutant des requêtes lorsque vous avez effectué votre test de 100 000 requêtes? Je ne dis pas que j'ai raison et que vous avez tort, mais si vous étiez le seul sur le système, ou l'un des rares, alors naturellement vous ne verriez pas beaucoup d'impact. Le problème du verrouillage n'était pas une question de blocage, c'était simplement une question de ressources dont il avait besoin pour SQL Server pour verrouiller et déverrouiller ces pages de données, même si elles étaient toujours dans le pool de tampons. C'est encore du travail qui se fait. Et les programmateurs ne sont pas illimités.
Solomon Rutzky
Et je ne dis pas que vous vous trompez. D'autres utilisateurs ou non, c'est toujours une mesure valable du temps qu'il a fallu et une mesure des ressources La charge indiquée est de 100-200 par minute. 100 000 d'un client en 30 secondes dépassent cette charge d'un facteur de 200 à 400. S'il n'y a pas de verrous de mise à jour, alors si cela venait d'un client ou 100, cela ne fait aucune différence. Votre réponse suppose qu'il existe un réseau surchargé ou un serveur SQL et que vous ne le savez pas. S'il s'agissait d'une attaque DDoS, il y en aurait plus comme 100 / sec (pas une minute) et ce ne serait pas contre une table vide.
paparazzo
Exact, sur la base de la question que nous ne connaissons pas suffisamment pour le réduire, c'est pourquoi je disais que ces choses pourraient être un problème, selon les circonstances. Et la chose DDoS n'était qu'une analogie, principalement basée sur le libellé de la question d'origine, ce qui impliquait que plusieurs étaient touchés à ce rythme et que beaucoup d'autres étaient touchés également, mais moins fréquemment.
Solomon Rutzky
Je considère que c'est une réponse valable dans le sens où le premier paragraphe le résume très bien: "A moins que vous n'ayez un CPU ou une mémoire élevée sur le serveur de base de données, il s'agit probablement d'un problème." Dans notre cas, nous avons une utilisation élevée du processeur à certains moments de la journée et donc la pression supplémentaire du processeur semble être un facteur basé sur mes tests.
Peter
Notamment, je n'ai cité que des requêtes exécutées 100 à 200 fois / minute, alors qu'en réalité, il y a environ 50 requêtes vers ces tables vides avec un nombre d'exécutions compris entre 200 et 4000 / minute. Cumulativement, l'effet d'interroger des tables vides avec cette fréquence affecte beaucoup le processeur, même dans le meilleur des cas de requêtes non paramétrées s'exécutant à plusieurs reprises, de sorte que le plan, les données, etc. sont tous en mémoire.
Peter
0

En général, pour chaque requête, les étapes suivantes sont effectuées:

  1. Demande de candidature.
  2. Base de données Analyser la requête.
  3. Le moteur de base de données vérifie si cette requête est déjà stockée dans la RAM. utiliser le plan d'exécution s'il existe en mémoire.
  4. s'il n'existe pas dans la RAM, le moteur de base de données vérifie les statistiques existantes sur les objets de la requête et détermine le plan d'exécution.
  5. Exécutez le plan d'exécution, utilisez les E / S pour obtenir les données du disque.
  6. réponse à la demande.

de nombreuses requêtes, comme vous l'avez mentionné, peuvent entraîner une charge supplémentaire sur un système qui est déjà lourd - une charge supplémentaire sur les connexions, le processeur, la RAM et les E / S.

alonk
la source