NHibernate事务下的大数据查询

NHibernate事务下的大数据查询,nhibernate,transactions,Nhibernate,Transactions,我理解即使在读取数据时也应该使用显式事务,但我无法理解为什么下面的代码在NHibernate事务下运行要慢得多(而不是在没有它的情况下运行) session.BeginTransaction(); var result=session.Query(); Commit(); 如果需要的话,我可以发布更详细的UT代码,但如果我查询50000多条订单记录,在NHibernate的显式事务下运行此查询大约需要1秒,没有查询只需要15/20毫秒 更新日期:2019年1月15日 下面是详细的代码 [Te

我理解即使在读取数据时也应该使用显式事务,但我无法理解为什么下面的代码在NHibernate事务下运行要慢得多(而不是在没有它的情况下运行)


session.BeginTransaction();
var result=session.Query();
Commit();

如果需要的话,我可以发布更详细的UT代码,但如果我查询50000多条订单记录,在NHibernate的显式事务下运行此查询大约需要1秒,没有查询只需要15/20毫秒

更新日期:2019年1月15日 下面是详细的代码

[Test]
        public void TestQueryLargeDataUnderTransaction()
        {
            int count = 50000;
            using (var session = _sessionFactory.OpenSession())
            {
                Order order;
                // write large amount of data
                session.BeginTransaction();
                for (int i = 0; i < count; i++)
                {

                    order = new Order {OrderNumber = i, OrderDate = DateTime.Today};
                    OrderLine ol1 = new OrderLine {Amount = 1 + i, ProductName = $"sun screen {i}", Order = order};
                    OrderLine ol2 = new OrderLine {Amount = 2 + i, ProductName = $"banjo {i}", Order = order};
                    order.OrderLines = new List<OrderLine> {ol1, ol2};

                    session.Save(order);
                    session.Save(ol1);
                    session.Save(ol2);
                }

                session.Transaction.Commit();

                Stopwatch s = new Stopwatch();

                // read the same data 
                session.BeginTransaction();
                var result = session.Query<Order>().Where(o => o.OrderNumber > 0).Skip(0).Take(100).ToList();
                session.Transaction.Commit();

                s.Stop();
                Console.WriteLine(s.ElapsedMilliseconds);
            }
        }
[测试]
public void TestQueryLargeDataUnderTransaction()
{
整数计数=50000;
使用(var session=\u sessionFactory.OpenSession())
{
订单;
//写入大量数据
session.BeginTransaction();
for(int i=0;i
for循环迭代50000次,每次迭代创建3个对象。因此,当您第一次调用Commit()时,会话知道大约150000个对象将在提交时(或更早)刷新到数据库中(取决于您的id生成器策略和刷新模式)

到目前为止,一切顺利。NHibernate不一定经过优化,可以在会话中处理这么多对象,但只要小心,它是可以接受的

关于这个问题

重要的是要认识到,提交事务不会从会话中删除150000个对象

当您稍后执行查询时,它会注意到它位于事务内部,在这种情况下,默认情况下将执行“自动刷新”。这意味着在将SQL查询发送到数据库之前,NHibernate将检查会话已知的任何对象是否有可能影响查询结果的更改(这有点简化)。如果发现这样的更改,它们将在执行实际的SQL查询之前传输到数据库。这将确保执行的查询能够根据同一会话中所做的更改进行筛选

您注意到的额外时间是NHibernate在会话已知的150000个对象上迭代以检查任何更改所需的时间。NHibernate的主要用例很少涉及几十个或几百个对象,在这种情况下,检查更改所需的时间可以忽略不计

您可以为查询使用一个新会话来避免这种效果,也可以在第一次提交后立即调用session.Clear()。(请注意,对于生产代码,session.Clear()可能很危险。)

附加:自动刷新在查询时发生,但仅在事务内部发生。可以使用session.FlushMode控制此行为。在自动刷新期间,NHibernate将只刷新可能影响查询结果的对象(即哪些数据库表受到影响)

关于保持会话,还有一个额外的影响需要注意。考虑这个代码:

using (var session = _sessionFactory.OpenSession())
{
    Order order;
    session.BeginTransaction();
    for (int i = 0; i < count; i++)
    {
        // Your code from above.
    }

    session.Transaction.Commit();

    // The order variable references the last order created. Let's modify it.
    order.OrderDate = DateTime.Today.AddDays(4);

    session.BeginTransaction();
    var result = session.Query<Order>().Skip(0).Take(100).ToList();
    session.Transaction.Commit();
}
使用(var session=\u sessionFactory.OpenSession())
{
订单;
session.BeginTransaction();
for(int i=0;i
在第一次调用Commit()之后,对订单日期的更改会发生什么情况?在第二个事务中执行查询时,尽管对象修改本身发生在事务启动之前,但该更改仍将保留到数据库中。相反,如果删除第二个事务,那么该修改当然不会持久化

管理会话和事务有多种方法,可用于不同的目的。但是,到目前为止,最简单的方法是始终遵循以下简单的工作单元模式:

  • 公开会议
  • 立即打开交易
  • 完成合理的工作量
  • 提交或回滚事务
  • 处理事务
  • 处置会话
  • 放弃使用会话加载的所有对象。在这一点上,他们仍然可以 将在内存中使用,但不会保留任何更改。更安全的方法是 摆脱他们

  • 您知道事务的隔离级别吗?@DavidOsborne默认隔离级别为Serializable。我也试过重新提交。另外,如果这是SQLite,那么ReadUncommitted和Chaos是不可用的……谢谢,这是有意义的。我相信,我们使用NHibernate会话的方式需要重新审视。