程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 性能優化總結(二):聚合SQL

性能優化總結(二):聚合SQL

編輯:關於.NET

本篇主要講如何使用一句較復雜的SQL來加載整個聚合對象,以達到最小化數據庫連接次數。主要是解釋其中的原理。

LazyLoad及其缺點

相信越來越多的人已經開始使用富領域對象進行領域/業務層的實現了。而目前主流的數據庫依然還是關系型的。這中間的轉換,我們 叫它ORM。ORM的設計中,有一個常用的模式叫作“延遲加載(LazyLoad)”。基設計思想大致上是說,不要把所有的數據都加載進內存, 而是等到真正要使用數據的時候,再把它加載進內存。

例如以下這個聚合對象:

(為了和後面的代碼保持一致,這裡面使用的是GIX4項目中真實的類,可能會帶有一些領域特性,望讀者見諒。後面可能會繼續使用此 例,現大致對其進行解釋:其中,PBSType表示一套PBS模板/類型,一套模板由許多PBS組成。PBS是Project Breakdown Structure的簡稱 ,用於對某一個項目進行分解,這裡面一個PBS對象的實例其實只是結構中的一項,應該在後面加上Item,不過公司的人都習慣了,所以就 延用這個命名。每個PBS有許多屬性(PBSProperty),每個屬性又有許多可選值(PBSPropertyOptionalValue)。)

這個對象,在使用了LazyLoad對PBSType進行設計之後,客戶程序使用代碼如下:

var type = PBSType.Get(id);
//do something
//...

//lazily load a pbs list. data access occurs.
PBSList pbsList = type.PBSs;

//read from memory
var pbsListCount = type.PBSs.Count;

這裡一共產生了兩次數據訪問:獲取PBSType對象、獲取所有在該PBS模板下的PBS對象列表。此例說明了對集合對象使用LazyLoad,還 有一種比較常用的LazyLoad:對引用對象的LazyLoad。如下例:

文章對象引用一個用戶對象來表示其作者。這個外鍵引用的關系,常常也被設計為LazyLoad。

這一模式已經被廣泛地應用在各種ORM框架中,Linq to sql、EF等。這些ORM框架極大的方便了開發者,不需要再寫煩人的SQL,加快了 開發效率。但是如果不謹慎使用這一模式,很可能會造成過多的數據庫連接次數,導致性能低下。如果是分布式程序,則會是更耗時的遠 程連接。如:

IList<Article> articles = ArticleRepository.Get(new PagerInfo()
{
   PageIndex = 1,
   PageSize = 10
});
foreach (var article in articles)
{
   //LazyLoad
   User owner = article.Owner;
}

這段代碼中一共產生了 11 次數據訪問/遠程連接,相當的恐怖吧!

如何能保證又能降低連接次數,又不使用傳統的Table方案呢?這就是今天要說的,一個用於重構的模式:聚合對象SQL。

什麼是“聚合SQL”

要支持OO的領域對象,同時保證性能,我們的ORM就需要做到:獲取對象時,一次性獲取它指定的關系對象(集合/引用);同時,仍然 保留LazyLoad。

例如,當我們加載上述的Article及User時,可以調用類似ArticleRepository.Get_With_User的方法,使得一次性加載Article及其對 應的User。那麼,數據層訪問數據庫時,對應的SQL應該是把所有的數據都查詢出來,大致是:

select a.*, u.*
from Articles a inner join Users u on a.UserId = u.Id

然後在把整個Table映射為Article對象列表的過程中,在每一行中讀取並映射出User對象,然後對該行的Article對象的Owner屬性賦值 。

對應的,集合對象的一次性加載,要完成對數據的一次性加載,生成類似以下的SQL:

select * from PBSType t
     left outer join PBS on t.Id = PBS.PBSTypeId

在應用中,當然不會那麼簡單,不過都是由以上兩種方式組合而成。如,在GIX4的項目PBS模塊中使用到這樣的一個SQL,其中關於SQL 的生成及格式定義,接下來我將會做更詳細的解釋:

private static readonly string SQL_GET_BY_PROJECT_WITH_PROPERTY_VALUES = string.Format(@"
select
{0},
{1},
{2},
{3} 
from ProjectPBS pp
   left outer join ProjectPBSPropertyValue v on pp.Id = v.ProjectPBSId
   left outer join PBSProperty p on v.PBSPropertyID = p.Id
   left outer join PBSPropertyOptionalValue ov on p.Id = ov.PBSPropertyId
where pp.ProjectId = '{{0}}'
order by pp.Id, v.Id, p.Id
", ProjectPBS.GetReadableColumnsSql("pp"),
  ProjectPBSPropertyValue.GetReadableColumnsSql("v"),
  PBSProperty.GetReadableColumnsSql("p"),
  PBSPropertyOptionalValue.GetReadableColumnsSql("ov"));

今天先把理論寫一下。下一節主要講在目前的GIX4系統中,我們是如何引入聚合SQL來改善性能的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved