问题
I use Fluent NHibernate to import data into a SQL Server Database.
I used Automapping on my entities (which worked perfectly)
Fluently.Configure(nhibernateConfig_)
.Mappings(map_ => map_.AutoMappings.Add(AutoMap
.AssemblyOf<EntityMapping>(new AutomapConfiguration())
.UseOverridesFromAssemblyOf<EntityMapping>()
.Conventions.AddFromAssemblyOf<EntityMapping>()));
and use QueryOver to get data from the database
public AttributeTranslation GetOrCreateAttributeTranslation(Attribute attribute_, string language_)
{
AttributeTranslation translation = _session
.QueryOver<AttributeTranslation>()
.And(trans_ => trans_.Attribute == attribute_)
.And(trans_ => trans_.Language == language_)
.SingleOrDefault();
if (translation == null) {
translation = new AttributeTranslation() {
Language = language_,
Attribute = attribute_
};
Save(translation);
}
return translation;
}
Unfortunately, as I process my input, each query to read and each committed transaction takes longer and longer as the session progresses.
// Surrounded in several for loops that process the input
using (ITransaction transaction = _repo.BeginTransaction()) {
Stopwatch transGetWatch = Stopwatch.StartNew();
AttributeTranslation translation = _repo.GetOrCreateAttributeTranslation(attribute, lang.Key);
transGetWatch.Stop();
translation.DisplayName = value;
_repo.Save(translation);
Stopwatch commitWatch = Stopwatch.StartNew();
transaction.Commit();
commitWatch.Stop();
Console.Write("\rGetTrans[{0}] Commit[{1}] ",
transGetWatch.ElapsedMilliseconds,
commitWatch.ElapsedMilliseconds);
}
I tried switching to HiLo for ID generation, to no avail.
public class AttributeTranslationMapOverride : IAutoMappingOverride<AttributeTranslation>
{
public void Override(AutoMapping<AttributeTranslation> map_)
{
map_.Id(attr_ => attr_.Id).GeneratedBy.HiLo("attributeTranslationMaxLo");
}
}
The queries and commits start out at around 3-4 ms per call and end up somewhere around 80 to 100 at the end.
I don't necessarily need to decrease the overall performance, I just want it stabilized at the initial 3 to 4 ms per call.
What might be going on here?
回答1:
As an ORM, NHibernate is not well suited for batch work, thus the troubles you encounter. But it has some features to help using it for batch work anyway.
As suspected by David Osborne, the session first level cache may grow too big when using an ISession for batch work such as your data import.
There are a number of solutions you can choose:
- Clear the session (
ISession.Clear), as suggested by David Osborne and recommended by reference documentation. If each row of data never references some data in common with previous rows, clearing after every row will not hurt. Doing it every 50 or 100 rows should be enough for avoiding a too big performance impact of growing session first level cache. Adjust according to your findings. - Sessions are cheap. You may, instead of clearing, discard it and use a new one. (But avoid doing that at each row, it is not completely free.)
- Consider using a stateless session (
ISessionFactory.OpenStatelessSession()). They can be better suited for some batch processing.
Notes:
Your current implementation seems to save only one entity per transaction. If you can save a bunch of entities of the same classes instead, you may benefit from batching (with SQL-Server at least) and improve the performances. It would also reduce the number of connection releases/re-acquiring, since by default the SQL connection is released after each transaction.
来源:https://stackoverflow.com/questions/43934112/queries-and-commits-take-longer-as-the-session-lives-on