ALTER PROCEDURE [dbo].[Insertorupdatedevicecatalog] (@OS NVARCHAR(50),@UniqueID VARCHAR(500),@Longitude FLOAT,@Latitude FLOAT,@Culture VARCHAR(10),@Other NVARCHAR(200),@IPAddress VARCHAR(50),@NativeDeviceID VARCHAR(50)) AS BEGIN DECLARE @OldUniqueID VARCHAR(500) = '-1'; SELECT @OldUniqueID = [UniqueID] FROM DeviceCatalog WHERE (@NativeDeviceID != '' AND [NativeDeviceID] = @NativeDeviceID); BEGIN TRANSACTION [Tran1] BEGIN TRY IF EXISTS(SELECT 1 FROM DeviceCatalog WHERE [UniqueID] = @UniqueID) BEGIN UPDATE DeviceCatalog SET [OS] = @OS,[Location] = geography::STGeomFromText('POINT(' + CONVERT(VARCHAR(100 ),@Longitude) + ' ' + CONVERT(VARCHAR(100),@Latitude) + ')',4326),[Culture] = @Culture,[Other] = @Other,[Lastmodifieddate] = Getdate(),[IPAddress] = @IPAddress WHERE [UniqueID] = @UniqueID; END ELSE BEGIN INSERT INTO DeviceCatalog ([OS],[UniqueID],[Location],[Culture],[Other],[IPAddress],[NativeDeviceID]) VALUES (@OS,@UniqueID,geography::STGeomFromText('POINT(' + CONVERT(VARCHAR(100),@Culture,@Other,@IPAddress,@NativeDeviceID); IF(@OldUniqueID != '-1' AND @OldUniqueID != @UniqueID) BEGIN EXEC DeleteOldDevice @OldUniqueID,@UniqueID; END END COMMIT TRANSACTION [Tran1]; END TRY BEGIN CATCH ROLLBACK TRANSACTION [Tran1]; DECLARE @ErrorNumber nchar(5),@ErrorMessage nvarchar(2048); SELECT @ErrorNumber = RIGHT('00000' + ERROR_NUMBER(),5),@ErrorMessage = @ErrorNumber + ' ' + ERROR_MESSAGE(); RAISERROR (@ErrorMessage,16,1); END CATCH END
这个SP有什么问题吗?为什么我在这个SP中得到超时异常?这是Stack Trace,
System.Data.sqlClient.sqlException (0x80131904): Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. at System.Data.sqlClient.sqlConnection.OnError(sqlException exception,Boolean breakConnection) at System.Data.sqlClient.TdsParser.ThrowExceptionAndWarning() at System.Data.sqlClient.TdsParser.Run(RunBehavior runBehavior,sqlCommand cmdHandler,sqlDataReader dataStream,BulkCopySimpleResultSet bulkCopyHandler,TdsParserStateObject stateObj) at System.Data.sqlClient.sqlCommand.FinishExecuteReader(sqlDataReader ds,RunBehavior runBehavior,String resetOptionsString) at System.Data.sqlClient.sqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior,Boolean returnStream,Boolean async) at System.Data.sqlClient.sqlCommand.RunExecuteReader(CommandBehavior cmdBehavior,String method,DbAsyncResult result) at System.Data.sqlClient.sqlCommand.InternalExecuteNonQuery(DbAsyncResult result,String methodName,Boolean sendToPipe) at System.Data.sqlClient.sqlCommand.ExecuteNonQuery() at App.Classes.DBLayer.Execute(sqlCommand command,Boolean executeNonQuery) at App.Helpers.sqlHelper.GetResult(List`1 parameters,Boolean storedProcedure,String commandText,ResultType type) at App.Helpers.sqlHelper.ExecuteNonQuery(List`1 parameters,String commandText) at App.Services.DeviceCatalogService.InsertOrUpdateDeviceCatalog(DeviceCatalog deviceCataLog) at WebApplication1.Handlers.RegisterDevice.ProcessRequest(HttpContext context) at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step,Boolean& completedSynchronously)
解决方法
SqlCommand.CommandTimeout
默认30秒引起的.
一个很好的资源是Waits and Queues,它是一种诊断sql Server性能瓶颈的方法.根据超时的实际原因,可以采取适当的措施.您必须首先确定您是否处理缓慢的执行(坏计划)或阻止.
如果我冒昧猜测,我会说IF IFISIS UPDATE的不正常模式是根本原因.此模式不正确并将导致并发故障.同时执行IF EXISTS的两个并发事务将同时得出相同的结论,并尝试进行INSERT或UPDATE.根据数据库中的现有限制,您可能会遇到死锁(幸运案例)或丢失的写入(不幸的情况).但是,只有适当的调查才能揭示实际的根本原因.可能是一个完全不同的东西,比如auto-growth events.
您的程序也错误地处理CATCH块.您必须始终检查XACT_STATE()
,因为您的CATCH块运行时可能已经回滚事务.也不清楚您对命名事务的期望,这是我常常遇到的常见错误,常常与命名事务与保存点混淆.对于正确的模式,请参阅Exception Handling and Nested Transactions.
编辑
这是一种可能的方式来调查这一点:
>将相关CommandTimeout
更改为0(即无限大).
>启用blocked process threshold
,设置为30秒(前一个CommandTimeout)
>在Profiler中监视Blocked Process Report Event
>开始你的工作量
>查看Profiler是否生成任何报告事件.如果这样做,他们会确定原因.
如果超时是由于阻塞引起的,那么每次收到超时时,这些操作将导致“阻止进程报告”事件.您的应用程序将继续等待,直到阻止被移除,如果阻塞是由live-lock造成的,那么它将永远等待.