What is the best way to improve performance of NHibernate?

.NetPerformanceNhibernateOrmData Access-Layer

.Net Problem Overview


I have an application that uses NHibernate as its ORM and sometimes it experiences performance issues due to how the data is being accessed by it. What kind of things can be done to improve the performance of NHibernate? (Please limit to one recommendation per answer)

.Net Solutions


Solution 1 - .Net

The first and most dramatic performance problem that you can run into with NHibernate is if you are creating a new session factory for every session you create. Only one session factory instance should be created for each application execution and all sessions should be created by that factory.

Along those lines, you should continue using the same session as long as it makes sense. This will vary by application, but for most web applications, a single session per request is recommended. If you throw away your session frequently, you aren't gaining the benefits of its cache. Intelligently using the session cache can change a routine with a linear (or worse) number of queries to a constant number without much work.

Equally important is that you want to make sure that you are lazy loading your object references. If you are not, entire object graphs could be loaded for even the most simple queries. There are only certain reasons not to do this, but it is always better to start with lazy loading and switch back as needed.

That brings us to eager fetching, the opposite of lazy loading. While traversing object hierarchies or looping through collections, it can be easy to lose track of how many queries you are making and you end up with an exponential number of queries. Eager fetching can be done on a per query basis with a FETCH JOIN. In rare circumstances, such as if there is a particular pair of tables you always fetch join, consider turning off lazy loading for that relationship.

As always, SQL Profiler is a great way to find queries that are running slow or being made repeatedly. At my last job we had a development feature that counted queries per page request as well. A high number of queries for a routine is the most obvious indicator that your routine is not working well with NHibernate. If the number of queries per routine or request looks good, you are probably down to database tuning; making sure you have enough memory to store execution plans and data in the cache, correctly indexing your data, etc.

One tricky little problem we ran into was with SetParameterList(). The function allows you to easily pass a list of parameters to a query. NHibernate implemented this by creating one parameter for each item passed in. This results in a different query plan for every number of parameters. Our execution plans were almost always getting released from the cache. Also, numerous parameters can significantly slow down a query. We did a custom hack of NHibernate to send the items as a delimited list in a single parameter. The list was separated in SQL Server by a table value function that our hack automatically inserted into the IN clause of the query. There could be other land mines like this depending on your application. SQL Profiler is the best way to find them.

Solution 2 - .Net

NHibernate's SessionFactory is an expensive operation so a good strategy is to creates a Singleton which ensures that there is only ONE instance of SessionFactory in memory:

   public class NHibernateSessionManager
    {
        private readonly ISessionFactory _sessionFactory;

        public static readonly NHibernateSessionManager Instance = new NHibernateSessionManager();

        private NHibernateSessionManager()
        {
            if (_sessionFactory == null)
            {
                System.Diagnostics.Debug.WriteLine("Factory was null - creating one");
                _sessionFactory = (new Configuration().Configure().BuildSessionFactory());
            }
        }

        public ISession GetSession()
        {
            return _sessionFactory.OpenSession();
        }

        public void Initialize()
        {
            ISession disposeMe = Instance.GetSession();
        }
    }

Then in your Global.Asax Application_Startup, you can initialize it:

protected void Application_Start()
{
    NHibernateSessionManager.Instance.Initialize();
}

Solution 3 - .Net

Avoid and/or minimize the Select N + 1 problem by recognizing when to switch from lazy loading to eager fetching for slow performing queries.

Solution 4 - .Net

No a recommendation but a tool to help you : NH Prof ( http://nhprof.com/ ) seems to be promising, it can evaluate your use of the ORM framework. It can be a good starting point for your tunning of NHibernate.

Solution 5 - .Net

Without any specifics about the kinds of performance issues you're seeing, I can only offer a generalization: In my experience, most database query performance issues arise from lack of proper indices. So my suggestion for a first action would be to check your query plans for non-indexed queries.

Solution 6 - .Net

NHibernate generates pretty fast SQL right out of the box. I've been using it for a year, and have yet to have to write bare SQL with it. All of my performance problems have been from Normalization and lack of indexes.

The easiest fix is to examine the execution plans of your queries and create proper indexes, especially on your foreign key columns. If you are using Microsoft SQL Server, the "Database Engine Tuning Advisor" helps out a lot with this.

Solution 7 - .Net

"One recommendation per answer" only? Then I would go for this one:

Avoid join duplicates (AKA cartesian products) due to joins along two or more parallel to-many associations; use Exists-subqueries, MultiQueries or FetchMode "subselect" instead.

Taken from: Hibernate Performance Tuning Tips

Solution 8 - .Net

If you're not already using lazy loading (appropriately), start. Fetching collections when you don't need them is a waste of everything.

Chapter Improving performance describes this and other ways to improve performance.

Solution 9 - .Net

Profiling is the first step - even simple timed unit tests - to find out where the greatest gains can be made

For collections consider setting the batch size to reduce the number of select statements issued - see section Improving performance for details

Solution 10 - .Net

I am only allowed to limit my answer to one option? In that case I would select that you implement the second-level cache mechanism of NHibernate.

This way, for each object in your mapping file you are able to define the cache-strategy. The secondlevel cache will keep already retrieved objects in memory and therefore not make another roundtrip to the database. This is a huge performance booster.

Your goal is to define the objects that are constantly accessed by your application. Among those will be general settings and the like.

There is plenty of information to be found for nhibernate second level cache and how to implement it.

Good luck :)

Solution 11 - .Net

Caching, Caching, Caching -- Are you using your first level caching correctly [closing sessions prematurely, or using StatelessSession to bypass first level caching]? Do you need to set up a simple second level cache for values that change infrequently? Can you cache query result sets to speed up queries that change infrequently?

[Also configuration -- can you set items as immutable? Can you restructure queries to bring back only the information you need and transform them into the original entity? Will Batman be able to stop the Riddler before he gets to the dam? ... oh, sorry got carried away.]

Solution 12 - .Net

What lotsoffreetime said.

Read Chapter 19 of the documentation, "Improving Performance".
NHibernate: http://nhibernate.info/doc/nhibernate-reference/performance.html<br> Hibernate: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html

Use SQL Profiler (or equivalent for the database you're using) to locate long-running queries. Optimize those queries with appropriate indexes.

For database calls used on nearly every single page of an application, use CreateMultiQuery to return multiple resultsets from a single database query.

And of course, cache. The OutputCache directive for pages/controls. NHibernate caching for data.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRayView Question on Stackoverflow
Solution 1 - .NetChuckView Answer on Stackoverflow
Solution 2 - .NetDavid PView Answer on Stackoverflow
Solution 3 - .NetRayView Answer on Stackoverflow
Solution 4 - .NetMatthieuGDView Answer on Stackoverflow
Solution 5 - .NetMike MonetteView Answer on Stackoverflow
Solution 6 - .NetEric LathropView Answer on Stackoverflow
Solution 7 - .Netgnome26View Answer on Stackoverflow
Solution 8 - .NetlotsoffreetimeView Answer on Stackoverflow
Solution 9 - .NetRichardView Answer on Stackoverflow
Solution 10 - .NetHaceView Answer on Stackoverflow
Solution 11 - .NetWatsonView Answer on Stackoverflow
Solution 12 - .NetAxlView Answer on Stackoverflow