2010-08-11 5 views
1

J'ai plusieurs fichiers XML et chaque fichier contient des données d'objets racines que j'analyse en utilisant Linq en XML, puis crée des objets racine réels que je conserve en utilisant NHibernate et le référentiel d'architecture pointu. J'ai commencé à optimiser l'insertion de données et à ajouter 30000 objets en environ 1 heure et 40 minutes à la base de données. Cependant, c'est encore trop lent.Performances d'insertion/recherche NHibernate

Je pense qu'un col de bouteille est la recherche d'objets dans la base de données qui nécessite des E/S. Les objets doivent être recherchés pour être réutilisés.

L'objet racine a plusieurs auteurs:

public virtual IList<Author> Authors { get; set; } 

Les auteurs ont cette structure:

public class Author : Entity 
    { 
public virtual Initials Initials { get; set; } 
     public virtual ForeName ForeName { get; set; } 
     public virtual LastName LastName { get; set; } 
    } 

J'ai atteint une grande vitesse par l'aide d'un typé Id (quelque chose que je ne serais pas normalement faire):

public class LastName : EntityWithTypedId<string>, IHasAssignedId<string> 
    { 
     public LastName() 
     { 
     } 
     public LastName(string Id) 
     { 
      SetAssignedIdTo(Id); 
     } 
     public virtual void SetAssignedIdTo(string assignedId) 
     { 
      Id = assignedId; 
     } 
    } 

Ce que je recherche (et potentiellement créer) comme ceci:

LastName LastName = LastNameRepository.Get(TLastName); 

         if (LastName == null) 
         { 
          LastName = LastNameRepository.Save(new LastName(TLastName)); 
          LastNameRepository.DbContext.CommitChanges(); 
         } 
         Author.LastName = LastName; 

Je suis à la recherche des auteurs comme ceci:

propertyValues = new Dictionary<string, object>();    
propertyValues.Add("Initials", Author.Initials); 
        propertyValues.Add("ForeName", Author.ForeName); 
        propertyValues.Add("LastName", Author.LastName); 
        Author TAuthor = AuthorRepository.FindOne(propertyValues); 

        if (TAuthor == null) 
        { 
         AuthorRepository.SaveOrUpdate(Author); 
         AuthorRepository.DbContext.CommitChanges(); 
         Root.Authors.Add(Author); 
        } 
        else 
        { 
         Root.Authors.Add(TAuthor); 
        } 

Puis-je améliorer cela? Devrais-je utiliser des procédures stockées/HQL/pure SQL/ICriteria à la place pour effectuer la recherche? Puis-je utiliser une forme de cache pour accélérer la recherche et réduire les entrées/sorties? Le CommitChanges semble être nécessaire ou devrais-je envelopper tout dans une transaction?

Je vide déjà ma session etc. tous les 10 objets racine.

Tous les commentaires seraient les bienvenus. Merci d'avance.

Meilleurs voeux,

Christian

Répondre

1

En toute honnêteté, je dirais que vous ne devriez pas être même en utilisant SA/NHibernate pour quelque chose comme ça. C'est une importation de données en masse à partir de XML - un outil ETL comme SSIS serait un meilleur choix. Même un processus à manivelle sur le serveur DB fonctionnerait mieux - étape 1, charger du XML dans une table, étape 2, faire l'UPSERT. Incidemment, SQL 2008 a introduit la commande MERGE pour les opérations UPSERT, qui pourraient être utiles.

Je suis également d'accord avec le commentaire de Dan - est-il vraiment nécessaire de traiter les initiales, le prénom et le nom de famille comme des entités distinctes? Les traiter comme de simples cordes améliorerait les performances. Qu'est-ce qui, dans votre modèle de domaine, spécifie qu'ils sont des entités à part entière?

Si vous devez vraiment continuer à utiliser SA/NHibernate, ont une lecture de ceci: http://www.lostechies.com/blogs/jimmy_bogard/archive/2010/06/24/bulk-processing-with-nhibernate.aspx

La suggestion dans le blog de Jimmy à propos de SELECTs devrait aider batching beaucoup.Si vous envisagez de traiter un lot de 250 enregistrements à la fois, faites tous les SELECT en une seule commande NH, traitez toutes les données, puis faites toutes les mises à jour en un seul lot (je crois que vous utilisez EntityWithTypedId et adonet.batch_size le paramétrage de la config aidera à atteindre)

Enfin - en ce qui concerne l'instruction "que j'utilise en utilisant Linq to XML" - est-ce vraiment la meilleure façon de le faire? Je suppose que c'est peut-être dû à la taille de votre fichier d'entrée, mais êtes-vous conscient de l'approche consistant à désérialiser simplement le fichier XML dans un graphe d'objets? SO ne me laissera pas publier le lien vers une page décrivant cela, parce que je n'ai pas encore gagné assez de réputation - mais si vous voulez en lire plus, Google "ne pas analyser ce xml" et le premier article expliquera il.

Espérons que cela aide. Jon

+0

J'ai fini par utiliser xsd2code. assurez-vous que, si votre fichier XML contient des informations dtd, utilisez: BlaClassBlaClass = ((BlaClass) (serializer.Deserialize (System.Xml.XmlReader.Create (nouveau XmlTextReader (chemin de fichier), new System.Xml.XmlReaderSettings() {ProhibitDtd = false })))); – cs0815

0

La première chose que je ferais est de simplifier l'entité des auteurs que je ne pense pas que vous avez besoin Initiales, PRENOM et objets LastName comme des entités distinctes. Je pense en utilisant des chaînes simples serait plus efficace:

public class Author : Entity 
{ 
    public virtual string Initials { get; set; } 
    public virtual string ForeName { get; set; } 
    public virtual string LastName { get; set; } 
} 
+0

merci - c'est un reste de quand j'ai essayé d'utiliser lookuptables pour les noms - où j'ai eu un int plutôt que la chaîne réelle comme clé primaire. J'ai alors réalisé que la recherche de l'int prend beaucoup de temps - alors j'ai payé pour l'efficacité d'insertion avec de la mémoire. – cs0815