保存事務:名稱是否是儲存過程的本地名稱?
在生產伺服器上發生了一些腳射。我解決了這個問題,但我現在很困惑。
有一部分儲存過程在失敗時保留狀態。也就是說,如果過程引發錯誤,它無論如何都可以安全地送出。這是用
save transaction
.現在,我最初的理解是,您為事務設置的保存點名稱對於使用它的儲存過程來說是本地的。而在嵌套場景中,如果內部儲存過程同名保存,其實是不同的名字,一切正常。
為了確保我的這種理解是正確的,我設置了一個測試案例:
create table dbo.[footest] ( v varchar(50) NULL ); go insert into dbo.footest(v) values ('Nothing'); go create procedure dbo.[foo_test_tran_inner] as begin set nocount on; declare @foo int; begin tran; update dbo.footest set v = 'Inner, before savepoint'; save tran the_constant_name; begin try update dbo.footest set v = 'Inner, after savepoint'; set @foo = 1/0; end try begin catch rollback tran the_constant_name; end catch; commit tran; set @foo = 1/0; return 0; end; go create procedure dbo.[foo_test_tran_outer] as begin set nocount on; begin tran; update dbo.footest set v = 'Outer, before savepoint'; save tran the_constant_name; begin try update dbo.footest set v = 'Outer, after savepoint'; exec dbo.foo_test_tran_inner; end try begin catch rollback tran the_constant_name; end catch; commit tran; return 0; end; go
begin tran; exec dbo.foo_test_tran_outer; commit tran; select * from dbo.footest;
**這會產生“外部,在保存點之前”。**這意味著,保存點名稱是過程的本地名稱,並且在嵌套場景中被正確回滾。
在生產中,那些保持狀態的儲存過程正是這種模式。其中有更多程式碼,但如果你刪除它,只留下保存、回滾和送出,它將完全是上面顯示的內容。
但。它在生產中不起作用。相反,每個具有相同名稱的嵌套保存點似乎都覆蓋了先前的保存點,該保存點由呼叫過程創建。而當最外層的程式碼在收到異常後執行 a
commit
(相信內部回滾已正確完成)時,數據庫處於半螺旋狀態。如果應用於上面的範例,這將意味著選擇返回Inner, before savepoint
。我使用調試器逐步完成了生產過程中的每一步,並確認執行正確地完成了所有預期
save tran a_name
的和rollback tran a_name
.為了解決生產中的問題,我替換了這個:
save tran the_constant_name; ... rollback tran the_constant_name;
有了這個:
declare @savepoint varchar(32) = replace(newid(), '-', ''); save tran @savepoint; ... rollback tran @savepoint;
並立即修復它。
那麼給了什麼?保存點名稱是否是儲存過程的本地名稱?
如果是,那麼為什么生產程式碼做了它所做的事情,並且如圖所示成功修復了?
如果不是,那麼為什麼上面的測試範例會這樣做呢?
不,它們不是儲存過程的本地。
如果您在具有簡單恢復模型的數據庫中執行以下命令
checkpoint begin tran; exec dbo.foo_test_tran_outer; commit tran; select * from dbo.footest; select Operation, Context, [Savepoint Name], [Transaction Name] , Description from sys.fn_dblog(NULL,NULL)
它給出了這些結果
+--------------------+----------+-------------------+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Operation | Context | Savepoint Name | Transaction Name | Description | +--------------------+----------+-------------------+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | LOP_BEGIN_CKPT | LCX_NULL | NULL | NULL | | | LOP_END_CKPT | LCX_NULL | NULL | NULL | log_minRecoveryLsn 00000112:00001ce6:008e;log_replbeginlsn 00000000:00000000:0000;log_replnextlsn 00000000:00000000:0000;log_distbackuplsn 00000000:00000000:0000;log_distlastlsn 00000000:00000000:0000 | | LOP_BEGIN_XACT | LCX_NULL | NULL | user_transaction | user_transaction;0x010500000000000515000000007dfcff481d5bed8a729370ee030000 | | LOP_MODIFY_ROW | LCX_HEAP | NULL | NULL | | | LOP_MARK_SAVEPOINT | LCX_NULL | the_constant_name | NULL | | | LOP_MODIFY_ROW | LCX_HEAP | NULL | NULL | | | LOP_MODIFY_ROW | LCX_HEAP | NULL | NULL | | | LOP_MARK_SAVEPOINT | LCX_NULL | the_constant_name | NULL | | | LOP_MODIFY_ROW | LCX_HEAP | NULL | NULL | | | LOP_MODIFY_ROW | LCX_HEAP | NULL | NULL | COMPENSATION | | LOP_LOCK_XACT | LCX_NULL | NULL | NULL | COMPENSATION | | LOP_MODIFY_ROW | LCX_HEAP | NULL | NULL | COMPENSATION | | LOP_MODIFY_ROW | LCX_HEAP | NULL | NULL | COMPENSATION | | LOP_LOCK_XACT | LCX_NULL | NULL | NULL | COMPENSATION | | LOP_COMMIT_XACT | LCX_NULL | NULL | NULL | | +--------------------+----------+-------------------+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
事務
[Savepoint Name]
日誌中記錄的不包含任何與儲存過程相關的唯一標識符。如果刪除
rollback tran the_constant_name;
fromfoo_test_tran_inner
那麼最終選擇的結果將更改為Inner, before savepoint
顯示rollback tran the_constant_name;
執行的 fromfoo_test_tran_outer
只是回滾到該名稱的最新(未回滾)保存點並且不考慮儲存過程嵌套。