Mam zapytanie odczytu, które wykonuję w ramach transakcji, aby określić poziom izolacji. Co mam zrobić po zakończeniu zapytania?
- Zatwierdź transakcję
- Wycofaj transakcję
- Nic nie rób (co spowoduje wycofanie transakcji na końcu using bloku)
Jakie są konsekwencje wykonania każdego z nich?
using (IDbConnection connection = ConnectionFactory.CreateConnection())
{
using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
{
using (IDbCommand command = connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandText = "SELECT * FROM SomeTable";
using (IDataReader reader = command.ExecuteReader())
{
// Read the results
}
}
// To commit, or not to commit?
}
}
EDYCJA: Nie chodzi o to, czy transakcja powinna zostać użyta, czy też istnieją inne sposoby ustawienia poziomu transakcji. Pytanie brzmi, czy ma znaczenie, że transakcja, która niczego nie modyfikuje, jest zatwierdzana lub wycofywana. Czy jest różnica w wydajności? Czy ma to wpływ na inne połączenia? Jakieś inne różnice?
sql
database
transactions
Stefan Moser
źródło
źródło
Odpowiedzi:
Zobowiązujesz się. Kropka. Nie ma innej rozsądnej alternatywy. Jeśli rozpocząłeś transakcję, powinieneś ją zamknąć. Zatwierdzenie zwalnia wszelkie blokady, które mogłeś mieć i jest równie sensowne z poziomami izolacji ReadUncommitted lub Serializable. Poleganie na niejawnym wycofywaniu zmian - choć może technicznie równoważne - jest po prostu kiepską formą.
Jeśli to cię nie przekonało, wyobraź sobie następnego faceta, który wstawia instrukcję aktualizacji w środku twojego kodu i musi wyśledzić niejawne wycofanie, które ma miejsce i usuwa jego dane.
źródło
Jeśli nic nie zmieniłeś, możesz użyć COMMIT lub ROLLBACK. Każda z nich zwolni wszystkie nabyte blokady odczytu, a ponieważ nie wprowadziłeś żadnych innych zmian, będą one równoważne.
źródło
Jeśli zaczynasz transakcję, najlepszą praktyką jest zawsze jej zatwierdzenie. Jeśli wyjątek zostanie zgłoszony w Twoim bloku użytkowania (transakcji), transakcja zostanie automatycznie wycofana.
źródło
IMHO może mieć sens zawijanie zapytań tylko do odczytu w transakcje, ponieważ (szczególnie w Javie) można powiedzieć, że transakcja ma być tylko do odczytu, co z kolei sterownik JDBC może rozważyć optymalizację zapytania (ale nie musi, więc nikt uniemożliwi Ci
INSERT
jednak wydanie ). Np. Sterownik Oracle całkowicie uniknie blokad tabeli w zapytaniach w transakcji oznaczonej jako tylko do odczytu, co zapewnia dużą wydajność w aplikacjach o dużym stopniu odczytu.źródło
Rozważ transakcje zagnieżdżone .
Większość systemów RDBMS nie obsługuje transakcji zagnieżdżonych lub próbuje emulować je w bardzo ograniczony sposób.
Na przykład w MS SQL Server wycofanie w transakcji wewnętrznej (która nie jest prawdziwą transakcją, MS SQL Server liczy tylko poziomy transakcji!) Spowoduje wycofanie wszystkiego, co wydarzyło się w transakcji zewnętrznej (która jest transakcją rzeczywistą).
Niektóre opakowania baz danych mogą traktować wycofanie w transakcji wewnętrznej jako znak, że wystąpił błąd i wycofać wszystko w transakcji zewnętrznej, niezależnie od tego, czy transakcja zewnętrzna została zatwierdzona, czy wycofana.
Tak więc COMMIT jest bezpiecznym sposobem, kiedy nie można wykluczyć, że twój komponent jest używany przez jakiś moduł oprogramowania.
Zwróć uwagę, że jest to ogólna odpowiedź na pytanie. Przykładowy kod sprytnie rozwiązuje problem z transakcją zewnętrzną, otwierając nowe połączenie z bazą danych.
Jeśli chodzi o wydajność: w zależności od poziomu izolacji, elementy SELECT mogą wymagać różnego stopnia blokad LOCK i danych tymczasowych (migawek). Jest to czyszczone po zamknięciu transakcji. Nie ma znaczenia, czy odbywa się to poprzez COMMIT czy ROLLBACK. Może występować nieznaczna różnica w wydanym czasie procesora - polecenie COMMIT jest prawdopodobnie szybsze do przeanalizowania niż ROLLBACK (dwa znaki mniej) i inne drobne różnice. Oczywiście dotyczy to tylko operacji tylko do odczytu!
Całkowicie niewymagane: inny programista, który mógłby odczytać kod, mógłby założyć, że ROLLBACK oznacza stan błędu.
źródło
Na marginesie, ale możesz też napisać ten kod w ten sposób:
using (IDbConnection connection = ConnectionFactory.CreateConnection()) using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted)) using (IDbCommand command = connection.CreateCommand()) { command.Transaction = transaction; command.CommandText = "SELECT * FROM SomeTable"; using (IDataReader reader = command.ExecuteReader()) { // Do something useful } // To commit, or not to commit? }
A jeśli nieco zmienisz strukturę, możesz również przenieść blok using dla IDataReader na górę.
źródło
Jeśli umieścisz SQL w procedurze składowanej i dodasz to powyżej zapytania:
set transaction isolation level read uncommitted
wtedy nie musisz przeskakiwać przez żadne obręcze w kodzie C #. Ustawienie poziomu izolacji transakcji w procedurze składowanej nie powoduje zastosowania tego ustawienia do wszystkich przyszłych zastosowań tego połączenia (o co należy się martwić w przypadku innych ustawień, ponieważ połączenia są w puli). Pod koniec procedury składowanej po prostu wraca do tego, z czym połączenie zostało zainicjowane.
źródło
ROLLBACK jest najczęściej używany w przypadku błędu lub wyjątkowych okoliczności, a COMMIT w przypadku pomyślnego zakończenia.
Powinniśmy zamykać transakcje za pomocą COMMIT (dla sukcesu) i ROLLBACK (dla niepowodzenia), nawet w przypadku transakcji tylko do odczytu, gdzie nie wydaje się to mieć znaczenia. W rzeczywistości ma to znaczenie dla spójności i przyszłości.
Transakcja tylko do odczytu może logicznie „zakończyć się niepowodzeniem” na wiele sposobów, na przykład:
Jeśli polecenia COMMIT i ROLLBACK są używane prawidłowo dla transakcji tylko do odczytu, będą nadal działać zgodnie z oczekiwaniami, jeśli w pewnym momencie zostanie dodany kod zapisu DB, np. W celu buforowania, audytu lub statystyk.
Niejawne ROLLBACK powinno być używane tylko w sytuacjach „krytycznego błędu”, gdy aplikacja ulega awarii lub kończy pracę z nieodwracalnym błędem, awarią sieci, awarią zasilania itp.
źródło
Biorąc pod uwagę, że READ nie zmienia stanu, nie zrobiłbym nic. Wykonanie zatwierdzenia nic nie da, poza zmarnowaniem cyklu na wysłanie żądania do bazy danych. Nie wykonałeś operacji, która zmieniła stan. Podobnie jest w przypadku wycofywania.
Należy jednak pamiętać o wyczyszczeniu obiektów i zamknięciu połączeń z bazą danych. Brak zamykania połączeń może prowadzić do problemów, jeśli ten kod jest wywoływany wielokrotnie.
źródło
Jeśli ustawisz AutoCommit false, a następnie TAK.
W eksperymencie z JDBC (sterownik Postgresql) odkryłem, że jeśli kwerenda wybierająca zrywa się (z powodu przekroczenia limitu czasu), nie można zainicjować nowego zapytania wybierającego, chyba że wycofasz wycofanie.
źródło
W przykładowym kodzie, gdzie masz
// Zrób coś pożytecznego
Czy wykonujesz instrukcję SQL, która zmienia dane?
Jeśli nie, nie ma czegoś takiego jak transakcja „Odczyt” ... Tylko zmiany z instrukcji Insert, Update i Delete (instrukcje, które mogą zmieniać dane) są w transakcji ... To, o czym mówisz, to blokady, które SQL Serwer zapisuje dane, które czytasz, z powodu INNYCH transakcji, które mają wpływ na te dane. Poziom tych blokad zależy od poziomu izolacji SQL Server.
Ale nie możesz Commit lub ROll Back niczego, jeśli twoja instrukcja SQL niczego nie zmieniła.
Jeśli zmieniasz dane, możesz zmienić poziom odseparowania bez jawnego rozpoczynania transakcji… Każda pojedyncza instrukcja SQL jest niejawnie zawarta w transakcji. jawne rozpoczęcie transakcji jest konieczne tylko w celu upewnienia się, że w ramach tej samej transakcji znajdują się 2 lub więcej wyciągów.
Jeśli wszystko, co chcesz zrobić, to ustawić poziom izolacji transakcji, a następnie ustawić tekst polecenia polecenia na „Ustaw poziom izolacji transakcji powtarzalny” (lub dowolny inny poziom), ustaw CommandType na CommandType.Text i wykonaj polecenie. (możesz użyć Command.ExecuteNonQuery ())
UWAGA: Jeśli wykonujesz WIELOKROTNE instrukcje odczytu i chcesz, aby wszystkie „widziały” ten sam stan bazy danych, co pierwsza, musisz ustawić poziom izolacji Odczyt powtarzalny lub serializowalny ...
źródło
Czy chcesz zablokować innym możliwość odczytu tych samych danych? Dlaczego warto skorzystać z transakcji?
@Joel - Moje pytanie byłoby lepiej sformułowane jako „Po co używać transakcji na przeczytanym zapytaniu?”
@Stefan - Jeśli zamierzasz używać AdHoc SQL, a nie przechowywanego procesu, po prostu dodaj WITH (NOLOCK) po tabelach w zapytaniu. W ten sposób nie ponosisz kosztów (choć minimalnych) w aplikacji i bazie danych dla transakcji.
SELECT * FROM SomeTable WITH (NOLOCK)
EDYCJA @ Komentarz 3: Ponieważ masz "sqlserver" w tagach pytania, założyłem, że MSSQLServer jest produktem docelowym. Teraz, gdy ten punkt został wyjaśniony, zmodyfikowałem tagi, aby usunąć odniesienie do konkretnego produktu.
Nadal nie jestem pewien, dlaczego w ogóle chcesz dokonać transakcji na opcjach odczytu.
źródło