J'ai une application en cours d'exécution sur mon serveur. Le problème avec cette application est que chaque jour, je reçois près de 10 à 20, System.Data.SqlClient.SqlException Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding
, un seul de mes SP. Voici mon SP,
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) ,@Longitude) + ' ' + CONVERT(VARCHAR(100), @Latitude) + ')', 4326)
,@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
Y at-il un problème avec ce SP? Pourquoi j'obtiens une exception Timeout uniquement dans ce SP? Voici la trace de la pile,
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, RunBehavior runBehavior, Boolean returnStream, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, 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, Boolean storedProcedure, 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)
Vous devez étudier cela côté serveur pour comprendre pourquoi le délai d’exécution est dépassé. Notez que le serveur n'a pas de délai d'expiration, ce délai est causé par le délai par défaut de 30 secondes sur SqlCommand.CommandTimeout
.
Une bonne ressource est Waits and Queues , qui est une méthodologie permettant de diagnostiquer les goulots d'étranglement des performances avec SQL Server. En fonction de la cause réelle du délai, une action appropriée peut être entreprise. Vous devez avant tout établir si vous avez affaire à une exécution lente (un mauvais plan) ou à un blocage.
Si je me permettais de deviner, je dirais que le motif malsain de IF EXISTS... UPDATE
en est la cause fondamentale. Ce modèle est incorrect et entraînera des échecs en simultanéité. Deux transactions simultanées exécutant simultanément le IF EXISTS
aboutiront à la même conclusion et les deux tenteront de INSERT
ou UPDATE
. En fonction des contraintes existantes dans la base de données, vous pouvez vous retrouver avec une impasse (le cas chanceux) ou avec une écriture perdue (le cas malheureux). Cependant, seule une enquête appropriée révélerait la cause profonde réelle. Cela pourrait être quelque chose de totalement différent, comme événements à croissance automatique .
Votre procédure traite également de manière incorrecte le bloc CATCH. Vous devez toujours vérifier le XACT_STATE()
_, car la transaction peut déjà être annulée au moment de l'exécution de votre bloc CATCH. On ne sait pas non plus clairement ce que vous attendez de la dénomination de la transaction. C’est une erreur courante que je vois souvent associée à des transactions avec nom pointues qui prêtent à confusion. Pour un modèle correct, voir Gestion des exceptions et transactions imbriquées } _.
Modifier
Voici un moyen possible d’enquêter sur ceci:
CommandTimeout
approprié par 0 (c'est-à-dire infini).blocked process threshold
, définissez-le sur 30 secondes (l'ancien CommandTimeout)Ces actions provoqueront un événement de «rapport de processus bloqué» à chaque fois que vous obtiendrez un délai d'expiration, si le délai a été causé par le blocage. Votre application continuera à attendre jusqu'à ce que le blocage soit supprimé. Si le blocage est provoqué par un live-lock , il attendra indéfiniment.
Ajoutez cette ligne à votre chaîne de connexion:
Connect Timeout=200; pooling='true'; Max Pool Size=200
Vous pouvez également définir myCom.CommandTimeout = 200
Si un grand nombre de données existe, vous pouvez également augmenter le délai d'attente de 200 à 600 secondes.
Modifiez également ceci dans web.config.
Suivez THIS doccument.
Cela peut arriver à cause du paramètre reniflant. Il suffit donc d’utiliser des variables locales, déclarées dans le proc stocké. et les utiliser de manière appropriée.
Déclarez @ InVar1 ...
.....
où condition = @ Invar1
Peut-être que les astuces OPTIMIZE FOR ou WITH RECOMPILE résolvent le problème de délai d'exception SQL.
Cet article explique comment le mettre en œuvre et explique les problèmes liés au "reniflement des paramètres":