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