2012-03-11 1 views
3

Je rencontre un problème de temps avec une boucle de 1 million de lignes potentielles à partir d'une base de données. En gros, je place les lignes dans un DataTable et je les contourne, mais ça devient lent. Quelle est l'alternative là-bas? Je peux diviser ces rangées en morceaux de 20 000 pièces. Puis-je utiliser le traitement parallèle en C#? Fondamentalement, le code boucle à travers chaque enregistrement potentiel qui correspond à une certaine requête et essaie de comprendre si c'est une entrée légitime. C'est pourquoi chaque disque doit être visité individuellement. Un enregistrement pour un objet peut atteindre 10 millions de lignes. Les approches semblent être un traitement parallèle sur plusieurs ordinateurs ou PP dans une seule machine avec plusieurs cœurs, ou une sorte de structure de données/changement d'approche?En boucle sur un grand nombre de lignes

Des opinions, des pensées et des suppositions sont-elles utiles pour rendre cela rapide et raisonnable?

Répondre

2

Tout d'abord: Ne pas utiliser DataTable pour des opérations comme celles-ci:

  • il est lent
  • il y a consommer trop de mémoire
  • et vous avez besoin d'attendre longtemps avant de pouvoir réellement commencer traitement des données
    • Pendant ce temps, des cœurs supplémentaires ne font rien, puisque la lecture des données dans un DataTable n'est pas parralisée.
    • En outre, lors de la lecture des données, le processeur est généralement presque sous-utilisé, car le réseau ou un autre retard d'E/S est souvent le facteur principal.

Encore une fois: Ne pas utiliser DataTable pour des opérations comme celles-ci.

Utilisez plutôt le DataReader. Cela vous permet de commencer immédiatement à consommer/traiter les données, au lieu d'attendre qu'elles soient chargées. La version la plus simple serait (exemple pour MS SQL Server):

var command = new SqlCommand() 
{ 
    CommandText = "SELECT * FROM Table"; 
    Connection = new SqlConnection("InsertConnectionString"); 
}; 

using(var reader = command.ExecuteReader()) 
{ 
    while(reader.Read()) 
    { 
    var values = new object[reader.FieldCount]; 
    reader.GetValues(values); 

    // process values of row 
    } 
} 

Le lecteur sera bloqué pendant l'exécution de votre code de traitement, ce qui signifie des lignes ne sont plus lues à partir du DB.
Si le code de traitement est lourd, cela peut valoir la peine d'utiliser la bibliothèque Task pour créer des tâches qui effectuent la vérification, ce qui vous permettrait d'utiliser plusieurs cœurs. Cependant, il y a une surcharge de la création d'un Task, si l'on Task ne contient pas assez de « travail » que vous pouvez traiter en lots deux rangées ensemble:

public void ReadData() 
{ 
    var taskList = new List<Task<SomeResultType>>(); 

    var command = new SqlCommand() 
    { 
    CommandText = "SELECT * FROM Table"; 
    Connection = new SqlConnection("InsertConnectionString"); 
    }; 
    using(var reader = command.ExecuteReader()) 
    { 
    var valueList = new List<object[]>(100); 
    while(reader.Read()) 
    { 
     var values = new object[reader.FieldCount]; 
     reader.GetValues(values); 

     valueList.Add(values); 

     if(valueList.Count == 100) 
     { 
     var localValueList = valueList.ToList(); 
     valueList.Clear(); 

     taskList.Add(Task<SomeResultType>.Factory.StartNew(() => Process(localValueList)); 
     } 
    } 
    if(valueList.Count > 0) 
     taskList.Add(Task<SomeResultType>.Factory.StartNew(() => Process(valueList)); 
    } 

    // this line completes when all tasks are done 
    Task.WaitAll(taskList.ToArray()); 
} 

public SomeResultType Process(List<object[]> valueList) 
{ 
    foreach(var vals in valueList) 
    { 
    // put your processing code here, be sure to synchronize your actions properly 
    } 
} 
  • La taille du lot (actuellement 100) dépend de la réelle le traitement est en cours et pourrait devoir être ajusté.
  • Synchroniser tient ses propres défis, vous devez être très prudent sur les ressources partagées
+0

Bonne explication Mais j'ai quelques questions: 1- ce qui serait mieux, la solution que vous aviez suggéré ou faisant plusieurs tâches, chacun d'eux utilise SQLReader pour lire une plage de lignes de la base de données (par ex.basé sur la pagination) en particulier dans le cas de traitement de résultat n'est pas très lourd. 2-Y at-il une différence significative de performance entre Task BackgroundWork? –

0

je suggère en parallèle en boucle avec une machine à double noyaux et aussi essayer d'utiliser pour chaque boucle avec des listes génériques, je pense que cela pourrait rendre votre processus plus rapide.

Questions connexes