web-dev-qa-db-fra.com

Obtention occasionnelle de SqlException: expiration du délai

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)
19
user960567

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:

  1. Remplacez le CommandTimeout approprié par 0 (c'est-à-dire infini).
  2. Activer le blocked process threshold , définissez-le sur 30 secondes (l'ancien CommandTimeout)
  3. Surveiller dans le profileur pour événement de rapport de processus bloqué
  4. Commencez votre charge de travail
  5. Voir si le profileur génère des événements de rapport. Si c'est le cas, ils en identifieront la cause.

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.

25
Remus Rusanu

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.

8
Freelancer

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

1
Rahul Vadhan

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":

https://blogs.msdn.Microsoft.com/robinlester/2016/08/10/improving-query-performance-with-option-recompile-constant-folding-and-avoiding-parameter-sniffing-issues/

1
Edu Pelais