2012-02-09 10 views
14

Mam aplikację C#, która wstawia dane do tabeli programu SQL Server (2008) za pomocą procedury przechowywanej. Używam wielu wątków, aby to zrobić. Procedura przechowywana jest wywoływana z poziomu wątku. Teraz moja procedura przechowywana używa "tablock" podczas wstawiania danych. Podczas wykonywania tego kodu pojawia się następujący błąd: "Transakcja (identyfikator procesu) została zakleszczona na zasobach blokujących innym procesem i została wybrana jako ofiara zakleszczenia. Ponownie uruchom transakcję."Transakcja (identyfikator procesu) została zablokowana na zasobach blokujących innym procesem i została wybrana jako ofiara zakleszczenia. Ponownie uruchom transakcję

Czy ktoś może mi pomóc w rozwiązaniu tego problemu?

+2

impas w SQL Server, trzeba będzie dodać w swoim pytaniu zapisany kod procedury, jak również powiązanych część swojego schematu DB. – ken2k

Odpowiedz

12

Występuje, gdy dwa procesy serwera Sql uzyskują dostęp do tych samych zasobów, ale w innej kolejności. Dlatego też czekają na drugi proces, który jest impasem.

Istnieje wiele sposobów, aby temu zapobiec, w tym:

  • Unikaj zbędnych blokad. Sprawdź poziom izolacji transakcji wymagany dla zapytania, w razie potrzeby użyj wskazówki blokowania dla zapytań.
  • Upewnij się, że podczas robienia zamków blokujesz przedmioty w tej samej kolejności w każdym zapytaniu.

Np. jeśli Proc1 blokuje table1, a następnie table2, ale Proc2 blokuje table2, a następnie table1, problem może się pojawić. Możesz przepisać albo proc, aby wykonać blokady w tej samej kolejności, aby uniknąć tego problemu.

2

można upakować zapytanie w bloku CATCH próbować, i goni numery błędów (związanych z zamkami)

  1. 1204
  2. 1205
  3. 1222

Następnie można zautomatyzować ponownych prób , aż do pewnej liczby. Więc zrobiłbyś coś takiego:

  DECLARE @RetryNo Int = 1 
    ,@RetryMaxNo Int = 5; 
    WHILE @RetryNo < @RetryMaxNo 
     BEGIN 
     BEGIN TRY 

     -- put your query that generates locks here.... 

      SELECT @RetryNo = @RetryMaxNo; 
     END TRY 
     BEGIN CATCH 
      IF ERROR_NUMBER() IN (1204, 1205, 1222) 
       BEGIN 
        SET @RetryNo += 1; 
        -- it will wait for 10 seconds to do another attempt 
        WAITFOR DELAY '00:00:10'; 
       END 
      ELSE 
       THROW; 
     END CATCH 
     END 

Można również użyć wskazówek dotyczących tabeli, takich jak UPDLOCK.

+0

Dlaczego używasz 'SELECT @RetryNo = @RetryMaxNo;' zamiast 'BREAK;'? – Storm

+0

Ponieważ w tym momencie nie chcemy, aby się nie udało, chcemy powtórzyć proces i dać mu maksymalną liczbę prób, zanim wyrzucimy wyjątek (jeśli jest to związane z przekroczeniem limitu czasu). – Mez

+0

Dlaczego "BREAK" spowodowałby awarię? Nie wyrzuci wyjątku, ponieważ już minął dowolny kod, który mógłby spowodować jego awarię, więc 'BREAK' po prostu zwiąże pętlę. – Storm

0

można korzystać z obiektu Blokada

 static object _lock = new object(); 
    public static void _main() 
    { 
      lock (_lock) 
      { 
       _bulkcopy(myData); 
      } 
    } 
    public static void _bulkcopy(DataTable dt) 
    { 
     try 
     { 
      using (var connection = new SqlConnection(ConfigurationSettings.AppSettings.Get("DBConnection"))) 
      { 
       connection.Open(); 
       SqlTransaction transaction = connection.BeginTransaction(); 

       using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction)) 
       { 
        bulkCopy.BatchSize = 100; 
        bulkCopy.DestinationTableName = "dbo.MyTable"; 
        try 
        { 
         bulkCopy.WriteToServer(dt); 
        } 
        catch (Exception) 
        { 
         transaction.Rollback(); 
         connection.Close(); 
        } 
       } 

       transaction.Commit(); 
      } 




     } 
     catch { } 
    } 
Powiązane problemy