NOTE这是对存储过程的一般看法,未针对特定情况进行监管
数据库管理系统。一些 DBMS(甚至不同的
相同 DBMS 的版本!)可以运行
与此相反,所以你会想要
仔细检查您的目标 DBMS
在假设所有这些仍然成立之前。
近十年来,我一直断断续续地担任 Sybase ASE、MySQL 和 SQL Server DBA(以及 C、PHP、PL/SQL、C#.NET 和 Ruby 中的应用程序开发)。所以,我在这场(有时)圣战中没有什么特别的目的。
存储过程的历史性能优势通常来自以下方面(排名不分先后):
- 预解析 SQL
- 预先生成的查询执行计划
- 减少网络延迟
- 潜在的缓存优势
预解析 SQL-- 与编译代码和解释代码类似的好处,除了非常微观的层面上。
还有优势吗?在现代 CPU 上根本不明显,但是如果您每秒发送一个非常大的 SQL 语句 1100 亿次,则解析开销可能会增加。
预先生成的查询执行计划。
如果您有许多 JOIN,则排列可能会变得非常难以管理(现代优化器出于性能原因有限制和截止)。众所周知,非常复杂的 SQL 具有明显的、可测量的(在我们调整 DBMS 之前,我见过一个复杂的查询需要 10 多秒才能生成一个计划)延迟,因为优化器试图找出“接近最佳” ”执行计划。通常,存储过程会将其存储在内存中,这样您就可以避免这种开销。
还有优势吗?大多数 DBMS(最新版本)都会缓存单个 SQL 语句的查询计划,从而大大减少存储过程和即席 SQL 之间的性能差异。在某些情况下,情况并非如此,因此您需要在目标 DBMS 上进行测试。
此外,越来越多的 DBMS 允许您提供优化器路径计划(抽象查询计划)以显着减少优化时间(对于即席 SQL 和存储过程 SQL!!)。
WARNING缓存查询计划并不是性能的灵丹妙药。有时,生成的查询计划不是最佳的。
例如,如果您发送SELECT *
FROM table WHERE id BETWEEN 1 AND
99999999
,DBMS可以选择一个
全表扫描而不是索引
扫描,因为你正在抓取每一行
在表中(如此说
统计数据)。如果这是缓存的
版本,那么你就会变穷
稍后发送时的性能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
and http://www.simple-talk.com/sql/performance/execution-plan-basics/
“总而言之,他们确定
提供除
编译时的通用值或
执行重新编译导致
优化器编译和缓存
该特定的查询计划
价值。然而,当该查询计划是
重用于后续执行
对公共值的相同查询
(“M”、“R”或“T”),结果是
次优性能。这
次优性能问题
存在直到查询
重新编译。那时,基于
提供的@P1参数值,
查询可能有也可能没有
性能问题。”
减少网络延迟A) 如果您一遍又一遍地运行相同的 SQL,并且该 SQL 的代码加起来会达到许多 KB,那么用简单的“exec foobar”替换它确实可以加起来。
B) 存储过程可用于将过程代码移至 DBMS 中。这样可以避免将大量数据传输到客户端,而只是让它发送回少量信息(或者根本不发送信息!)。类似于在 DBMS 中与代码中进行 JOIN(每个人最喜欢的 WTF!)
还有优势吗?A) 现代 1Gb(以及 10Gb 及以上!)以太网确实使这一点可以忽略不计。
B) 取决于您的网络的饱和程度——为什么要无缘无故地来回传输几兆字节的数据?
潜在的缓存优势如果 DBMS 上有足够的内存并且您需要的数据位于服务器内存中,则执行服务器端数据转换可能会更快。
还有优势吗?除非您的应用程序具有对 DBMS 数据的共享内存访问权限,否则优势将始终是存储过程。
当然,如果不讨论参数化和即席 SQL,那么对存储过程优化的讨论就不完整。
参数化/准备好的 SQL
它们是存储过程和即席 SQL 之间的一种交叉,它们是使用“参数”作为查询值的主机语言中的嵌入式 SQL 语句,例如:
SELECT .. FROM yourtable WHERE foo = ? AND bar = ?
它们提供了更通用的查询版本,现代优化器可以使用它来缓存(和重用)查询执行计划,从而获得存储过程的大部分性能优势。
即席 SQL只需打开 DBMS 的控制台窗口并输入 SQL 语句即可。在过去,这些是“最差”的执行者(平均而言),因为 DBMS 无法像参数化/存储过程方法那样预先优化查询。
还是劣势?不必要。大多数 DBMS 都能够将临时 SQL“抽象”为参数化版本,从而或多或少地消除了两者之间的差异。有些是隐式执行此操作或必须使用命令设置启用(SQL 服务器:http://msdn.microsoft.com/en-us/library/ms175037.aspx,甲骨文:http://www.praetoriate.com/oracle_tips_cursor_sharing.htm).
得到教训?摩尔定律继续发展,DBMS 优化器在每个版本中都变得更加复杂。当然,您可以将每一个愚蠢的微小 SQL 语句放入存储过程中,但要知道,从事优化器工作的程序员非常聪明,并且不断寻找提高性能的方法。最终(如果还没有)临时 SQL 性能将与存储过程性能变得无法区分(平均而言!),因此任何类型的massive存储过程使用**仅出于“性能原因”**对我来说听起来确实是过早的优化。
无论如何,我认为如果您避免边缘情况并使用相当普通的 SQL,您将不会注意到临时过程和存储过程之间的差异。