开发者

NHibernate - Performance Tuning with Native SQL

I am trying to use NHibernate to map a graph built with the following entites to the relational database (The code is incomplete and just for demo purpose, which should look straightforward). Potentially Node and Edge classes may have subclases and there are already a string of subclasses defined inherited from Node class. For all the inheritance relationships in this model, the mapping type used is joined-subclass (table-per-subclass);

class GraphObject { ... }

class Node : GraphObject {
   List<Edge> IngoingEdges;
   List<Edge> OutgoingEdges;
}

class Edge : GraphObject {
   Node StartNode { get; set; }
   Node EndNode { get; set; }
}

For connections between nodes and edges a dual many-to-one mapping is used as follows,

many-开发者_StackOverflow社区to-one from Edge.StartNode to nodes(.OutgoingEdges); many-to-one from Edge.EndNode to nodes(.IngoingEdges)

Since there's a need to work with huge volume data in our project (millions of nodes and edges) and we would like to both keep having the benefits NHibernate provides and minimize the performance issues. Unfortunately it seems to take nearly an hour to save or load such a model. What I'm currently doing is trying to figure out a way to finish loading in one statement and see how long it takes. I made a few attempts and I used NHibernate Profiler to keep track of the SQL statements generated by the NHibernate framework when doing things like loading the entire graph from the data persistence, but so far I haven't managed to eliminate that huge amount of individual queries apparently for determining which are the start and end nodes for specific edges which look like

select ...StartNode as .., ..Id as .., ... from Link link where link.StartNode=10 (a number indicating node id)

which means I am kind of suffering from the so-called N+1 issues. So is there anyone wo has come across a similar problem and can give me some idea, either in native SQLs or improving performance for this particular case by other approaches. I would really appreciate that. Any questions regarding points unclear are also welcome.


some optimisations come to mind:

  • disable lazyloading of StartNode and EndNode (get Egde in 1 Query instead 3)
  • EagerLoad the collections of an edge (http://ayende.com/blog/4367/eagerly-loading-entity-associations-efficiently-with-nhibernate)

this would give something along the lines of

// initialize the collections efficiently
session.QueryOver<Node>()
    .Where(n => n.Id == nodeId)
    .Fetch(n => n.IngoingEdges)
    .ToFuture();

firstNode = session.QueryOver<Node>()
    .Where(n => n.Id == nodeId)
    .Fetch(n => n.OutgoingEdges)
    .ToFuture().Value;

var egdeIds = firstNode
    .SelectMany(n => n.IngoingEdges)
    .SelectMany(edge => new [] { edge.StartNode.Id, edge.EndNode.Id });

EagerLoadNode(nodeIds);

void EagerLoadNode(IEnumerable<int> nodeIds)
{
    // initialize the collections efficiently
    session.QueryOver<Node>()
        .Where(n => n.Id.IsIn(nodeIds))
        .Fetch(n => n.IngoingEdges)
        .ToFuture();

    firstNode = session.QueryOver<Node>()
        .Where(n => n.Id.IsIn(nodeIds))
        .Fetch(n => n.OutgoingEdges)
        .ToFuture();
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜