Jaka jest najlepsza metoda dodawania obsługi błędów w przechowywanych procesach SQL 2005?

11

Jaki jest dobry sposób, aby przechowywane procesory były wystarczająco solidne, aby mogły bardzo dobrze skalować, a także zawierać obsługę błędów?

Ponadto, jaki jest najlepszy sposób radzenia sobie z wieloma scenariuszami błędów w przechowywanym proc i posiadania inteligentnego systemu informacji zwrotnej, który zwróci istotne informacje o błędach do aplikacji wywołujących?

kacalapy
źródło
2
Spróbuj użyć nowszego bloku TRY CATCH w SQL Server 2005. sommarskog.se/error_handling_2005.html
Sankar Reddy
Cześć @Kalalapy ~ Chciałbym w przyszłości polecić zadawanie każdego pytania osobno, w ten sposób możemy uzyskać konkretne odpowiedzi skupione na jednym pytaniu na raz. Zachęcam do zrobienia tego z tym pytaniem.
jcolebrand

Odpowiedzi:

12

Alex Kuznetsov ma świetny rozdział w swojej książce Defensive Database Programming (rozdział 8), który obejmuje T-SQL TRY ... CATCH, transakcje T-SQL i ustawienia SET XACT_ABORT oraz korzystanie z obsługi błędów po stronie klienta. Pomoże ci to w podjęciu decyzji, która z opcji ma największy sens w tym, co musisz osiągnąć.

Jest dostępny za darmo na tej stronie . Nie jestem w żaden sposób związany z firmą, ale posiadam wersję papierową tej książki.

Istnieje wiele drobnych szczegółów na ten temat, które Alex bardzo dobrze wyjaśnia.

Na prośbę Nicka ... (ale nie wszystko to znajduje się w rozdziale)

Jeśli chodzi o skalowanie, musisz być brutalnie uczciwy w kwestii tego, które działania muszą znajdować się w kodzie db, a które w aplikacji. Zauważyłeś kiedyś, jak szybko wykonywany kod powraca do projektowania pojedynczego problemu na metodę?

Najłatwiejszym sposobem komunikacji są niestandardowe kody błędów (> 50 000). Jest również dość szybki. Oznacza to, że musisz synchronizować kod db i kod aplikacji. Za pomocą niestandardowego kodu błędu można również zwrócić przydatne informacje w ciągu komunikatu o błędzie. Ponieważ masz kod błędu ściśle na tę sytuację, możesz napisać analizator składni w kodzie aplikacji dostosowanym do formatu danych błędu.

Jakie warunki błędu wymagają logiki ponownej próby w bazie danych? Jeśli chcesz ponowić próbę po X sekundach, lepiej jest zrobić to w kodzie aplikacji, aby transakcja nie blokowała tak dużo. Jeśli od razu przesyłasz tylko operację DML, powtarzanie jej w SP może być bardziej wydajne. Pamiętaj jednak, że będziesz musiał zduplikować kod lub dodać warstwę SP, aby ponowić próbę.

Naprawdę, to jest obecnie największy problem z TRY ... logiką CATCH w SQL Server w tej chwili. Można to zrobić, ale to trochę nieudacznik. Spójrz na niektóre ulepszenia nadchodzące w tym programie SQL Server 2012, zwłaszcza ponowne zgłaszanie wyjątków systemowych (zachowując oryginalny numer błędu). Ponadto istnieje FORMATMESSAGE , który dodaje pewnej elastyczności w konstruowaniu komunikatów o błędach, szczególnie do celów rejestrowania.

Phil Helmer
źródło
Świetna rada i bardzo dobra książka!
Marian
Red Gate oferuje kilka niezwykle pomocnych darmowych e-książek, a ta z pewnością jest jedną z lepszych. Świetna sugestia.
Matt M.
Nie wszystkie ich książki to robią, ale darmowa wersja książki „Defensywne ...” Kuzniecowa nie zawiera 2 ostatnich rozdziałów na temat poziomów izolacji transakcji i opracowywania modyfikacji, które przetrwają współbieżność. Dla mnie. zawartość tam była warta zakupu.
Phil Helmer,
7

To jest nasz szablon (usunięto rejestrowanie błędów)

Uwagi:

  • Bez XACT_ABORT wszystkie TXN zaczynają się i zatwierdzanie / wycofywanie musi być sparowane
  • Dekret zatwierdzenia @@ TRANCOUNT
  • Wycofanie zwraca @@ TRANCOUNT na zero, aby otrzymać błąd 266
  • Nie można ZWRÓCIĆ TYLKO bieżącej warstwy (np. Zmniejszenie @@ TRANCOUNT przy wycofaniu)
  • XACT_ABORT pomija błąd 266
  • Każdy przechowywany proc musi być zgodny z tym samym szablonem, aby każde wywołanie miało charakter atomowy
  • Kontrola wycofania jest w rzeczywistości zbędna z powodu XACT_ABORT. Jednak sprawia, że ​​czuję się lepiej, bez niego wygląda dziwnie i pozwala na sytuacje, w których nie chcesz
  • Pozwala to na TXN po stronie klienta (jak LINQ)
  • Remus Rusanu ma podobną powłokę, która wykorzystuje punkty zapisu. Wolę atomowe wywołanie DB i nie używam częściowych aktualizacji, takich jak ich artykuł

... więc nie twórz więcej TXN niż potrzebujesz

Jednak,

CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON

DECLARE @starttrancount int

BEGIN TRY
    SELECT @starttrancount = @@TRANCOUNT

    IF @starttrancount = 0
        BEGIN TRANSACTION

       [...Perform work, call nested procedures...]

    IF @starttrancount = 0 
        COMMIT TRANSACTION
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 AND @starttrancount = 0 
        ROLLBACK TRANSACTION
    RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO
gbn
źródło
co jeśli @@ TRANCOUNT jest większy niż 0? nie wykonujesz żadnej pracy lub masz jakieś uwagi?
kacalapy
@kacalapy: nie ma czegoś takiego jak zagnieżdżona transakcja, więc nie rozpoczynamy kolejnego scribd.com/doc/49579859/33/Nested-Transactions-Are-Real
gbn
3

Używam Try / Catch, ale zbieram także jak najwięcej informacji i zapisuję je w dzienniku błędów PO wycofaniu. W tym przykładzie „LogEvent” to procedura składowana, która zapisuje do tabeli EventLog, zawierająca szczegółowe informacje o tym, co się wydarzyło. GetErrorInfo () to wywołanie funkcji, które zwraca dokładny komunikat o błędzie.

Gdy wystąpi błąd, informacje są gromadzone, procedura przeskakuje do sekcji obsługi błędów i wydaje wycofanie. Informacje są zapisywane w dzienniku, a następnie procedura kończy się.

Biorąc pod uwagę dodatkowe wywołania procedur / funkcji, wydaje się to nieco przesadzone. JEDNAK ta metoda jest niezwykle pomocna przy próbie debugowania problemu.

exec LogEvent @Process, @Database, „Próba wstawienia bla bla bla”
ROZPOCZNIJ SPRÓBUJ
  wstaw do MyTable
  wybierz Wartości
    z MyOtherTable

  wybierz @rowcount = @@ ROWCOUNT
KONIEC SPRÓBUJ
- Obsługa błędów
ROZPOCZNIJ POŁOW
  wybierz @error = ERROR_NUMBER (),
         @rowcount = -1,
         @TableAction = 'insert',
         @TableName = @Database + '.MyTable',
         @AdditionalInfo = '(Próba wstawienia bla bla bla)' + dbo.GetErrorInfo ()
   GOTO TableAccessError
ZAKOŃCZ POŁOW

.
.
.
.

TableAccessError:
JEŻELI (@@ TRANCOUNT> 0) ROLLBACK
wybierz @wyjście = górne (@TableAction) + 
       „BŁĄD - Wystąpił błąd podczas„ + 
       case (@TableAction)
         kiedy „aktualizacja” to „aktualizacja”
         kiedy „usuń”, a następnie „usuń”
         else @TableAction + 'ing'
       koniec + 
       „rekordy” + 
       case (@TableAction) 
         kiedy „wybierz”, a następnie z 
         kiedy „aktualizacja”, a następnie „w” 
         kiedy „wstaw”, a następnie „do”
         else „from”   
         koniec + 
         „tabela„ + @TableName + ”.”
wybierz @wyjście = @wyjście + „@@ BŁĄD:” + konwersja (varchar (8), @ błąd) 
wybierz @wyjście = @wyjście + '@@ ROWCOUNT:' + konwersja (varchar (8), @ rowcount) 

wybierz @output = @output + isnull (@AdditionalInfo, '')
exec LogEvent @Process, @Database, @Output
RAISERROR (@ wyjście, 16,1) z logiem
wybierz @ReturnCode = -1
GOTO THE_EXIT


datagod
źródło