1
votes

Comment récupérer, traiter et sauvegarder efficacement un énorme jeu d'enregistrements dans C #?

J'essaie de réaliser les choses ci-dessous:

  • récupérez les données de SQL DB.
  • Passer les données à la méthode PerformStuff qui a une méthode tierce MethodforResponse (Il vérifie l'entrée et fournit une réponse)

  • Enregistrez la réponse (xml) dans SQL DB.

ci-dessous est l'exemple de code.performance sage ce n'est pas bon, s'il y a 1000.000 enregistrements dans DB c'est très lent.

il vaut mieux le faire? conseils pour l'améliorer.

s'il vous plaît aider.

using thirdpartylib;
 public class Program
    {

        static void Main(string[] args)
        {
            var response = PerformStuff();
            Save(response);


        }

        public class TestRequest
        {
            public int col1 { get; set; }
            public bool col2 { get; set; }
            public string col3 { get; set; }
            public bool col4 { get; set; }

            public string col5 { get; set; }
            public bool col6 { get; set; }
            public string col7 { get; set; }

        }
        public class TestResponse
        {
            public int col1 { get; set; }
            public string col2 { get; set; }
            public string col3 { get; set; }
            public int col4 { get; set; }

        }
        public TestRequest GetDataId(int id)
        {
            TestRequest testReq = null;
            try
            {
                SqlCommand cmd = DB.GetSqlCommand("proc_name");
                cmd.AddInSqlParam("@Id", SqlDbType.Int, id);
                SqlDataReader dr = new SqlDataReader(DB.GetDataReader(cmd));
                while (dr.Read())
                {
                    testReq = new TestRequest();

                    testReq.col1 = dr.GetInt32("col1");
                    testReq.col2 = dr.GetBoolean("col2");
                    testReq.col3 = dr.GetString("col3");
                    testReq.col4 = dr.GetBoolean("col4");
                    testReq.col5 = dr.GetString("col5");
                    testReq.col6 = dr.GetBoolean("col6");
                    testReq.col7 = dr.GetString("col7");



                }
                dr.Close();
            }

            catch (Exception ex)
            {
                throw;
            }
            return testReq;

        }
        public static TestResponse PerformStuff()
        {
            var response = new TestResponse();
            //give ids in list
            var ids = thirdpartylib.Methodforid()


            foreach (int id in ids)
            {

                var request = GetDataId(id);


                var output = thirdpartylib.MethodforResponse(request);

                foreach (var data in output.Elements())
                {
                    response.col4 = Convert.ToInt32(data.Id().Class());
                    response.col2 = data.Id().Name().ToString();

                }
            }
            //request details
            response.col1 = request.col1;
            response.col2 = request.col2;
            response.col3 = request.col3;

            return response;
        }

        public static void Save(TestResponse response)
        {

            var Sb = new StringBuilder();
            try
            {
                Sb.Append("<ROOT>");
                Sb.Append("<id");
                Sb.Append(" col1='" + response.col1 + "'");
                Sb.Append(" col2='" + response.col2 + "'");
                Sb.Append(" col3='" + response.col3 + "'");
                Sb.Append(" col4='" + response.col4 + "'");

                Sb.Append("></Id>");
                Sb.Append("</ROOT>");
                var cmd = DB.GetSqlCommand("saveproc");
                cmd.AddInSqlParam("@Data", SqlDbType.VarChar, Sb.ToString());
                DB.ExecuteNoQuery(cmd);

            }
            catch (Exception ex)
            {

                throw;
            }
        }

    }

Merci!


7 commentaires

Est-il nécessaire de sauvegarder XML dans DB?


@DarjanBogdan, en fait ce n'est pas le cas.


Je suggérerais de diviser les disques en petits morceaux. La procédure stockée ne doit émettre qu'un sous-ensemble et proposer au client d'implémenter une sorte de pagination. Par exemple, le client peut demander des enregistrements d'une plage pafticulaire (0 à 500). Dans un deuxième jeu de résultats, la procédure peut indiquer à l'appelant le nombre d'enregistrements restants.


La seule façon dont je peux penser à accélérer les choses est de savoir si la fonction tierce accepterait plusieurs objets de demande par invocation. Sinon, vous êtes coincé avec une opération séquentielle. Vous pouvez essayer de générer plusieurs threads de travail afin que plusieurs enregistrements puissent être traités simultanément.


À la lecture de votre exemple de code, il semble que vous lisiez de nombreux enregistrements et n'en écriviez qu'un. Votre méthode PerformStuff est appelée une fois et renvoie une seule méthode TestResponse. Est-ce vrai?


@Leng .. oui il accepte aussi la liste


@jones .. oui c'est vrai


4 Réponses :


0
votes

Votre question est très large et la méthode PerformStuff () sera fondamentalement lente car elle prend O (n) * db_lookup_time avant une autre itération de la sortie. Donc, pour moi, il me semble que vous abordez ce problème dans le mauvais sens.

Les langages de requête de base de données sont conçus pour optimiser la traversée des données. Donc, itérer par identifiant, puis vérifier les valeurs, permet de contourner ce temps de recherche le plus lent possible.

Au lieu de cela , tirez parti du puissant langage de requête de SQL et utilisez des clauses telles que où id <10 et valeur> 100 , car vous voulez en fin de compte limiter la taille des données set devait être traité par C #.

  1. Lisez uniquement les plus petites données dont vous avez besoin dans la base de données
  2. Traitez ces données comme une unité, le parallélisme pourrait aider.
  3. Réécrire les modifications dans une seule connexion de base de données.

J'espère que cela vous mettra dans la bonne direction.


0 commentaires

0
votes

Je pense que la racine de votre problème est que vous obtenez et insérez des données enregistrement par enregistrement. Il n'y a aucun moyen possible de l'optimiser. Vous devez changer l'approche en général.

Vous devriez penser à une solution qui: 1. Obtient toutes les données en une seule commande dans la base de données. 2. Traitez-le. 3. Enregistrez-le dans la base de données en une seule commande, en utilisant une technique telle que BULK INSERT . Veuillez noter que BULK INSERT a certaines limitations, alors lisez attentivement la documentation.


0 commentaires

0
votes

D'après votre commentaire, il y a plusieurs choses que vous pouvez améliorer dans votre solution, de la consommation de mémoire à l'utilisation du processeur.

  1. Profitez de pagination au niveau de la base de données . Ne récupérez pas tous les enregistrements en même temps, pour éviter les fuites de mémoire et / ou la consommation de mémoire élevée dans le cas de plus d'un million d'enregistrements, prenez plutôt morceau par morceau et faites ce que vous devez en faire.

  2. Comme vous n'avez pas besoin d'enregistrer du XML dans une base de données, vous pouvez choisir d'enregistrer la réponse dans le fichier. L'enregistrement de XML dans un fichier vous donne la possibilité de diffusez les données sur votre disque local.

  3. Au lieu d'assembler XML par vous-même, utilisez XmlSerializer pour faire ce travail pour vous. XmlSerializer fonctionne parfaitement avec XmlWriter < / a> qui à la fin peut fonctionner avec n'importe quel flux comprenant FileStream . Il existe un thread à ce sujet, que vous pouvez prendre comme exemple.

Pour conclure, la méthode PerformStuff ne sera pas seulement plus rapide, mais elle demandera beaucoup moins de ressources (mémoire, CPU) et le plus important, vous pourrez facilement limiter la consommation de ressources de votre programme (en modifiant la taille de la page de la base de données).


0 commentaires

0
votes

Observation: votre exigence semble correspondre au modèle map / Reduce.

Si les valeurs de votre collection ids renvoyées par thirdpartylib.Methodforid () sont raisonnablement dense, et le nombre de lignes dans la table derrière votre procédure stockée proc_name a presque le même nombre d'éléments dans la collection ids , vous pouvez récupérer tous les enregistrements dont vous avez besoin avec une seule requête SQL (et un jeu de résultats à plusieurs lignes) plutôt que de les récupérer une par une. Cela pourrait ressembler à ceci:

public static TestResponse PerformStuff()
{
    var response = new TestResponse();

    var idHash = new HashSet<int> (thirdpartylib.Methodforid());

    SqlCommand cmd = DB.GetSqlCommand("proc_name_for_all_ids");
    using (SqlDataReader dr = new SqlDataReader(DB.GetDataReader(cmd)) { 
        while (dr.Read()) {
            var id = dr.GetInt32("id");
            if (idHash.Contains(id)) {
                testReq = new TestRequest();

                testReq.col1 = dr.GetInt32("col1");
                testReq.col2 = dr.GetBoolean("col2");
                testReq.col3 = dr.GetString("col3");
                testReq.col4 = dr.GetBoolean("col4");
                testReq.col5 = dr.GetString("col5");
                testReq.col6 = dr.GetBoolean("col6");
                testReq.col7 = dr.GetString("col7");

                var output = thirdpartylib.MethodforResponse(request);
                foreach (var data in output.Elements())  {
                    response.col4 = Convert.ToInt32(data.Id().Class());
                    response.col2 = data.Id().Name().ToString();
                }
            } /* end if hash.Contains(id) */  
        }  /* end while dr.Read() */
    } /* end using() */
    return response;
}

Pourquoi cela pourrait-il être plus rapide? Il effectue beaucoup moins de requêtes de base de données et, à la place, les flux dans les multiples lignes de données à traiter. Ce sera beaucoup plus efficace que votre exemple.

Pourquoi cela pourrait-il ne pas fonctionner?

  1. si les valeurs id doivent être traitées dans le même ordre que celui produit par thirdpartylib.Methodforid () cela ne fonctionnera pas.
  2. s'il n'y a aucun moyen de récupérer toutes les lignes, c'est-à-dire qu'aucune procédure stockée proc_name_for_all_ids n'est disponible, vous ne pourrez pas diffuser les lignes.


0 commentaires