Je travaille sur une application existante. Cette application lit les données d'un fichier énorme, puis, après avoir effectué des calculs, il stocke les données d'une autre table.
Mais la boucle faisant cela (voir ci-dessous) prend vraiment longtemps. Étant donné que le fichier contient parfois 1 000 d'enregistrements, l'ensemble du processus prend des jours. P>
Puis-je remplacer ce Après avoir examiné les réponses, j'ai supprimé l'ASYNC et utilisé édité le code comme ci-dessous. Mais cela n'a pas amélioré les performances. P> foreach code> boucle avec quelque chose d'autre? J'ai essayé d'utiliser parallel.foreach code> et il a aidé. Je suis nouveau à cela, alors apprécierez votre aide. P> using (command = new SqlCommand("[sp]", sqlConn))
{
command.CommandTimeout = 0;
command.CommandType = CommandType.StoredProcedure;
foreach (record someRecord in someReport.)
{
command.Parameters.Clear();
command.Parameters.Add(....)
command.Prepare();
using (dr = command.ExecuteReader())
{
while (dr.Read())
{
if ()
{
}
else if ()
{
}
}
}
}
}
6 Réponses :
Au lieu de boucler la connexion SQL tant de fois, envisagez jamais d'extraire l'ensemble de données de SQL Server et de traiter les données via le jeu de données? P>
Edit: a décidé d'expliquer davantage ce que je voulais dire .. Vous pouvez faire ce qui suit, pseudo code comme suit p>
+1. Mais il serait probablement préférable de charger les données dans une collection fortement dactylographiée, puis utilisez Linq à ce sujet plutôt que d'utiliser un jeu de données.
J'ai essayé d'utiliser une base de données, mais pour une raison quelconque, il a encore ralenti le processus. Nous vous enregistrons également chaque opération. Pensez-vous que je peux faire la journalisation sur un thread distinct améliorer les performances?
@ user1110790 - De mon expérience, les jeux de données sont généralement lents au travail. C'est pourquoi j'ai suggéré une collection fortement dactylographiée. Travailler simplement avec une collection iEnumerable en mémoire va être très rapide. Et si vous faites beaucoup de recherches clés, cela pourrait être rendu encore plus rapide avec un dictionnaire code>.
@Stevewortham Cela nécessitera probablement des changements dans la classe de base. Pouvez-vous m'envoyer un exemple? On dirait que ce problème n'a pas de solution simple. Merci beaucoup
@ user1110790 - Je viens de poster une réponse décrivant comment je le ferais ... Stackoverflow.com/a/12205953/102896
Édité ma réponse pour expliquer plus comment vous pouvez atteindre ce que j'ai suggéré.
@Guohonglim Le système actuel lit les données du fichier plat et en fonction des données calcons les valeurs totales, etc. Ces valeurs sont maintenant stockées dans une liste. Enregistrement Somerecord à Somereport se réfère à la liste.
AVERTISSEMENT: strong> Assurez-vous de réinitialiser / effacer / supprimer les paramètres que vous n'avez pas besoin de l'itération précédente. Nous avons fait quelque chose comme ça avec des paramètres facultatifs et «saignez-en-travers» de l'itération précédente, car nous n'avons pas nettoyé les paramètres que nous n'avions pas besoin! P>
+1 La deuxième étape est très importante, beaucoup de gens oublient cela.
Le 'Étape 2: Déplacez le SQLCOMMAND en dehors de la boucle ...' est l'amélioration majeure !!!
Votre plus gros problème est que vous en boucle sur ceci:
public void BeginExecutingCommands(Report someReport)
{
foreach (record someRecord in someReport.r)
{
var command = new SqlCommand("[procname]", sqlConn);
command.CommandTimeout = 0;
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(â¦);
command.BeginExecuteReader(ReaderExecuted,
new object[] { command, someReport, someRecord });
}
}
void ReaderExecuted(IAsyncResult result)
{
var state = (object[])result.AsyncState;
var command = state[0] as SqlCommand;
var someReport = state[1] as Report;
var someRecord = state[2] as Record;
try
{
using (SqlDataReader reader = command.EndExecuteReader(result))
{
// work with reader, command, someReport and someRecord to do what you need.
}
}
catch (Exception ex)
{
// handle exceptions that occurred during the async operation here
}
}
J'ai supprimé le modificateur d'accessibilité du Public code> à partir de la méthode de rappel ( ReadReRexecuted code>). Ceux-ci ne devraient pas être publics, car ils ne sont pas des opérations complètes, mais seulement le «reste» logique d'une autre méthode.
+1 Pour démontrer l'utilisation correcte du Commencez ... code> / fin ... code> Async motif. Cependant, je ne suis pas sûr de 100% que cela corrige le problème principal. Je ne suis pas non plus certain à quel point la piscine de fil et la DB traiteront-elles éventuellement 1 000 de demandes presque simultanées ...?
La piscine de fil planifie un certain nombre d'entre eux aussi rapidement que possible, jusqu'à un seuil de «minimum». Ensuite, il va tourner 4 threads par seconde, tandis que le nombre de fils est au-dessus de ce seuil, jusqu'à ce qu'il atteigne un seuil maximum, à quel point il contiendra de nouvelles demandes. Les seuils minimum et maximum sont configurables.
Quant à la DB, j'ai vu MSSQL se faire pilonner de manière assez difficile. Notre serveur principal DB a environ 24 EUS et gère à peu près toute notre entreprise, à l'exception des rapports (nous expédions des journaux de transaction à une DB déclarante afin d'éviter les problèmes de verrouillage). Je sais pour un fait qu'au moins une requête d'état non verrouillable est exécutée contre notre DB principale une moyenne de 100 fois par seconde i>. Pas de problème.
Merci d'avoir répondu à mes deux préoccupations, je ne le savais pas. Bon à savoir!
Il semble exécuter votre commande Si la base de données n'est pas utilisée, essayez un accès exclusif à celui-ci. Même dans ce cas, il existe certaines transactions internes dus à la complexité du modèle de données, envisagez de consulter le concepteur de base de données. P> SQL code> met à disposition certaines ressources requises et c'est la raison pour laquelle vous devez utiliser async code> async code). p>
Merci . La base de données est en train d'être utilisée, donc je ne peux pas faire comme vous l'avez suggéré.
en SQL à l'autre extrémité d'une écriture est un (un) disque. Vous pouvez rarement écrire plus rapidement en parallèle. En fait, en parallèle ralentit souvent en raison de la fragmentation d'index. Si vous pouvez trier les données par la clé primaire (cluster) avant le chargement. Dans une charge importante, désactivez d'autres clés, chargez des clés de reconstruction de données.
Pas vraiment sûr de ce que font dans l'Asynch, mais il ne faisait certainement pas ce que vous attendiez car il attendait lui-même. p>
@stakx sera un RDR.Close () prendre soin de cela?
Iirc rdr.close () code> aurait le même effet que rdr.dispose () code> ... mais un en utilisant le bloc code> est plus facile que d'emballage < Code> RDR.Close () Code> dans un Enfin code> (que vous devriez faire pour la sécurité des exceptions).
Ok mais à la tête de créer une nouvelle RDR chaque boucle. Quelle exception ne serait pas prise sur la capture si RDR a été réutilisé?
Vous ne pouvez pas réellement les réutiliser dans plusieurs requêtes. Pour toutes les fins pratiques, vous devez supposer que chaque appel à sqlcommand.executereader () code> retournera une nouvelle instance de sqldatreader code>. Ce n'est pas parce que vous faites référence à toutes ces instances différentes avec la même variable i> ne les utilise pas de quelque manière que ce soit.
@stakx, je dois dire que je ne vous croyais pas, mais j'ai rencontré des tests et ils ne contestent pas ce que vous avez dit et pour la lisibilité, j'aime l'utilisation de la syntaxe.
Comme nous parlions dans les commentaires, stockez ces données en mémoire et en travaillant avec elle peut y avoir une approche plus efficace. P>
Un moyen facile de le faire est de commencer avec le cadre d'entité. L'entité framework générera automatiquement les classes pour vous en fonction de votre schéma de base de données. Ensuite, vous pouvez Importer une procédure stockée qui contient votre relevé de sélection. La raison pour laquelle je suggère d'importer un PROC stocké dans EF est que cette approche est généralement plus efficace que de faire vos requêtes à Linq contre EF. P>
puis exécutez la procréation stockée et stockez les données dans une liste Ensuite, vous pouvez faire tout ce que vous voulez avec ce de toute façon, vous travaillerez avec vos données code> comme ceci ... p>
var données = db.mystoredProc (). Tolist (); Code> P>
DATA code>. Ou comme je l'ai mentionné, si vous faites beaucoup de recherches sur les touches primaires, utilisez toodictaire () code> quelque chose comme ça ... P>
var données = db.mystoredProc (). TODICTARY (K => K.MYPRIMARYKEYKEY); CODE> P>
code> en mémoire à ce stade. p>
Deux pensées - Tout d'abord, vous faites l'async, ce qui vous permet de dormir pour de nombreux articles dans la boucle. Deuxièmement, pouvez-vous réutiliser l'objet SQLCOMMAND pour toute la boucle au lieu de créer / détruire une fois?
Si vous nous en dire plus sur ce que vous essayez d'accomplir, nous pourrions potentiellement vous montrer une solution dans SQL qui exécute plusieurs ordres de grandeur plus rapidement et évite toute cette activité asynchraement / parallèle.
@ user1110790: Le code que vous avez affiché était rempli d'erreurs (et en a toujours au moins une), donc j'ai nettoyé un peu. Puis-je suggérer humblement que lorsque vous posterez, assurez-vous que votre code est correct; Sinon, vous pourriez simplement obtenir de nombreux commentaires se concentrer sur cela, au lieu de la question réelle.
Off-thème: B> Pendant que je suis d'accord avec les autres ici que vous utilisez les méthodes asynchrones dans le mauvais sens, permettez-moi d'ajouter que vous ne devez jamais interroger un
asyncrédule code> comme celui-ci: < Code> tandis que (! résultat.iscompensé) thread.sleep (...); code>. Au lieu de cela, vous devriez faire ceci:résultat.Asyncwaithandle.waitone (); code> pendant que cela bloquera également le thread d'appel, il n'a pas besoin d'interrogation; Le système d'exploitation réalisera le fil d'appel à la fin.@RoberTharvey, il s'agit donc d'une partie du service qui lit un fichier contenant des informations de compte et leur utilisation en ligne. Sur la base des données, nous calculons l'utilisation totale et ajoute ces informations à 3 tableaux à l'aide de la procédure stockée. La procédure stockée met simplement à jour un enregistrement existant ou insère un nouvel enregistrement. Les tables sont petites et contiennent uniquement 5 colonnes.
@RoberTharvey Le problème est que les fichiers sont généralement énormes, contenant près de 36 000 enregistrements sur une moyenne. Sur la base de ce que j'ai vu, le temps de traiter chaque enregistrement augmente à mesure que le nombre d'enregistrements augmente. J'apprécierais vraiment si nous pouvions trouver une autre façon de le faire.
Peut-être essayer d'utiliser un profileur. Cela peut vous donner un indice ce qui se passe.