Co konkretnie robi OracleBulkCopy i jak mogę zoptymalizować jego wydajność?


14

Podsumowując szczegóły: Musimy wprowadzić około 5 milionów wierszy do bazy danych dostawców (Oracle). Wszystko idzie świetnie w przypadku partii 500 tys. Wierszy przy użyciu OracleBulkCopy(ODP.NET), ale gdy próbujemy skalować do 5 mln, wydajność zaczyna zwalniać do indeksowania, gdy osiągnie znak 1 mln, staje się stopniowo wolniejsza w miarę ładowania większej liczby wierszy i ostatecznie upływa po około 3 godzinach.

Podejrzewam, że jest to związane z kluczem podstawowym na stole, ale przeszukiwałem fora Oracle i Stack Overflow w celu uzyskania informacji i wiele tego, co czytam, zaprzecza temu (również wiele postów wydaje się zaprzeczać sobie nawzajem ) . Mam nadzieję, że ktoś może ustalić rekord w kilku ściśle powiązanych pytaniach dotyczących tego procesu:

  1. Czy OracleBulkCopyklasa stosuje ładowanie konwencjonalne czy bezpośrednie? Czy jest jakiś sposób na potwierdzenie tego, w ten czy inny sposób?

  2. Zakładając, że robi załadunek korzystanie z bezpośrednim ścieżka: Czy to prawda, że Oracle automatycznie ustawia wszystkie indeksy bezużyteczny podczas obciążenia i umieszcza je z powrotem w Internecie potem? Przeczytałem kilka oświadczeń na ten temat, ale znów nie mogę tego potwierdzić.

  3. Jeśli # 2 jest prawdą, to czy powinno mieć jakiekolwiek znaczenie, jakie indeksy znajdują się w tabeli przed zainicjowaniem operacji kopiowania zbiorczego? Jeśli tak, to dlaczego?

  4. Czy w związku z punktem 3 istnieje jakakolwiek praktyczna różnica między ładowaniem masowym z indeksem bezużytecznym a faktycznym upuszczaniem indeksu przed ładowaniem, a następnie jego odtwarzaniem?

  5. Jeśli # 2 jest nie poprawny, lub jeśli istnieją pewne zastrzeżenia nie mam zrozumienia, wtedy byłoby żadnej różnicy do jawnie zrobić indeks bezużyteczny przed obciążeniem luzem, a następnie jawnie odbudować go potem?

  6. Czy istnieje coś innego niż kompilacje indeksu, które mogłyby powodować stopniowe spowolnienie operacji kopiowania zbiorczego w miarę dodawania coraz większej liczby rekordów? (Być może ma to coś wspólnego z logowaniem, chociaż spodziewałbym się, że operacje masowe nie są rejestrowane?)

  7. Jeśli naprawdę nie ma innego sposobu na podniesienie wydajności do tabaki oprócz upuszczenia najpierw PK / indeksu, jakie kroki mogę podjąć, aby upewnić się, że indeks nie zniknie całkowicie, tj. Jeśli połączenie z bazą danych zostanie utracone w środek procesu?


Uwaga dodatkowa: kopiowane dane są już posortowane zgodnie z PK, który jest jedynym indeksem w tabeli.
Aaronaught

Czy używasz DataReadera do odczytu danych ze źródła?
bernd_k

@bernd_k: Nie, ładowanie całkowicie z pamięci. Na pewno nie jest to źródło problemu.
Aaronaught

Odpowiedzi:


13

Jeszcze kilka dni czytania i eksperymentowania i mogłem (głównie) odpowiedzieć na wiele z nich:

  1. Znalazłem to zakopane w dokumentacji ODP.NET (jak na ironię nie w OracleBulkCopydokumentach):

    Funkcja ODP.NET Bulk Copy wykorzystuje metodę bezpośredniego ładowania ścieżki, która jest podobna, ale nie taka sama, jak Oracle SQL * Loader. Korzystanie z bezpośredniego ładowania ścieżki jest szybsze niż ładowanie konwencjonalne (przy użyciu konwencjonalnych INSERTinstrukcji SQL ).

    Wygląda więc na to, że ma używać bezpośrednią drogę.

  2. Mogłem to zweryfikować, wykonując dużą operację kopiowania zbiorczego i uzyskując właściwości indeksu od SQL Developer. Indeks pojawił się tak, jakby trwałoUNUSABLE kopiowanie zbiorcze. Jednak odkryłem również, że OracleBulkCopy.WriteToServerodmówi uruchomienia, jeśli indeks zaczyna się w UNUSABLEstanie, więc wyraźnie dzieje się tutaj więcej, ponieważ jeśli byłoby to tak proste, jak wyłączenie i przebudowanie indeksu, nie powinno to obchodzić stanu początkowego.

  3. To robi różnicę, szczególnie jeśli indeks jest również ograniczeniem . Znalazłem ten mały klejnot w powyższej dokumentacji:

    Włączone ograniczenia
    Podczas kopiowania zbiorczego Oracle domyślnie włączone są automatycznie następujące ograniczenia:

    • NOT NULL
    • UNIQUE
    • PRIMARY KEY (unikalne ograniczenia na niepustych kolumnach)

    NOT NULLograniczenia są sprawdzane w czasie kompilacji tablicy kolumn. Każdy wiersz, który narusza NOT NULLograniczenie, jest odrzucany.

    UNIQUEograniczenia są weryfikowane, gdy indeksy są odbudowywane na końcu ładowania. Indeks jest pozostawiony w stanie Nieużywalny, jeśli narusza UNIQUEograniczenie.

    Dokumentacja jest nieco mglista na temat tego, co dzieje się podczas ładowania, szczególnie w przypadku kluczy podstawowych, ale jedno jest absolutnie pewne - zachowuje się inaczej z kluczem podstawowym niż bez niego . Ponieważ na OracleBulkCopyszczęście pozwoli ci na złamanie ograniczeń indeksu (i doprowadzi indeks do UNUSABLEstanu po zakończeniu ), mam przeczucie, że buduje on indeks PK podczas kopiowania zbiorczego, ale po prostu nie sprawdza go później.

  4. Nie jestem pewien, czy zaobserwowana różnica dotyczy samego Oracle, czy tylko dziwactwa OracleBulkCopy. Jury wciąż nie bierze udziału w tym.

  5. OracleBulkCopywyrzuci wyjątek, jeśli indeks jest początkowo w UNUSABLEstanie, więc naprawdę jest to kwestia sporna.

  6. Jeśli tam inne czynniki, indeksy i indeksy (zwłaszcza PK) są nadal najważniejszym, jak dowiedziałem się poprzez:

  7. Tworzenie globalnej tabeli tymczasowej z tym samym schematem (za pomocą CREATE AS), a następnie kopiowanie zbiorcze do tabeli tymczasowej, a na koniec tworzenie zwykłej starej INSERTtabeli tymczasowej do tabeli rzeczywistej. Ponieważ tabela temp nie ma indeksu, kopiowanie zbiorcze odbywa się bardzo szybko, a wersja ostateczna INSERTjest również szybka, ponieważ dane są już w tabeli (jeszcze nie wypróbowałem wskazówki dołączania, ponieważ 5-wierszowa kopia tabeli do tabeli zajmuje już mniej niż 1 minutę).

    Nie jestem jeszcze pewien potencjalnych konsekwencji (ab) korzystania z tymczasowego obszaru tabel w ten sposób, ale jak dotąd nie przysporzyło mi to żadnych problemów i jest znacznie bezpieczniejsze niż alternatywa, ponieważ zapobiega uszkodzeniu któregoś z wierszy lub indeksy.

    Sukces tego również dość wyraźnie pokazuje, że problem stanowi wskaźnik PK, ponieważ jest to jedyna praktyczna różnica między tabelą tymczasową a tabelą stałą - oba rozpoczęły się od zerowych wierszy podczas testów wydajności.

Wniosek: nie zawracaj sobie głowy próbą kopiowania więcej niż około 100 000 wierszy do indeksowanej tabeli Oracle za pomocą ODP.NET. Upuść indeks (jeśli go nie potrzebujesz) lub „załaduj” wstępnie dane do innej (nieindeksowanej) tabeli.


Nie jestem pewien, czy sprawdzę ograniczenia klucza podstawowego. Zdarzyło mi się masowo wstawiać te same dane do tabeli Oracle 2 razy i Select * pokazuje 2 zduplikowane wiersze. W tym stanie usuwanie nie jest możliwe, ale tabela obcinania pomaga przywrócić czysty stan.
bernd_k 30.10.11

@bernd_k: Deletenie jest możliwe, ponieważ indeks jest UNUSABLE. Jest to wynik kontroli ograniczenia, która ma miejsce na końcu kopii zbiorczej.
Aaronaught

Mam uruchomiony skrypt PowerShell, wywołujący masowe kopiowanie do bazy danych Oracle z czytnika danych SQL Server, wszystkie tabele docelowe z kluczami podstawowymi i nie mam problemów z tabelami z maksymalnie 205278 wierszami. Ale jestem bardzo ostrożny, aby najpierw zapełnić tabele główne przed wypełnieniem tabel szczegółów. Nie usunąłem żadnego innego indeksu z tabeli i nie mam problemów, gdy tabele są początkowo puste.
bernd_k

@bernd_k: Tak, nie miałem zbyt wielu problemów na tym tomie (zobacz mój ostatni akapit). Kiedy dochodzi do milionów, robi się okropnie. Może również istnieć różnica, jeśli opróżnisz tabelę jakiś czas po każdej kopii zbiorczej (ta nie zostanie opróżniona, zostanie do niej dołączona i wiesz, jak indeksy stają się wolniejsze, gdy stają się większe).
Aaronaught

Może to pomaga, kiedy robiszalter session set skip_unusable_indexes = true;
Wernfried Domscheit

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.