性能优化总结(二):聚合SQL
本篇主要讲如何使用一句较复杂的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来改善性能的。
- 未来AI可能会淘汰180万个工作岗位,你感到恐惧了吗
- css基础:把所有背景图都集成在一张图片上,减少图片服务器请求次数
- Docker可视化界面(Consul+Shipyard+Swarm+Service Discover)部署记录
- windows 2008上启用防火墙后sqlserver 2005经常出现连接超时的解决办法
- 重温delphi之控制台程序:Hello World!
- Docker集中化web界面管理平台-Shipyard部署记录
- "Goole项目托管"及"CodePlex发布开源项目"要点
- Docker网络解决方案-Calico部署记录
- c#中开发ActiveX的学习笔记
- Android新手之旅(4) 通过HTTP访问web
- Flash/Flex学习笔记(9):ActionScript3.0与Javascript的相互调用
- Flash/Flex学习笔记(8):ActionScript3.0中的面对对象
- Docker网络解决方案-Weave部署记录
- Flash/Flex学习笔记(7):FMS3.5基于IIS的安装
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【一起学系列】之状态模式:你听过“流程”模式吗?
- 【一起学系列】之代理模式:是为了控制访问啊!
- 【一起学系列】之剩下的设计模式们
- 如何使用k3OS和Argo进行自动化边缘部署?
- 设计模式总篇:从为什么需要原则到实际落地(附知识图谱)
- 一文入门DNS?从访问GitHub开始
- 没内鬼,来点干货!SQL优化和诊断
- 好像很厉害的生成器!一秒钟搞定一个项目
- 【一起学系列】之策略模式:好多鸭子啊
- 没内鬼,来点干货!volatile和synchronized
- 【一起学系列】之观察者模式:我没有在监控你啊
- Celery 4 初体验及踩坑
- MySQL 最佳实践:gh-ost 工具使用详解
- 如何将 Hexo 博客部署到云开发静态网站托管
- WordPress 静态化部署到云开发网站托管