数据库访问的性能问题与瓶颈问题
声明:
本文是一篇有争议的文章,甚至有可能是一篇争议非常大的文章,可能争来争去依然无法得到一个统一的意见。
场景
个别公司的技术决策者要求团队的开发人员在编写数据访问层的时候,禁止在程序中出现任何的SQL语句,禁止使用Entity Library,禁止使用NBear、NHibernate、IBatis、Entity Framework等ORM框架,只允许使用存储过程。试想一下,您的公司是否是这样子的?您的身边有没有这样的朋友,他们的公司存在这样或类似这样的情况吗?
矛盾点
对于开发人员来说,使用存储过程的话,工作量比以前要大很多,而且涉及到表的字段更改,项目重构也是个非常麻烦的问题。使用ORM很方便就可以实现数据的CRUD功能,多表操作也非常的容易,原来写很少的代码就可以操作数据,现在却要写很多的代码。本来公司给的时间就短,项目紧,现在这么一搞,工作量一下就增加了不少,再者,如果需求一旦发生变化,不可避免的数据库就得增加或修改某些字段,相应的存储过程跟数据访问层的方法都得做调整,开发人员的日子不好过了。
对于公司的决策者来说,性能问题是不可以妥协的,无论付出多大代价,既然做出了决定,那么就没什么好讨论的。
结果
结果可想而知,最终是按照决策者的决定,开发人员加班加点的做开发,既然决策者都已经做了决定,似乎再讨论用不用存储过程就是一个非常敏感的话题,再讨论类似的问题的话,开发人员所面临的处境就可能是尴尬的,在某些公司甚至是危险的。无论怎样,开发人员可能最终都很难改变决策者的决定。
决策者的心声
带着这些疑惑,我跟多位决策者进行沟通,搜集了他们的意见,总结下来的话,大概就是以下几点:
- 性能问题。经过测试,使用ORM比直接调用存储过程慢10倍。如果是做软件项目或软件产品,使用ORM问题不大,可是如果是以运营为主(访问量较大)的Web网站,性能上就会有问题。
- 并发问题。一旦访问量较大并且达到一定数量级的时候,ORM就可能会出现并发问题。
- DBA的能力受限。一旦出现性能问题,如果是按照写存储过程的方式来做,公司可以招来技术实力强的DBA直接改存储过程进行优化,而如果是使用ORM的话,那么DBA就很难进行优化,因为DBA很难读懂ORM写的程序,更不知道ORM内部的实现方式,这样一来,DBA的能力就得不到施展。
- 不愿意被微软绑架。以Entity Framework为例,Entity Framework不是开源的,如果出现性能问题,不能够看到源代码,这可能是一个风险。再者,一个强有力的公司强有力的团队,如果没有自己的技术,总是使用微软的不开源的框架,这怎么可以?
- Entity Framework是微软的产品,微软的产品只适合中小型的公司做开发,大的互联网公司是不敢用的,甚至他们可能采用Java+Oracle来做,一旦达到一定数量级,微软的东西就可能会出现问题。
笔者的心声
在充分理解了决策者的心声以后,我思绪万千,心中久久不能平静。终于,在把很多东西看淡,抛开杂念,在这样一个宁静的夜晚,也坦诚的把我的想法一一阐述,分别针对决策者的心声,谈一谈我的个人看法。
1. 到底什么是性能问题?存在不存在性能问题?
来看下测试是如何做的,使用存储过程进行插入或删除操作,分别使用存储过程和Entity Framework,循环10000次或者1000000次操作,然后整体上存储过程要比使用Entity Framework要快10倍。实际场景是怎样的呢?
实际的场景是用户点击页面上的按钮,执行了1次操作,我们假定按照写存储过程的方式来做,用户这1次操作可能需要0.001秒,而使用Entity Framework要慢10倍,用了0.01秒,那么这0.001秒比0.01秒的确是快10倍,但是对用户来说,可能根本就没有明显的差距,因为这么微弱的时间用户是体会不出来的。我们开发的程序,对用户来说,我们的产品会不会因为这0.001秒跟0.01秒的差距而打折扣呢?这微弱的差距是严重性能问题?还是可以忽略不计?
2. 到底存在不存在并发问题?
诚然,可能之前有团队使用ORM开发高并发的项目,他们在运营中出现了并发问题,可是DBA又无法查出来到底是什么地方导致了并发问题,最终把一切的一切归咎在ORM上。
亲爱的朋友们,让我们理智的冷静的来分析下两者的技术实现上的不同吧。
直接调用存储过程:打开数据库连接--》执行编译好的数据库语句--》关闭数据库连接;
ORM:打开数据库连接--》把对象解释成SQL语句--》执行SQL操作--》关闭数据库连接。
通过对比,我们可以发现,ORM就可以比作是一个SQL生成器,它把对象解释,拼一个SQL语句出来,然后在执行这个SQL语句,由于还需要解释,就相当于多了一步翻译的工作。因此,就比存储过程慢了一点点,那么慢的这一点点会不会出现并发问题?我的意见是并发问题多半是由于锁的影响,只要不产生锁,就不会有并发问题。正因为如此,高并发的项目开发,多半是忌讳使用事务,有的程序员手写异常后的数据库回滚语句(有些滑稽哈,但事实上就是这样),项目中也不推荐使用游标跟触发器。
3. DBA能力受限。诚然,DBA看不懂ORM写的程序,更加不明白ORM内部的实现原理。但是,DBA是可以跟程序员配合,利用SQL Profiler等工具,看到最终SQL语句是如何执行的。也就是说,DBA的能力也是依然可以发挥出来的,只不过是需要跟程序员配合而已,或者DBA需要熟悉如何调试、跟踪。如果说全部写存储过程了,DBA能力是放开了,可是程序员的能力就受限了,譬如说,在进行大批量的数据插入的时候,大家都知道,.ADO.NET2.0的一个新特性SqlBulkCopy是多么的快,估计这是DBA无法优化的。对SqlBulkCopy不熟悉的朋友,请参考《SQLServer中批量插入数据方式的性能对比》。
很多时候,一个软件性能的优化,需要从整体去考虑,并不一定是说出现数据库性能问题,就一定是DBA的责任,或者说一定是程序员的责任。在DBA跟程序员之间难道就真的像插销跟插板之间那样,职责分的特别的清楚?很多时候我们得充分利用存储过程的特性,跟.NET平台的一些优良特性,选择适合我们的来进行开发,没有什么是最佳的,但是对我们来说,适合我们就好。
从另外的角度考虑,其实在项目初期,DBA就应该参与进来,进行数据库的设计了,而一旦数据库设计好了,设计得并不规范,存储过程也写了成千上万了,将来一旦出现性能问题,相信也够DBA喝一壶的。
4. 不愿意被微软绑架。这个观点倒是让我感觉到意外,至少我们很多都在用微软的.NET Framework,我们使用微软的SQL Server数据库,如果说我们被绑架,可能现在就已经被绑架了,SQLServer的存储过程跟Oracle、Mysql的存储过程是不一样的,如果将来进行数据库的迁移,那么可想而知后果是怎样的。到底怎样才是真正的被绑架?
5. .Net与Java孰好孰坏?
关于这个问题的讨论,一直就是个无休止的讨论。scottgu把这个比作是“带有宗教性质的技术争论”。诚然,讨论这样的问题的确令人讨厌,而且是浪费时间,而且讨论的双方都深切的关注着。讨论来讨论去最终也不会有结果。
在目前所运行的软件系统中,我们可以看到其背后的平台、语言等是各种各样,MySpace是基于.NET平台的,淘宝网是基于Java的,而Google则推崇使用Python,许多大型的电力系统还依然运行在C++平台上,最关键的一点,.NET并不是没有在大型项目中应用。只不过是Java起步早,.NET起步晚而已,要在前几年,Java做的大型项目的确是比.NET的大型项目要多。
很多时候,即使是使用相同的开发语言,不同的程序员开发的程序效率就差30倍以上,甚至几千倍以上,这点好不夸张。诚然,每门技术自有其缺点,但它们也都自有其优点,如果它的优点恰好能符合你的需要,用它就好了。重要的是,你有没有使用好它的能力。
总结
其实总结就不必了,说点题外话吧。存储过程在单条执行操作的时候,的确要比使用ORM要快,可是如果是执行批量的操作,使用存储过程就会非常的费劲。之前我是这样做的。假定更新1000条数据,数据库里只有2个字段,循环调用1000次存储过程需要2分钟左右,当时我把要更新的id以参数的形式逗号分隔传入存储过程,在存储过程中循环执行1000条数据,发现时间跟循环调用1000次存储过程的时间是差不太多的,最终进行了改进,改进的方法嘛,还是把要更新的Id以参数形式逗号分隔传入存储过程,然后使用update table set value=’value’ where id in select id in 分隔函数(id1,id2,id3…..),经过这种方式,更新1000条数据的时间从2分钟变成了200毫秒,可是问题依然不完美,方法存在局限性。
首先,使用这种方法参数的长度是有限制的,varchar类型最大不超过8000,nvarchar类型最大长度不超过4000。
其次,如果表中有多个列,要更新的也是多列,存储过程的局限性就出来了。
再次声明:文中观点仅代表个人观点,如果您有不同意见,欢迎共同讨论。
最后,给大家分享个幽默视频,来缓解下这种紧张而激烈的争论吧: