您是否认为在存储过程中始终围绕SQL语句进行事务是一种好习惯?我只是想在我的公司中优化这个遗留应用程序,我发现的一件事是每个存储过程都有BEGIN TRANSACTION
。即使是具有单个select或update语句的过程也只有一个。我认为如果执行多个动作,BEGIN TRANSACTION
会很好,但不仅仅是一个动作。我可能错了,这就是为什么我需要别人来建议我。谢谢你的时间,伙计们。
答案 0 :(得分:4)
完全没必要,因为每个SQL语句都以原子方式执行,即。就好像它已经在自己的交易中运行一样。事实上,打开不必要的事务可能会导致锁定增加,甚至死锁。只要与数据库的连接打开并且干扰同一连接中的其他事务,忘记将COMMIT与BEGIN匹配就可以使事务处于打开状态。
这样的编码几乎肯定意味着编写代码的人在数据库编程方面不是很有经验,并且确实存在可能存在其他问题的气味。
答案 1 :(得分:3)
我不知道不仅仅为这些语句使用自动提交事务有任何好处。
在任何地方使用显式事务的可能缺点可能是它只会增加代码的混乱,因此在使用显式事务确保多个语句的正确性时,不太容易看到。
此外,除非小心谨慎,否则会增加交易保持锁定的风险(例如,使用SET XACT_ABORT ON)。
还有次要性能影响,如@8kb's answer所示。这说明了使用visual studio profiler的另一种方式。
(针对空表进行测试)
CREATE TABLE T (X INT)
SET NOCOUNT ON
DECLARE @X INT
WHILE ( 1 = 1 )
BEGIN
BEGIN TRAN
SELECT @X = X
FROM T
COMMIT
END
SET NOCOUNT ON
DECLARE @X INT
WHILE ( 1 = 1 )
BEGIN
SELECT @X = X
FROM T
END
它们最终都花费在CMsqlXactImp::Begin
和CMsqlXactImp::Commit
上的时间,但对于显式交易案例,它在这些方法中花费了更大比例的执行时间,从而减少了做有用工作的时间。 / p>
+--------------------------------+----------+----------+
| | Auto | Explicit |
+--------------------------------+----------+----------+
| CXStmtQuery::ErsqExecuteQuery | 35.16% | 25.06% |
| CXStmtQuery::XretSchemaChanged | 20.71% | 14.89% |
| CMsqlXactImp::Begin | 5.06% | 13% |
| CMsqlXactImp::Commit | 12.41% | 24.03% |
+--------------------------------+----------+----------+
答案 2 :(得分:2)
我能看到的唯一可能原因是,如果您因SQL失败以外的原因需要回滚事务。
但是,如果代码是字面意思
begin transaction
statement
commit
然后我认为没有理由使用显式事务,而且可能是因为it's always been done that way而完成的。
答案 3 :(得分:2)
一个优点是你可以添加另一个INSERT(例如),它已经安全了。
然后,如果存储过程调用另一个存储过程,您还会遇到嵌套事务的问题。内部回滚将导致错误266。
如果每次调用都是简单的CRUD而没有嵌套那么它就没有意义了:但是如果你在TXN之前嵌套或有多次写入,那么拥有一致的模板是件好事。
答案 4 :(得分:2)
您提到您将优化此旧版应用。
为提高性能,您可以做的第一件也是最简单的事情就是删除所有只执行SELECT的存储过程的BEGIN TRAN和COMMIT TRAN。
这是一个简单的测试来证明:
/* Compare basic SELECT times with and without a transaction */
DECLARE @date DATETIME2
DECLARE @noTran INT
DECLARE @withTran INT
SET @noTran = 0
SET @withTran = 0
DECLARE @t TABLE (ColA INT)
INSERT @t VALUES (1)
DECLARE
@count INT,
@value INT
SET @count = 1
WHILE @count < 1000000
BEGIN
SET @date = GETDATE()
SELECT @value = ColA FROM @t WHERE ColA = 1
SET @noTran = @noTran + DATEDIFF(MICROSECOND, @date, GETDATE())
SET @date = GETDATE()
BEGIN TRAN
SELECT @value = ColA FROM @t WHERE ColA = 1
COMMIT TRAN
SET @withTran = @withTran + DATEDIFF(MICROSECOND, @date, GETDATE())
SET @count = @count + 1
END
SELECT
@noTran / 1000000. AS Seconds_NoTransaction,
@withTran / 1000000. AS Seconds_WithTransaction
/** Results **/
Seconds_NoTransaction Seconds_WithTransaction
--------------------------------------- ---------------------------------------
14.23600000 18.08300000
您可以看到与交易相关的明确开销。
注意:这是假设您的这些存储过程没有使用任何特殊的隔离级别或锁定提示(用于处理悲观并发)。在那种情况下,你会想要保留它们。
因此,为了回答这个问题,我只会留下您正在尝试保留数据修改完整性的事务,以防代码,SQL Server或硬件出现错误。
答案 5 :(得分:1)
我只能说将这样的事务块放到每个存储过程中可能是新手的工作。
事务应该只放在具有多个insert / update语句的块中,除此之外,不需要在存储过程中放置事务块。
答案 6 :(得分:1)
执行多次插入/更新/删除时,最好有一个事务来确保操作的原子性,并确保执行所有操作任务或不执行任何操作。
对于单个插入/更新/删除语句,它取决于您执行的操作类型(从业务层角度来看)以及它的重要性。如果您在单次插入/更新/删除之前执行某些计算,那么更好地使用事务,可能是在检索插入/更新/删除数据后更改了一些数据。
答案 7 :(得分:1)
默认情况下,不应在每个存储过程中使用BEGIN TRANSACTION / COMMIT语法,除非您尝试涵盖以下方案:
您包含WITH MARK选项,因为您希望支持将数据库从备份还原到特定时间点。
您打算将代码从SQL Server移植到另一个数据库平台,如Oracle。默认情况下,Oracle不提交事务。