一般来说,存储过程比现代RDBMS的内联语句更高效?
传统观点认为存储过程总是比较快。 所以,因为他们总是更快,使用他们所有的时间 。
我很确定这是以一度历史背景为基础的。 现在,我不主张存储过程是不需要的,但是我想知道什么情况下存储过程在MySql,SqlServer,Oracle等现代数据库中是必需的。 所有通过存储过程访问是矫枉过正的吗?
请注意 ,这是对未针对特定DBMS进行调整的存储过程的一般看法。 某些DBMS(甚至是同一个DBMS的不同版本)可能会与此相反,因此在假设所有这些依然成立之前,您需要仔细检查目标DBMS。
近十年来,我一直是Sybase ASE,MySQL和SQL Server数据库pipe理员(随着C,PHP,PL / SQL,C#.NET和Ruby的应用程序开发)。 所以,在这个(有时候)神圣的战争中,我没有特别的斧头。
存储过程的历史性能好处通常来自以下(没有特定的顺序):
- 预分析的SQL
- 预生成的查询执行计划
- 减lessnetworking延迟
- 潜在的caching好处
预parsing的SQL – 与编译和解释代码类似的好处,除非是非常微观的。
仍然是一个优势? 在现代CPU上一点也不明显,但是如果你发送的是一个非常大的一秒SQL语句,parsing开销可能会增加。
预生成的查询执行计划 。 如果你有很多JOIN,排列可能会变得难以pipe理(现代优化器由于性能的原因有限制和截断)。 对于非常复杂的SQL来说,由于优化程序试图找出“最接近的最好的”,所以具有不同的,可测量的(我已经看到一个复杂的查询需要10多秒才能生成一个计划,然后再调整DBMS) “执行计划。 通常,存储过程将其存储在内存中,这样可以避免这种开销。
仍然是一个优势? 大多数DBMS(最新版本)将cachingINDIVIDUAL SQL语句的查询计划,大大降低了存储过程和临时SQL之间的性能差异。 有一些注意事项和情况并非如此,所以您需要testing目标DBMS。
此外,越来越多的DBMS允许您提供优化程序path计划(抽象查询计划),以显着减less优化时间(对于特定和存储过程SQL !!)。
警告caching的查询计划不是性能万能药。 偶尔,生成的查询计划是次优的。 例如,如果您发送
SELECT * FROM table WHERE id BETWEEN 1 AND 99999999
,DBMS可能select全表扫描而不是索引扫描,因为您正在抓取表中的每一行(所以说统计数据)。 如果这是caching的版本,那么当您以后发送SELECT * FROM table WHERE id BETWEEN 1 AND 2
时,性能可能会变差。 这背后的原因超出了本文的范围,但进一步阅读,请参阅: http : //www.microsoft.com/technet/prodtechnol/sql/2005/frcqupln.mspx和http://msdn.microsoft.com/ en-us / library / ms181055.aspx和http://www.simple-talk.com/sql/performance/execution-plan-basics/“总而言之,他们认为在执行编译或重新编译时提供除公共值之外的任何东西都会导致优化程序编译和caching该特定值的查询计划,但是当该查询计划被重复用于后续执行时查询公共值('M','R'或'T'),导致次优性能,这个次优性能问题一直存在,直到查询被重新编译为止。参数值提供,查询可能会或可能不会有性能问题。“
减lessnetworking延迟 A)如果你反复运行相同的SQL,并且SQL增加了许多KB的代码 – 用一个简单的“exec foobar”取代这个代码真的可以加起来。 B)存储过程可用于将程序代码移入DBMS。 这样可以将大量的数据转移到客户端,只是为了让信息发回(或者根本没有!)。 类似于在DBMS中进行JOIN(在你的代码中)(大家最喜欢的WTF!)
仍然是一个优势? A)现代的1Gb(和10Gb以上!)以太网真的使这个可以忽略不计。 B)取决于你的networking是多么饱和 – 为什么没有好的理由来回传送几兆字节的数据呢?
潜在的caching优势如果DBMS上有足够的内存,并且所需的数据位于服务器的内存中,则执行服务器端数据转换的速度可能会更快。
仍然是一个优势? 除非你的应用程序共享内存访问DBMS数据,边缘将永远是存储过程。
当然,没有关于参数化和临时SQL的讨论,没有关于存储过程优化的讨论将是完整的。
参数化/准备SQL
存储过程和临时SQL之间的交叉,它们是使用“参数”作为查询值的主机语言的embedded式SQL语句,例如:
SELECT .. FROM yourtable WHERE foo = ? AND bar = ?
这些提供了一个更现代的优化器可以用来caching(和重用)查询执行计划的查询的更一般化的版本,从而带来存储过程的许多性能优势。
Ad Hoc SQL只需打开一个控制台窗口到您的DBMS并键入一个SQL语句。 过去,由于DBMS无法像参数化/存储过程方法那样预先优化查询,所以这些是“最糟糕的”performance(平均)。
仍然是一个缺点? 不必要。 大多数DBMS有能力将特定的SQL“抽象”为参数化的版本,从而或多或less地否定了两者之间的差异。 有些是隐含的,或者必须使用命令设置(SQL服务器: http : //msdn.microsoft.com/en-us/library/ms175037.aspx,Oracle : http : //www.praetoriate.com/oracle_tips_cursor_sharing。 htm )。
得到教训? 摩尔定律继续前进,DBMS优化器在每一个版本中都变得更加复杂。 当然,您可以将每一个愚蠢的小SQL语句放在一个存储过程中,但只要知道优化器上的程序员非常聪明,并且不断寻找提高性能的方法。 最终(如果它已经不在这里了)特别的SQL性能将与存储过程的性能无法区分(平均而言),所以任何types的海量存储过程**仅仅用于“性能原因”**肯定听起来像是我过早的优化。
无论如何,我认为如果你避免边缘情况,并有相当的香草SQL,你不会注意到特设和存储过程之间的差异。
在很多情况下,存储过程实际上比较慢,因为它们更加通用。 虽然存储过程可以高度调整,但根据我的经验,有足够的发展和体制上的摩擦,一旦他们工作,他们留在原地,所以存储过程往往会返回很多列“以防万一” – 因为你不每次更改应用程序时都想要部署新的存储过程。 另一方面,OR / M只请求应用程序正在使用的列,这减less了networkingstream量,不必要的连接等。
使用存储过程的原因:
- 减lessnetworkingstream量 – 您必须通过networking发送SQL语句。 使用sprocs,您可以批量执行SQL,这也更有效率。
- caching查询计划 – 第一次执行sproc时,SQL Server会创build一个执行计划,该计划被caching以供重用。 这对于频繁运行的小型查询尤其有效。
- 能够使用输出参数 – 如果您发送返回一行的内联SQL,则只能取回logging集。 随着sprocs你可以得到他们作为输出参数,这是相当快。
- 权限 – 当你发送内联SQL时,你必须授予用户权限,授予更多的权限,而不仅仅是授予执行权限的权限
- 逻辑分离 – 删除SQL生成代码并将其分离到数据库中。
- 无需重新编译即可编辑 – 这可能是有争议的。 您可以在sproc中编辑SQL,而无需重新编译应用程序。
- 查找表的使用位置 – 使用sprocs,如果要查找引用特定表的所有SQL语句,可以导出该代码并search它。 这比在代码中find它容易得多。
- 优化 – 当使用sprocs时,DBA更容易优化SQL并调整数据库。 find缺失的索引等很容易。
- SQL注入攻击 – 正确写入的内联SQL可以抵御攻击,但sprocs更适合这种保护。
这是一场激烈的争论(例如, 这里 )。
编写错误的存储过程就像在应用程序中编写错误的数据访问逻辑一样简单。
我的select是Stored Procs,但是这是因为我通常在企业环境中处理非常庞大而复杂的应用程序,这些环境中有专门的DBA负责保持数据库服务器的良好运行。
在其他情况下,我很满意LINQ等数据访问技术来处理优化。
纯粹的performance不是唯一的考虑,但。 安全和configurationpipe理等方面通常至less同样重要。
编辑:虽然Frans Bouma的文章确实是冗长的,但却忽视了一英里的安全性。 5岁的事实也不利于其相关性。
存储过程与大多数现代数据库的参数化或准备查询没有明显的速度差异,因为数据库也会caching这些查询的执行计划。
请注意,参数化查询与ad hoc sql不同。
仍然支持存储过程的主要原因是安全问题。 如果您独占使用存储过程,则可以禁用应用程序用户的INSERT,SELECT,UPDATE,DELETE,ALTER,DROP和CREATE等权限,而仅使用EXECUTE。
这为二阶 SQL注入提供了一点额外的保护。 参数化查询只能防止第一次订单注入。
显然,实际performance应该在个别情况下进行衡量,而不是假设。 但是即使性能受到存储过程的阻碍 ,也有充分的理由使用它们:
-
应用程序开发人员并不总是最好的SQL编码器。 存储过程隐藏了应用程序中的SQL。
-
存储过程自动使用绑定variables。 应用程序开发人员通常会避免绑定variables,因为他们看起来像不必要的代码,在小型testing系统中显示出很小的好处 稍后,未能使用绑定variables可能会限制RDBMS的性能。
-
存储过程创build一个间接层,稍后可能会有用。 可以在数据库端更改实现细节(包括表结构),而无需接触应用程序代码。
-
创build存储过程的练习可用于logging系统的所有数据库交互。 当事情发生变化时,更新文档会更容易。
也就是说,我通常会在应用程序中粘贴原始SQL,以便自己控制它。 这取决于你的开发团队和理念。
没有人提到存储过程的好处的一个主题是安全性。 如果通过存储过程专门构build数据访问应用程序,则可以locking数据库,以便通过这些存储过程进行唯一访问。 因此,即使有人获得数据库ID和密码,他们也会受限于他们可以看到或对数据库执行的操作。
我更喜欢在使用SP的时候使用SP。 无论如何,在SQL Server中,通过参数化查询,SP没有性能优势。
但是,在我目前的工作中,我的老板提到我们被迫使用SP,因为我们的客户需要他们。 他们觉得他们更安全。 我在这里没有足够长的时间来看看我们是否正在实施基于angular色的安全,但是我有一种感觉,
所以在这种情况下,客户的感受胜过所有其他的论点。
在2007年,我在一个项目中,我们通过ORM使用MS SQL Server。 我们有两个大的增长表,在SQL Server上占用7-8秒的加载时间。 在完成2个大的SQL存储过程之后,从查询规划器中优化它们,每个DB加载时间缩短到不到20毫秒,因此使用存储的SQL过程仍然是有效的。
话虽如此,我们发现存储过程的最重要的好处是增加了维护简便性,安全性,数据完整性和将业务逻辑从中间件逻辑中解耦出来,使所有中间件逻辑都能够重用这两个过程。
我们的ORM供应商做出了通常的说法,即引发许多小型的SQL查询将比获取大型的连接数据集更有效率。 我们的经验(令我们惊讶)显示了其他的东西。
在机器,networking,操作系统,SQL服务器,应用程序框架,ORM框架和语言实现之间,这当然可能会有所不同,所以要衡量一下你从其他方面得到的好处。
直到我们进行基准testing,我们才发现问题出在ORM和数据库之间,承担了所有的负担。
阅读弗朗斯·鲍马的优秀post (如果有点偏见)。
我只能说的是SQL服务器。 在这个平台上,存储过程是可爱的,因为服务器存储执行计划,在大多数情况下,性能提高了一点点。 我说“在大多数情况下”,因为如果SP具有广泛不同的执行path,您可能会获得不理想的性能。 但是,即使在这些情况下,SP的一些开明的重构也会加快速度。
对我来说,存储过程的一个优点是与主机语言无关:您可以从C,Python,PHP或任何应用程序切换到另一种编程语言,而无需重写代码。 另外,像批量操作这样的一些function可以提高真正的性能,并且不容易在主机语言中使用(根本不可用)。
我不知道他们更快。 我喜欢使用ORM进行数据访问(不要重新发明轮子),但我意识到这并不总是一个可行的select。
Frans Bouma在这个问题上有一篇很好的文章: http : //weblogs.asp.net/fbouma/archive/2003/11/18/38178.aspx
存储过程非常适合SQL代码频繁运行的情况,因为数据库将其存储在内存中。 如果您反复在存储过程之外运行相同的代码,您将会喜欢从数据库中反复重新分析相同的代码。
我通常经常将代码作为存储过程或SqlCommand(.NET)对象调用,并根据需要执行多次。
使用存储过程进行CRUD操作可能是矫枉过正,但这取决于所使用的工具以及您自己的偏好(或要求)。 我更喜欢内联SQL,但我确保使用参数化查询来防止SQL注入攻击。 我把这个xkcd漫画打印出来,作为提醒,如果你不小心的话会出错。
当您使用多组数据来返回一组数据时,存储过程可以带来真正的性能优势。 在存储过程中处理数据通常比在客户端通过networking发送数据更高效。
是的,他们大部分时间都比较快 SQL组合也是一个巨大的性能调优领域。 如果我正在做一个后台types的应用程序,我可能会跳过他们,但任何生产面临我使用他们的所有原因,其他人也说安全。
意识到这个问题有点偏离主题,但是如果你使用了大量的存储过程,确保有一个一致的方法把它们放在某种源代码控制下(例如,subversion或者git),并且能够将更新从开发系统迁移到testing系统到生产系统。
当这是手工完成的,无法轻易地审计哪些代码是在哪里,这很快就变成了一场噩梦。
恕我直言…
限制存储过程的“C_UD”操作可以将数据完整性逻辑保存在一个地方。 这也可以通过将“C_UD”操作限制到单个中间件层来完成。
读操作可以提供给应用程序,以便他们只能连接他们需要的表/列。
存储过程也可以用来代替参数化查询(或即席查询)来获得其他一些优点:
- 如果你需要纠正一些事情(sorting顺序等),你不需要重新编译你的应用程序
- 您可以拒绝访问该用户帐户的所有表,只允许访问存储过程并通过存储过程路由所有访问。 这样,您可以对所有input进行自定义validation,比表约束更灵活。
networkingstream量减less – SP通常比dynamicSQL差。 因为人们不会为每个select创build一个新的SP,所以如果只需要一列,就会使用具有所需列的SP,而忽略其余部分。 获得额外的专栏,减less刚刚离开的networking使用量。 当你使用SP的时候,你也往往会有很多的客户端过滤。
caching – MS-SQL不会以任何不同的方式处理它们,不是因为MS-SQL 2000可能是7,但我不记得。
权限 – 这不是一个问题,因为几乎我所做的每件事情都是networking或者有一些中间应用程序层来完成所有数据库访问。 我唯一使用的直接客户端到数据库访问的软件是第三方产品,专为用户devise,可以直接访问并基于给予用户权限。 是的MS-SQL权限安全模型SUCKS! (在2008年还没有花费时间)作为最后一部分,我们希望看到一个调查,有多less人仍然在做直接的客户端/服务器编程与Web和中间应用服务器编程; 如果他们正在做大项目,为什么没有ORM。
分离 – 人们会质疑为什么你把业务逻辑放在中间层之外。 另外,如果您正在寻找独立的数据处理代码,有办法做到这一点,而不是把它放在数据库中。
编辑能力 – 你没有testing和版本控制,你不必担心? 也只有客户端/服务器的问题,在networking世界没有问题。
find表 – 只有当你能够识别使用它的SP时,才会坚持使用版本控制系统的工具,代理快速search或者visual studio来查找。
优化 – 您的DBA应该使用数据库的工具来查找需要优化的查询。 数据库可以告诉DBA哪些语句最多时间和资源在说话,他们可以从那里修复。 对于复杂的SQL语句,程序员应该被告知与DBA交谈,如果简单的select不担心的话。
SQL注入攻击 – SP提供没有更好的保护。 他们得到的唯一一点是,他们中的大多数都使用参数来教导vsdynamicSQL,大多数例子都忽略了参数。