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)
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.
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.
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.
If you're not already using lazy loading (appropriately), start. Fetching collections when you don't need them is a waste of everything.
Chapter 15 of the documentation describes this and other ways to improve performance.
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 14.3 Chapter 14. Improving performance for details
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 :)
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.]
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();
}
Avoid and/or minimize the Select N + 1 problem by recognizing when to switch from lazy loading to eager fetching for slow performing queries.
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.
What lotsoffreetime said.
Read Chapter 15 of the documentation, "Improving Performance". https://www.hibernate.org/hib_docs/nhibernate/1.2/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.