sql >> Databáze >  >> RDS >> Oracle

Výkon Oracle:Bulk Collect

V rámci Oracle existuje virtuální stroj SQL (VM) a virtuální počítač PL/SQL. Když se potřebujete přesunout z jednoho virtuálního počítače na druhý, vynaložíte náklady na posun kontextu. Jednotlivě jsou tyto změny kontextu relativně rychlé, ale když provádíte zpracování řádek po řádku, mohou se sčítat a představovat významný zlomek času, který váš kód stráví. Když používáte hromadné vazby, přesouváte více řádků dat z jednoho virtuálního počítače do druhého s jediným posunem kontextu, čímž se výrazně snižuje počet posunů kontextu a váš kód je rychlejší.

Vezměte si například explicitní kurzor. Pokud něco takového napíšu

DECLARE
  CURSOR c 
      IS SELECT *
           FROM source_table;
  l_rec source_table%rowtype;
BEGIN
  OPEN c;
  LOOP
    FETCH c INTO l_rec;
    EXIT WHEN c%notfound;

    INSERT INTO dest_table( col1, col2, ... , colN )
      VALUES( l_rec.col1, l_rec.col2, ... , l_rec.colN );
  END LOOP;
END;

pak pokaždé, když provedu načtení, jsem

  • Provedení přesunu kontextu z virtuálního počítače PL/SQL na virtuální počítač SQL
  • Požádání virtuálního počítače SQL, aby spustil kurzor a vygeneroval další řádek dat
  • Provedení dalšího posunu kontextu z virtuálního počítače SQL zpět na virtuální počítač PL/SQL, aby se vrátil můj jeden řádek dat

A pokaždé, když vložím řádek, dělám to samé. Vznikají mi náklady na kontextový posun za účelem odeslání jednoho řádku dat z virtuálního počítače PL/SQL do virtuálního počítače SQL a žádám SQL, aby provedl INSERT a pak vzniknou náklady na další kontextový posun zpět do PL/SQL.

Pokud source_table má 1 milion řádků, to jsou 4 miliony kontextových posunů, které budou pravděpodobně představovat rozumný zlomek uplynulého času mého kódu. Pokud na druhou stranu udělám BULK COLLECT s LIMIT ze 100, mohu eliminovat 99 % mých kontextových posunů načtením 100 řádků dat z SQL VM do kolekce v PL/SQL pokaždé, když mi vzniknou náklady na kontextový posun, a vložením 100 řádků do cílové tabulky pokaždé, když tam dojde ke změně kontextu.

Pokud mohu přepsat můj kód, aby bylo možné využívat hromadné operace

DECLARE
  CURSOR c 
      IS SELECT *
           FROM source_table;
  TYPE  nt_type IS TABLE OF source_table%rowtype;
  l_arr nt_type;
BEGIN
  OPEN c;
  LOOP
    FETCH c BULK COLLECT INTO l_arr LIMIT 100;
    EXIT WHEN l_arr.count = 0;

    FORALL i IN 1 .. l_arr.count
      INSERT INTO dest_table( col1, col2, ... , colN )
        VALUES( l_arr(i).col1, l_arr(i).col2, ... , l_arr(i).colN );
  END LOOP;
END;

Nyní pokaždé, když provedu načtení, získám 100 řádků dat do své kolekce s jedinou sadou kontextových posunů. A pokaždé, když udělám svůj FORALL vložit, vkládám 100 řádků s jedinou sadou kontextových posunů. Pokud source_table má 1 milion řádků, to znamená, že jsem přešel ze 4 milionů kontextových posunů na 40 000 kontextových posunů. Pokud změny kontextu představovaly řekněme 20 % uplynulého času mého kódu, odstranil jsem 19,8 % uplynulého času.

Můžete zvětšit velikost LIMIT k dalšímu snížení počtu kontextových posunů, ale rychle narazíte na zákon klesajících výnosů. Pokud jste použili LIMIT z 1000 namísto 100, odstranili byste 99,9 % kontextových posunů spíše než 99 %. To by však znamenalo, že vaše sbírka využívala 10x více paměti PGA. A v našem hypotetickém příkladu by to eliminovalo pouze o 0,18 % více uplynulého času. Velmi rychle dosáhnete bodu, kdy další paměť, kterou používáte, přidává více času, než ušetříte tím, že eliminujete další posuny kontextu. Obecně LIMIT někde mezi 100 a 1000 bude pravděpodobně nejpříjemnější místo.

V tomto příkladu by samozřejmě bylo ještě efektivnější eliminovat všechny posuny kontextu a udělat vše v jediném příkazu SQL

INSERT INTO dest_table( col1, col2, ... , colN )
  SELECT col1, col2, ... , colN
    FROM source_table;

Uchýlit se k PL/SQL by dávalo smysl pouze v případě, že provádíte nějakou manipulaci s daty ze zdrojové tabulky, kterou nemůžete rozumně implementovat v SQL.

Navíc jsem ve svém příkladu záměrně použil explicitní kurzor. Pokud používáte implicitní kurzory, v posledních verzích Oracle získáte výhody BULK COLLECT s LIMIT 100 implicitně. Existuje další otázka StackOverflow, která pojednává o výhodách relativního výkonu implicitních a explicitních kurzorů s hromadnými operacemi, které se podrobněji zabývají těmito konkrétními vráskami.



  1. Kontrola časového překrývání tabulky?

  2. PostgreSQL - nastavte výchozí hodnotu buňky podle hodnoty jiné buňky

  3. Jak změníte kódování znaků postgresové databáze?

  4. Záměna s Oracle CONNECT BY