随着会话的持续,查询和提交需要更长的时间

Queries and Commits take longer as the session lives on

我使用 Fluent NHibernate 将数据导入 SQL 服务器数据库。

我在我的实体上使用了自动映射(效果很好)

Fluently.Configure(nhibernateConfig_)
    .Mappings(map_ => map_.AutoMappings.Add(AutoMap
    .AssemblyOf<EntityMapping>(new AutomapConfiguration())
    .UseOverridesFromAssemblyOf<EntityMapping>()
    .Conventions.AddFromAssemblyOf<EntityMapping>()));

并使用 QueryOver 从数据库中获取数据

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;
}

不幸的是,当我处理我的输入时,随着会话的进行,每个要读取的查询和每个已提交的事务花费的时间越来越长。

// 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);
}

我尝试切换到 HiLo 来生成 ID,但无济于事。

public class AttributeTranslationMapOverride : IAutoMappingOverride<AttributeTranslation>
{
    public void Override(AutoMapping<AttributeTranslation> map_)
    {
        map_.Id(attr_ => attr_.Id).GeneratedBy.HiLo("attributeTranslationMaxLo");
    }
}

查询和提交开始时每次调用大约 3-4 毫秒,结束时大约 80 到 100 毫秒。

我不一定需要降低整体性能,我只是希望它稳定在每次调用的初始 3 到 4 毫秒。

这里可能发生了什么?

作为ORM,NHibernate不适合批处理,所以遇到的麻烦。但无论如何它都有一些功能可以帮助将其用于批处理工作。

suspected by David Osborne 一样,当使用 ISession 进行批量工作(例如数据导入)时,session 一级缓存可能会变得太大。

您可以选择多种解决方案:

  • 按照 David Osborne 的建议和 reference documentation 的建议清除 session (ISession.Clear)。如果每一行数据从不引用与前几行相同的某些数据,则在每一行之后进行清除不会有什么坏处。每 50 或 100 行执行一次应该足以避免增加 session 一级缓存对性能造成太大影响。根据您的发现进行调整。
  • 会议很便宜。您可以丢弃它并使用新的,而不是清除它。 (但避免在每一行都这样做,它不是完全免费的。)
  • 考虑使用 stateless session (ISessionFactory.OpenStatelessSession())。它们可以更适合一些批处理。

备注:

您当前的实施似乎每笔交易只保存一个实体。如果您可以保存一堆相同 类 的实体,您可能会受益于 batching(至少 SQL-Server)并提高性能。它还会减少连接数 releases/re-acquiring,因为默认情况下 SQL 连接会在每次交易后释放。