目錄
寫在前面
在前面的文章(延遲加載,立即加載)中都提到了N+1 Select的問題,總覺得理解的很不到位,也請大家原諒,這也是為什么單獨將該問題拿出來做分析的原因。nhibernate的默認Lazy加載方式是解決N+1 select問題的一種方案,而我自身的理解是立即加載可以解決,完全的背道而馳了。寫出那篇文章后,對這個問題,一直念念不忘,總覺得哪地方不對勁。由於我對問題的理解很不透徹,也同樣造成你的誤解,真的很抱歉。
文檔與系列文章
[NHibernate]持久化類(Persistent Classes)
[NHibernate]集合類(Collections)映射
[NHibernate]緩存(NHibernate.Caches)
[NHibernate]NHibernate.Tool.hbm2net
[NHibernate]Nhibernate如何映射sqlserver中image字段
[NHibernate]條件查詢Criteria Query
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的問題豁然開朗?這也怪我了,當時對那一塊理解確實有誤,再次感到很抱歉。