2012-08-06 25 views
7

Ostatnio natknąłem się na dziwny problem, programując w bazie danych Oracle: wewnątrz transakcji serializowalnej, robię masową wstawkę (INSERT ... SELECT), a zaraz potem, otwórz kursor za pomocą przycisku SELECT na zmienionej tablicy. Zakładałem, że kursor ten będzie zawierał nowo wstawione wiersze, ale ku mojemu zaskoczeniu jego zawartość jest nieregularna, czasami zawiera wszystkie nowo wstawione wiersze, a czasem tylko podzbiór.Oracle: Wybierz natychmiast po wstawieniu do transakcji szeregowalnej

Rozwiązałem ten problem, zatwierdzając przed otwarciem kursora, ale zachowanie mnie zaintrygowało. Czy wybór po wstawieniu w ramach tej samej transakcji, bez zatwierdzenia interwiningu, może być zaufany? Czy to zachowanie jest w jakiś sposób związane z serializacją transakcji?

Followup: Gdy próbuje stworzyć powtarzalne przypadek testowy, byłem tylko w stanie uzyskać ten problem raz dodałem indeksu (w tym przypadku indeks klucza podstawowego, od rzeczywistego kodu to był regularny index). Być może problem leży w czasie poświęconym na budowanie indeksu, tak aby SELECT używał niekompletnego indeksu do pobierania wyników? W każdym razie, tu idzie powtarzalnej przypadek testowy:

-- Create empty source table 
CREATE TABLE TEST_CASE_1 AS 
    (SELECT 'CONTENT' AS CONTENT 
    FROM DUAL 
    WHERE 1 = 2) 

-- Add primary key 
ALTER TABLE TEST_CASE_1 
ADD CONSTRAINT TEST_CASE_1_PK PRIMARY KEY (CONTENT); 

-- Create empty destination table 
CREATE TABLE TEST_CASE_2 AS 
    (SELECT 'CONTENT' AS CONTENT 
    FROM DUAL 
    WHERE 1 = 2) 

-- Example of faulty code 
BEGIN 

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 

    -- Populate with 100.000 rows (I used ALL_OBJECTS but any source of 100.000 rows is good) 
    INSERT INTO TEST_CASE_1 
    (SELECT ROWNUM 
    FROM ALL_OBJECTS 
    WHERE ROWNUM <= 100000); 

    INSERT INTO TEST_CASE_2 
    (SELECT * 
    FROM TEST_CASE_1 
    WHERE CONTENT > 0); 

    COMMIT; 

END; 

W tym przykładzie, spodziewałbym TEST_CASE_2 również mieć 100.000 wierszy. Reprodukując ten przypadek testowy (w bez obciążeniowej bazie danych), uzyskałem około 400-500 wstawionych wierszy. Usuwając instrukcję ustawiającą transakcję jako możliwą do serializacji, uzyskałem poprawną liczbę wierszy 100 000.

+0

Czy otwierasz kursor w tej samej sesji bazy danych co podczas wstawiania? Gdzie robisz tę pracę - w rzeczywistości w bazie danych lub z aplikacji klienckiej - i czy ta ostatnia posiada pulę połączeń, a Ty (czasami) uzyskujesz inne połączenie dla tych dwóch działań? –

+0

Kursor jest otwierany w tej samej sesji. Pierwotnie kursor został otwarty w bazie danych, a następnie iterowany w pliku wykonywalnym .NET, ale w celu wyizolowania problemu wykonałem wersję procedury, która iterowała kursor wewnątrz bazy danych, a problem utrzymywał się - w rzeczywistości przed wykonaniem tego nie mogłem ". t przekonaj się, że problem dotyczył wyboru po wstawieniu. – user1578874

+0

To nie powinno się zdarzyć; from [the docs] (http://docs.oracle.com/cd/E11882_01/server.112/e25789/consist.htm#sthref1189) "Na poziomie izolacji serializacji, transakcja widzi tylko zmiany popełnione w momencie transakcji -nie zapytanie rozpoczęte i zmiany dokonane przez samą transakcję, więc powinieneś zobaczyć własne zmiany. Może to być błąd w twojej konkretnej wersji. Zaangażowanie sprawia, że ​​zmiana poziomu izolacji jest bezsensowna. Czy możesz opublikować powtarzalny test testowy? –

Odpowiedz

8

To wydaje się być błędem; jeśli masz dostęp do strony wsparcia Oracle, spójrz na notatkę 1455175.1, która pochodzi z 8i. Istnieje kilka wymienionych błędów (7592038 - "NIEWIDZIALNE DANE OD WYBORU/AKTUALIZACJI NOWO WSTAWIONEGO RZĘDU W SERIALIZOWANIU", 6363019), ale są zamknięte jako duplikaty 440317 ("POZIOM ISOLACJI POZIOMU ​​PRZYCZYNIENIA NIE ZNAJDUJE SIĘ NA WYBRANYCH RZĘDACH PO WSTAWIU "), co zostało pokazane jako wciąż otwarte i badane przez rozwój - mimo że zostało pierwotnie podniesione przeciwko wersji 7 (!).

Wydaje się, że masz rację, to jest związane z PK. Dostępne są następujące obejścia:

  • Zaakceptuj wykonaną pracę do tego momentu.
  • Wykonaj dodatkowe (ale różne) instrukcje (być może po powrocie do punktu zapisu ustalonego wcześniej w transakcji).
  • Wycofaj całą transakcję i zrestartuj transakcję od początku.
  • Wykonaj pełne skanowanie tabeli i unikaj indeksowania.

Wiesz, że pierwsze obejście jest skuteczne i nie sądzę, że pomoże ci drugi czy trzeci? Możesz spróbować czwartego, dodając wskazówkę /*+ FULL(TEST_CASE_1) */ do wyboru dla drugiej wstawki.

Nie dostaję błędu w 11.2.0.2 (Linux), ale nie mogę znaleźć niczego sugerującego, że błąd został naprawiony; i nie mam środowiska 11.1, żeby go wypróbować - więc nie mogę sprawdzić, czy ostatnie obejście dotyczy tego przypadku testowego.

Jest notatka, że ​​zamiast OG-08177 można pobrać 11G. Miałem ten problem, gdy uruchomiłem anonimowy blok zbyt wcześnie po utworzeniu tabel lub gdy wstawiłem zbyt wiele wierszy, co również wydaje się być związane z PK. This previous question może być istotne.

Wygląda na to, że nadal będzie to problemem, więc jeśli obejścia się nie pomogą, być może trzeba będzie ponownie rozważyć, czy naprawdę trzeba zmienić poziom izolacji; a jeśli to zrobisz, być może będziesz musiał podnieść żądanie usługi z Oracle, aby uzyskać lepszą odpowiedź.

+0

Bardzo dziękuję za dokładną odpowiedź! Błąd, którego doświadczam, wydaje się odpowiadać problemowi w obsłudze Oracle. Cieszę się, że jest to błąd, ponieważ jesteśmy zależni od transakcji nadających się do serializacji w bardzo małej, ale krytycznej dla misji sekcji naszego systemu, a gdyby były to standardowe semantyki dla transakcji podlegających serializacji, prawdopodobnie musielibyśmy przepisać całą sekcję.Pierwsze obejście (które użyłem) jest w porządku w moim szczególnym scenariuszu, ale nie w innych częściach programu. Przechowam cały arsenał obejść w mojej skrzynce narzędziowej. – user1578874

+1

Wygląda na to, że ten błąd * nadal * nie został naprawiony; Właśnie powtórzyłem to z Oracle 12.1.0.1.0 w systemie Windows. –

+1

Właśnie dostałem go na Oracle 12.1.0.2.0 - 64-bitowy Linux ... – Jakob

2

To jest potwierdzony błąd, a Oracle stwierdziło, że nie planuje go naprawić. Oto fragment z ich odpowiedzi na moją prośbę usług (styczeń 2015):

te objawy są związane z transakcją Serializable zostały z znanych problemów i Ty zawarcia z Bug 440317 jest poprawna znaleźć.

Bug 440317 - izolacja POZIOM SERIALIZABLE PRZYCZYNY Brak danych w wierszach wybrany po INSERT
Bug 16803610 - Rzędy wstawić za pomocą INSERT INTO giną w SERIALIZABLE izolacji LEVEL TRANSAC

Oba te błędy są publikowane, dzięki czemu można zobaczyć szczegóły w wyszukiwarce błędów MOS .

Jak na rozwój, było wiele błędów w tym samym problemie z bardzo długą historią . Projekt nie jest łatwy do zmiany, dlatego nie ma żadnej poprawki do tej chwili, gdy rozwidla się tę funkcję, która jest mało przydatna.

Rozwój zakończył się błędem mówiąc, że naprawa kodu nie jest możliwa.

Do obejścia je są
kod aplikacji modifiction:
zmienić logikę mieć popełnić przed select
lub nie używać serializacji
bez modyfikacji kodu aplikacji:
Nie używaj klucza podstawowego lub indeksy na stole

Powiązane problemy