`

是否应该使用存储过程?浅论架构中的粘合层

阅读更多

 

        前一阵子看到网上关于是否该使用存储过程的一些争论,对我颇有启发,当初我们在做系统设计时,对是否该使用存储过程,也争论了很久,最后还是没有定论,留了一些存储过程,但是业务逻辑都在代码里,现在想来是一个明智的决定。这个争论使我想起一个架构设计时的词语“粘合层”,下面我把自己这方面的心得和感受详细的描述一下。

 



 

 

 

    一般情况下,我做设计时,很有可能会使用上图这种结构。粘合层使用什么方式完全是随客观情况来定的。(粘合层一般有把不一致的东西粘合在一个系统中这个作用,用流行的话来说,OO和关系数据库不匹配,有阻抗,粘合层就是想办法让两个不大对脾气的人协力完成系统的目标)
     业务逻辑层控制产品的业务逻辑,而持久化层提供业务对象的持久化功能。但是持久化层,可能也并不清楚对象和数据库表结构之间的关系。而是要粘合层来处理。
     对持久化层来讲,我只知道把数据存储到数据库中了,至于对象的几个属性是否拼成一个字段来保存了,还是一个字段对应一个属性,甚至一个属性劈开成几个字段,都和业务逻辑层、持久化层无关。他们只关心,从外部来看,这个对象有哪些属性就可以。
   这么来看,粘合层有几种选择,首选(也是最流行的)就是使用ORM机制,例如Hibernate;第二,自己做一个粘合层,和第一种做法基本一致;第三,使用数据库的机制(例如存储过程、触发器、数据库自定义函数)来进行粘合。
   我们把(1)和(2)看成一种思路,到底使用程序中使用粘合层和使用数据库粘合层,哪种更好呢?就像很多人说的,没有最好,只有合适不合适。
  
    说一个简单的粘合层,比如我的产品要支持多种数据库,那么,我的代码中,如果所有的查询都是直接从表查询的,在跨数据库时,就会面临一个问题,比如有些内容,在Oracle中可以设计成一个表,但是在SQLServer中需要设计成两个表才行,那么,移植部分就会非常痛苦。但是如果我的查询都是对应视图的,管你几个表,只要某个视图能向程序提供需要的数据就行,那么Oracle里一个表,SQLServer的两个表,设计上就自由多了。这样,这些视图就可以称之为“粘合层”,避免了“表”和代码的硬挂接,留下了一些设计上扩展和变更的余地。
 
    代码中的粘合层,例如使用ORM,我就不多写了,ITEye的很多高手比我精通。
我们来看有人总结的使用存储过程的利弊:
收益:
1.在生产环境下,可以通过直接修改存储过程的方式修改业务逻辑(或bug),而不用重启服务器。但这一点便利被许多人滥用了。有人直接就在正式服务器上修改存储过程,而没有经过完整的测试,后果非常严重。

2.执行速度快。存储过程经过编译之后会比单独一条一条执行要快。但这个效率真是没太大影响。如果是要做大数据量的导入、同步,我们可以用其它手段。

3.减少网络传输。存储过程直接就在数据库服务器上跑,所有的数据访问都在服务器内部进行,不需要传输数据到其它终端。但我们的应付服务器通常与数据库是在同一内网,大数据的访问的瓶颈会是硬盘的速度,而不是网速。

4.能够解决presentation与数据之间的差异,说得文艺青年点就是解决OO模型与二维数据持久化之间的阻抗。 领域模型和数据模型的设计可能不是同一个人(一个是SA,另一个是DBA),两者的分歧可能会很大——这不奇怪,一个是以OO的思想来设计,一个是结构化 的数据来设计,大家互不妥协——你说为了软件的弹性必须这么设计,他说为了效率必须那样设计,为了抹平鸿沟,就用存储过程来做数据存储的逻辑映射(把属性 映射到字段)。好吧,台下已经有同学在叨咕ORM了。

5.方便DBA优化。所有的SQL集中在一个地方,DBA会很高兴。这一点算是ORM的软肋。不过按照CQRS框架的思想,查询是用存储过程还是ORM,还真不是问题——DBA对数据库的优化,ORM一样会受益。况且放在ORM中还能用二级缓存,有些时候效率还会更高。
 
弊端:

1.SQL本身是一种结构化查询语言,加上了一些控制(赋值、循环和异常处理等),但不是OO的,本质上还是过程化的,面对复杂的业务逻辑,过程化的处理会很吃力。这一点算致命伤。

2.不便于调试。基本上没有较好的调试器,很多时候是用print来调试,但用这种方法调试长达数百行的存储过程简直是噩梦。好吧,这一点不算啥,C#/java一样能写出噩梦般的代码。

3.没办法应用缓存。虽然有全局临时表之类的方法可以做缓存,但同样加重了数据库的负担。如果缓存并发严重,经常要加锁,那效率实在堪忧。

4.无法适应数据库的切割(水平或垂直切割)。数据库切割之后,存储过程并不清楚数据存储在哪个数据库中。

5.精通SQL的新手越来越少——不要笑,这是真的,我面试过N多新人,都不知道如何创建全局临时表、不知道 having、不知道聚集索引和非聚集索引,更别提游标和提交叉表查询了。好吧,这个缺点算是凑数用的,作为屌丝程序员,我们的口号是:没有不会的,只有 不用的。除了少数有语言洁癖的人,我相信精通SQL只是时间问题。
 
在实际的过程中,我的做法是:
(1)坚决不能把业务过程写在数据库中
(2)在设计过程中,因为大多数时候,数据存取效率必须考虑,所以会使用一些数据库机制来做粘合层,这部分,都安排给组内精通数据库的工程师(例如之前我们产品组有一个DBA,程序方面一般但是能按要求写代码,数据库方面功力很深)来做。
(3)数据库的粘合层,是为了解决数据形式转换的问题
(4)持久层通过粘合层取出的数据,和业务对象是一致的;但实际数据库表中的数据,可能形态、结构和业务对象差的很远,例如一个对象可能分存在两个表中;也可能两个对象存在一个表中(这是粘合层存在的意义)
(5)跨数据库的工作可以由粘合层完成,但是也可以在代码里完成。如果组内没有精通数据库的,代码是优先选择。

 

 

 

 

  • 大小: 21.9 KB
3
1
分享到:
评论
9 楼 windshome 2013-06-07  
花了一点时间看了Mybatis,深感Mybatis也就是一种粘合层,用以粘合对象(OO)和DB。不知道我这里理解是不是准确。

之前在JDon看到的一篇帖子观点是“干掉DBA”,我觉得非常诧异,我倒是觉得,一个好的产品,如果涉及到需要数据库方面很高的效率要求,需要DBA和架构师、开发人员一起协作。所以,我才会写这样的文章,我想表达的是,根据需要,把粘合层放在数据库中(以存储过程、内部函数、视图、触发器等形式)和放在自己代码中都是可以。这个粘合层不是业务逻辑层,而是业务逻辑层和存储层之间的一种粘合、缓冲。

最好是,DBA和设计开发人员一起努力,利用自己的技术,合作开发出产品。

8 楼 windshome 2013-06-06  
这次你老兄说的很清楚,完全同意你的说法。我的那篇文章,也基本上是针对一般产品的。效率和数据量上有特殊要求一定得特殊设计了。
7 楼 rensanning 2013-06-06  
windshome 写道
rensanning 写道
还应该考虑到:
1、数据库connection:
数据库都有最大连接数的限制,虽然可以通过连接池来管理connection,但是如果AP层的业务处理需要大量的DB链接,或者互联网应用的高并发(C10K),对数据库也不是什么好事。

2、服务器负载:
业务处理是否会加重AP server的负载,从而引起AP server的OOM。


没有搞清楚你老兄的意思,你的第一点,前端不管要求连接还是事务,当然都不能超出数据库的最大能力范围,不管哪个行业都应该如此,用存储过程和代码控制都一样。使用存储过程减少了连接占用,但是数据库的内存、CPU消耗也是需要的

第二,AP Server本身就是做业务处理的,负载都是应该AP Server承担的,内存不足增加存储呗

不知道我理解的对不对,如果我说的不对你老兄再指点。

数据库连接这个需要根据业务来看,如果你的业务处理很复杂,不能通过1次连接而是需要多次连接数据库,存储过程可以减少数据库连接,当然相应的数据库端的负载将会加大。

对于特定的场景,如果你不做存储过程,而是将数据通过JDBC->ResultSet->Bean的方式拿到APServer端处理,同样会加重APServer的负载,虽然可以通过加强APServer的配置来避免。以JavaEE为例,本身占用的内存就很多,而很多项目的开发并不能去调整GC策略,基本采用默认的GC策略,假设你有几千万条记录需要拿来做业务处理,试想在处理的一瞬间内存将会暴涨,如果JVM配置不当,将会频繁引起FullGC,以至于OOM。

很多东西都不是绝对的,任何事物的存在都有它存留的价值。
6 楼 windshome 2013-06-03  
rensanning 写道
还应该考虑到:
1、数据库connection:
数据库都有最大连接数的限制,虽然可以通过连接池来管理connection,但是如果AP层的业务处理需要大量的DB链接,或者互联网应用的高并发(C10K),对数据库也不是什么好事。

2、服务器负载:
业务处理是否会加重AP server的负载,从而引起AP server的OOM。


没有搞清楚你老兄的意思,你的第一点,前端不管要求连接还是事务,当然都不能超出数据库的最大能力范围,不管哪个行业都应该如此,用存储过程和代码控制都一样。使用存储过程减少了连接占用,但是数据库的内存、CPU消耗也是需要的

第二,AP Server本身就是做业务处理的,负载都是应该AP Server承担的,内存不足增加存储呗

不知道我理解的对不对,如果我说的不对你老兄再指点。
5 楼 rensanning 2013-05-31  
还应该考虑到:
1、数据库connection:
数据库都有最大连接数的限制,虽然可以通过连接池来管理connection,但是如果AP层的业务处理需要大量的DB链接,或者互联网应用的高并发(C10K),对数据库也不是什么好事。

2、服务器负载:
业务处理是否会加重AP server的负载,从而引起AP server的OOM。
4 楼 windshome 2013-05-31  
须等待 写道
如果存储过程只和业务或者LZ所说的粘合层做一样的工作,那这个存储过程就太废了肯定要被舍弃的。但是存储过程还是有自己的优势,比如预编译在性能上是略高的、面对大数据的处理直接省略了网络传输这个过程;不过在面对数据库迁移之类的变更,存储过程就很无力了。

还是看业务场景吧


就是考虑效率才用数据库中建存储过程来做粘合层的,否则我也更倾向于代码方式。
因为从产品设计之初,就做了多种数据库兼容方面的设计,所以数据库迁移对我们来讲还算好说。
3 楼 windshome 2013-05-31  
freezingsky 写道
对于结论我认同。特别是在业务相关的逻辑是否应该通过存储放入数据库这一方面,我个人是把数据库认为一个数据的存取点,只用来控制如何存,如何取。而业务相关逻辑本身应该放到业务层去完成。


嗯,只是我把业务层和数据存取隔离开来,把格式转换等等放在数据库层而已。
2 楼 须等待 2013-05-31  
如果存储过程只和业务或者LZ所说的粘合层做一样的工作,那这个存储过程就太废了肯定要被舍弃的。但是存储过程还是有自己的优势,比如预编译在性能上是略高的、面对大数据的处理直接省略了网络传输这个过程;不过在面对数据库迁移之类的变更,存储过程就很无力了。

还是看业务场景吧
1 楼 freezingsky 2013-05-31  
对于结论我认同。特别是在业务相关的逻辑是否应该通过存储放入数据库这一方面,我个人是把数据库认为一个数据的存取点,只用来控制如何存,如何取。而业务相关逻辑本身应该放到业务层去完成。

相关推荐

Global site tag (gtag.js) - Google Analytics