3. 1 Objective:
To study the tips/tricks/Guidelines related to performance tuning for NHibernate based application.
2 Problem definition:
Any fool can start a software to build, but to make it live on client’s production environment as a living
legend is not easy task, it requires high end skills in Software Architecture. Performance is always a major
concerns, which contribute to failures of software projects.
OR Mapping is generally slower then direct ADO .Net connection. That is true for both nHibernate and
LINQ2SQL. Applications which have a lot of data centric operations are not suited for NHibernate in
general for OR Mapping. However application which have application centric operations or calculation
going on are best suited for NHibernate or OR Mapping.
So if you are making a Enterprise CRM Product, then NHibernate(OR mapping) is not a good choice
because of inherited performance issues. ( Because of heavy data centric operations)
If you are making a multi player online game or simulator, then NHibernate is a good choice because a few
data operations are required and more processing is required at application logic.
So think twice before starting a project and make good choice at the start of application and rest in peace
rest of your life….:)
Bad Architecture decisions cause trouble for whole development team and project.
3 Performance Tuning tips for NHibernate:
After some googleing and reading online documentation, I have finalized, following list of 21 points
regarding performance tuning for a NHibernate application.
3.1 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)
{
4. 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();
}
3.2 Fetching strategy is key to performance tuning in Nhibernate project. A fetching strategy is the
strategy NHibernate will use for retrieving associated objects if the application needs to
navigate the association.
Two things are important: when is the association fetched, and how is it fetched (what SQL is used).
Clearly understand and use following:
Join fetching
Select fetching
Subselect fetching
"Extra-lazy" collection fetching
Batch fetching
Also follow:
Immediate fetching
Lazy collection fetching
Proxy fetching
5. Best tip is to analyze the trade off b/w eager loading vs lazy loading. It varies case of case for
implementation.
3.3 Make read-only classes immutable.
3.4 Multi query is executed by concatenating the queries and sending the query to the database as a
single string. This means that the database should support returning several result sets in a
single query. At the moment this functionality is only enabled for Microsoft SQL Server and
SQLite.
IMultiQuery multiQuery = s.CreateMultiQuery()
.Add(s.CreateQuery("from Item i where i.Id > ?")
.SetInt32(0, 50).SetFirstResult(10))
.Add(s.CreateQuery("select count(*) from Item i where i.Id > ?")
.SetInt32(0, 50));
IList results = multiQuery.List();
IList items = (IList)results[0];
long count = (long)((IList)results[1])[0];
3.5 Multi Criteria like Multi query allows to perform several criteria queries in a single round trip.
IMultiCriteria multiCrit = s.CreateMultiCriteria()
.Add(s.CreateCriteria(typeof(Item))
.Add(Expression.Gt("Id", 50))
.SetFirstResult(10))
.Add(s.CreateCriteria(typeof(Item))
.Add(Expression.Gt("Id", 50))
.SetProject(Projections.RowCount()));
IList results = multiCrit.List();
IList items = (IList)results[0];
long count = (long)((IList)results[1])[0];
3.6 NHibernate 1.2 supports batching SQL update commands (INSERT, UPDATE, DELETE) for
SQL Server and .NET Framework 2.0 or above.Update batching is enabled by setting
hibernate.adonet.batch_size to a non-zero value.
3.7 Most Important tip is to avoid looping b/w object collections , when conditional data fetching /
manipulation is required. Looping b/w collection of large size can really hurt the performance.
Use HQL to transform your conditions to Criterias that can really save you processing time.
Lets take example of getting Voucher entities having voucher details collection with some
conditions. So don’t traverse the collection of vouchers and voucher details, use HQL to fetch
the required entities.
System.Collections.IList list = null;
NHibernate.ICriteria crit =
Common.Common.Instance.GetSession.CreateCriteria(typeof(VoucherDetail),
"VoucherDetail");
crit.CreateCriteria("VoucherHead" ,"VH");
crit.Add(NHibernate.Criterion.Expression.Eq("Account", objacc));
6. crit.Add(NHibernate.Criterion.Expression.Eq("VH.SupInvNo", InvNo));
crit.Add(NHibernate.Criterion.Expression.Eq("SrNo", Convert.ToInt16(0)));
crit.Add(NHibernate.Criterion.Expression.Not(NHibernate.Criterion.Expression.Eq("VH.Vouc
herID", ID)));
crit.SetFetchMode("VoucherDetail", NHibernate.FetchMode.Eager);
crit.SetFetchMode("VoucherHead", NHibernate.FetchMode.Eager);
setFinancialYearCriteria(crit, typeof(VoucherDetail));
list = crit.List();
3.8 Use SQL Profiler and NH Profiler to view the queries generated by OR Mapping.
3.9 Use query plan to analyze the queries sent by nhibernate.
3.10 Create good indexes on tables to get better results.
3.11 Use “Database Engine Tuning Advisor” to get feedback from SQL Server regarding
optimization.
3.12 Best practices for DB normalization can really help to solve the slow down of application.
Bad DB design really kills NHibernate speed.
3.13 Using value type objects for your properties and components as opposed to reference types,
when you have a choice, this will lower memory footprint.
3.14 Consider your logging strategy for production (less verbose) and usually disable show_sql in
production.
3.15 When expensive data queries are envolved or there are limitations of OR Mapping coming up,
it is better to use native SQL queries in NHibernate. That increases the performance of
application.
3.16 In case of extreme database centric operations, store procedures can also speed up the
performance.
string sql = "exec GetJobExpenseAccounts ";
NHibernate.ISQLQuery qry= Common.Common.Instance.GetSession.CreateSQLQuery(sql);
3.17 Avoid composite keys (exception legacy databases). Composite keys are problematic for
database design as well as OR/M therefore avoid them. reference
3.18 Remove any columns and properties that you will never or should never need or use.
3.19 Have a good strategy for flushing and transaction management. Remember that if you commit
a transaction that you do not need to flush the object. reference
3.20 Enable 2nd level cache and have a strategy for using it for some of your classes. For details
view manual here.
3.21 Read Collection optimization tips in NHibernate documentation. For details view manual here.
References:
http://scottwhite.blogspot.com/2009/04/nhibernate-performance-tuning.html
http://www.codeproject.com/KB/database/NHibernate_Perf.aspx
http://www.codeproject.com/KB/database/NHibernate_Perf2.aspx
http://www.iamnotmyself.com/2008/07/02/NHibernateTestingThePerformanceUrbanLegend.aspx
http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx
http://www.objectreference.net/post/NHibernate-and-Execution-Plans.aspx
http://devlicio.us/blogs/billy_mccafferty/archive/2007/03/03/nhibernate-performance-tuning.aspx