性能优化总结(二):聚合SQL

简介:

   本篇主要讲如何使用一句较复杂的SQL来加载整个聚合对象,以达到最小化数据库连接次数。主要是解释其中的原理。

LazyLoad及其缺点

    相信越来越多的人已经开始使用富领域对象进行领域/业务层的实现了。而目前主流的数据库依然还是关系型的。这中间的转换,我们叫它ORM。ORM的设计中,有一个常用的模式叫作“延迟加载(LazyLoad)”。基设计思想大致上是说,不要把所有的数据都加载进内存,而是等到真正要使用数据的时候,再把它加载进内存。

    例如以下这个聚合对象:

image

    (为了和后面的代码保持一致,这里面使用的是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。如下例:

image

    文章对象引用一个用户对象来表示其作者。这个外键引用的关系,常常也被设计为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来改善性能的。

目录
相关文章
|
10月前
|
SQL Java 数据库连接
JDBC基础入门与常用实例详解
JDBC基础入门与常用实例详解
108 0
|
10月前
|
应用服务中间件 nginx Windows
nginx配置wss
nginx配置wss
210 0
|
SQL 存储 算法
做 SQL 性能优化真是让人干瞪眼
很多大数据计算都是用SQL实现的,跑得慢时就要去优化SQL,但常常碰到让人干瞪眼的情况。
35154 4
做 SQL 性能优化真是让人干瞪眼
|
3月前
|
机器学习/深度学习 人工智能 运维
AI在自动化运维中的应用与挑战
在过去的十年中,人工智能(AI)技术已迅速渗透到各个行业,尤其是在自动化运维(AIOps)领域,显示出了巨大的潜力。本文探讨了AI在自动化运维中的具体应用、带来的技术优势以及面临的主要挑战。通过分析实际案例和技术细节,我们揭示了AI如何提升运维效率、降低成本以及改进系统可靠性。同时,也深入讨论了当前AI技术在AIOps应用中遇到的数据隐私、安全性和复杂度问题。
129 2
|
SQL 存储 关系型数据库
SQL调优指南—SQL调优进阶—聚合优化和执行
本文介绍如何优化器和执行器如何处理聚合(Group-by),以达到减少数据传输量和提高执行效率的效果。
227 0
|
SQL 存储 Oracle
SQL 性能优化
SQL 性能优化
181 0
SQL 性能优化
|
算法 图形学
计算机图形学——实验五 基本图形学算法及着色器初步编程
实验五 基本图形学算法及着色器初步编程 1、 理解基本图形元素光栅化的基本原理,理解直线裁剪算法的原理; 2、 掌握直线的光栅化算法:DDA和Bresenham算法; 3、 掌握直线裁剪算法:Cohen-Surtherland算法; 1、 编程实现DDA算法和Bresenham算法生成直线。 2、 实现Cohen-Surtherland直线裁剪算法,调试、编译、修改程序。 要求: 根据所给的直线光栅化的示范源程序,在计算机上编译运行,输出正确结果(示范代码有错误,指出并改正)。
299 0
计算机图形学——实验五 基本图形学算法及着色器初步编程
|
消息中间件 Cloud Native RocketMQ
带你读《企业级云原生白皮书项目实战》——6.3.1 行业背景
带你读《企业级云原生白皮书项目实战》——6.3.1 行业背景
119 0
|
存储 Kubernetes 容灾
Longhorn,企业级云原生容器分布式存储 - 备份与恢复
Longhorn,企业级云原生容器分布式存储 - 备份与恢复
627 0