首页 > 代码库 > [NHibernate]N+1 Select查询问题分析
[NHibernate]N+1 Select查询问题分析
目录
写在前面
文档与系列文章
N+1 Select查询问题分析
总结
写在前面
在前面的文章(延迟加载,立即加载)中都提到了N+1 Select的问题,总觉得理解的很不到位,也请大家原谅,这也是为什么单独将该问题拿出来做分析的原因。nhibernate的默认Lazy加载方式是解决N+1 select问题的一种方案,而我自身的理解是立即加载可以解决,完全的背道而驰了。写出那篇文章后,对这个问题,一直念念不忘,总觉得哪地方不对劲。由于我对问题的理解很不透彻,也同样造成你的误解,真的很抱歉。
文档与系列文章
[Nhibernate]体系结构
[NHibernate]ISessionFactory配置
[NHibernate]持久化类(Persistent Classes)
[NHibernate]O/R Mapping基础
[NHibernate]集合类(Collections)映射
[NHibernate]关联映射
[NHibernate]Parent/Child
[NHibernate]缓存(NHibernate.Caches)
[NHibernate]NHibernate.Tool.hbm2net
[NHibernate]Nullables
[NHibernate]Nhibernate如何映射sqlserver中image字段
[NHibernate]基本配置与测试
[NHibernate]HQL查询
[NHibernate]条件查询Criteria Query
[NHibernate]增删改操作
[NHibernate]事务
[NHibernate]并发控制
[NHibernate]组件之依赖对象
[NHibernate]一对多关系(级联删除,级联添加)
[NHibernate]一对多关系(关联查询)
[NHibernate]多对多关系(关联查询)
[NHibernate]延迟加载
[NHibernate]立即加载
[NHibernate]视图处理
N+1 Select查询问题分析
使用sql查询数据的时候,如果select太多,势必也会对数据库造成压力,影响性能。比如如果查询n个Customer对象,则一次查询获得所有的customer对象,然后根据customerid查询关联的Order表。(立即加载的情况下)
一个例子
1 /// <summary> 2 /// NHibernateUtil方式,立即加载客户信息及关联的数据 3 /// </summary> 4 /// <param name="customerID"></param> 5 /// <returns></returns> 6 public Customer GetCustomerByImmediatelyLoadNHibernateUtil(Guid customerID) 7 { 8 //获得ISession实例 9 //通过using的方式将session释放,为了保证是立即加载的数据,而不是延迟加载的10 //还记得上篇文章中提到的在延迟加载中,在关闭session情况下,再读取数据的时候就会有一个异常信息11 //忘记的可以再回到上一篇文章进行查看12 using (ISession session = NHibernateHelper.GetSession())13 {14 Customer customer = session.Get<Customer>(customerID);15 //16 // 摘要: 17 // Force initialization of a proxy or persistent collection.18 //19 NHibernate.NHibernateUtil.Initialize(customer.Orders);20 return customer;21 }22 }
这里使用NHinernateUtil实用类进行强制立即加载方式,测试代码
1 protected void btnImmediately_Click(object sender, EventArgs e)2 {3 Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness();4 Customer customer = customerBusiness.GetCustomerByImmediatelyLoadNHibernateUtil(new Guid("B0720295-9541-40B3-9994-610066224DB8"));5 bool isInitOrder = NHibernate.NHibernateUtil.IsInitialized(customer.Orders);6 }
查看生成的sql语句
本来我们是想查询customer,一次查询就够,可是通过监视到的sql,发现多了一次sql查询,也就是查询customer的关联数据表的数据。
而在延迟加载的情况下,则会在需要的时候才会去查询,性能上面更好。(有关延迟加载的内容,请在系列文章中查找,这里不再赘述了)
如果采用延迟加载方式,生成的sql如下(借用延迟加载文章中的图说明)
一对多关系Lazy加载
多对多关系Lazy加载
order和Product多对多关系,此时生成的sql语句
你可以看到此时生成的sql语句是通过left outer join(左外连接)进行关联数据表的,而此时只生成一条sql语句,明显减少了查询数据的次数。
总结
(1)select语句的数目太多,需要频繁的访问数据库,会影响检索性能。此时可采用sql中的左外连接查询,在一条sql语句中将所有数据查询出来,并且减少了select的次数。
(2)在应用逻辑只需要访问Customer对象,而不需要访问Order对象的场合,加载Order对象完全是多余的操作,这些多余的Order对象白白浪费了许多内存空间。
为了解决以上问题,Hibernate提供了其他两种检索策略:延迟检索策略和迫切左外连接检索策略。延迟检索策略能避免多余加载应用程序不需要访问的关联对象,迫切左外连接检索策略则充分利用了SQL的外连接查询功能,能够减少select语句的数
这里就N+1 select问题做一下特别说明,有问题,如果不想办法解决,总觉得有个疙瘩,早解决,早有好心情。通过本篇的分析,你是不是也对n+1的问题豁然开朗?这也怪我了,当时对那一块理解确实有误,再次感到很抱歉。
[NHibernate]N+1 Select查询问题分析