Oracle
 sql >> Teknologi Basis Data >  >> RDS >> Oracle

Oracle:Kinerja Kumpulkan Massal

Di dalam Oracle, ada mesin virtual SQL (VM) dan PL/SQL VM. Saat Anda perlu berpindah dari satu VM ke VM lainnya, Anda akan dikenakan biaya perubahan konteks. Secara individual, perubahan konteks tersebut relatif cepat, tetapi ketika Anda melakukan pemrosesan baris demi baris, perubahan tersebut dapat menambahkan hingga memperhitungkan sebagian besar waktu yang dihabiskan kode Anda. Saat Anda menggunakan pengikatan massal, Anda memindahkan beberapa baris data dari satu VM ke VM lainnya dengan satu pergeseran konteks, secara signifikan mengurangi jumlah pergeseran konteks, membuat kode Anda lebih cepat.

Ambil, misalnya, kursor eksplisit. Jika saya menulis sesuatu seperti ini

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;

maka setiap kali saya menjalankan pengambilan, saya

  • Melakukan perubahan konteks dari PL/SQL VM ke SQL VM
  • Meminta SQL VM untuk mengeksekusi kursor untuk menghasilkan baris data berikutnya
  • Melakukan pergeseran konteks lain dari SQL VM kembali ke PL/SQL VM untuk mengembalikan satu baris data saya

Dan setiap kali saya menyisipkan baris, saya melakukan hal yang sama. Saya menanggung biaya pergeseran konteks untuk mengirimkan satu baris data dari PL/SQL VM ke SQL VM, meminta SQL untuk mengeksekusi INSERT pernyataan, dan kemudian menimbulkan biaya pergeseran konteks lain kembali ke PL/SQL.

Jika source_table memiliki 1 juta baris, itu adalah 4 juta pergeseran konteks yang kemungkinan akan menjelaskan sebagian kecil dari waktu yang berlalu dari kode saya. Sebaliknya, jika saya melakukan BULK COLLECT dengan LIMIT dari 100, saya dapat menghilangkan 99% dari pergeseran konteks saya dengan mengambil 100 baris data dari SQL VM ke dalam koleksi di PL/SQL setiap kali saya dikenakan biaya pergeseran konteks dan memasukkan 100 baris ke tabel tujuan setiap kali saya menimbulkan pergeseran konteks di sana.

Jika dapat menulis ulang kode saya untuk menggunakan operasi massal

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;

Sekarang, setiap kali saya menjalankan pengambilan, saya mengambil 100 baris data ke dalam koleksi saya dengan satu set pergeseran konteks. Dan setiap kali saya melakukan FORALL insert, saya memasukkan 100 baris dengan satu set pergeseran konteks. Jika source_table memiliki 1 juta baris, ini berarti saya telah beralih dari 4 juta pergeseran konteks menjadi 40.000 pergeseran konteks. Jika pergeseran konteks menyumbang, katakanlah, 20% dari waktu yang telah berlalu dari kode saya, saya telah menghilangkan 19,8% dari waktu yang telah berlalu.

Anda dapat meningkatkan ukuran LIMIT untuk lebih mengurangi jumlah pergeseran konteks tetapi Anda dengan cepat mencapai hukum hasil yang semakin berkurang. Jika Anda menggunakan LIMIT 1000 daripada 100, Anda akan menghilangkan 99,9% dari pergeseran konteks daripada 99%. Itu berarti koleksi Anda menggunakan memori PGA 10x lebih banyak. Dan itu hanya akan menghilangkan 0,18% lebih banyak waktu yang telah berlalu dalam contoh hipotetis kami. Anda dengan sangat cepat mencapai titik di mana memori tambahan yang Anda gunakan menambah lebih banyak waktu daripada yang Anda hemat dengan menghilangkan pergeseran konteks tambahan. Secara umum, sebuah LIMIT suatu tempat antara 100 dan 1000 kemungkinan akan menjadi sweet spot.

Tentu saja, dalam contoh ini, akan lebih efisien untuk menghilangkan semua perubahan konteks dan melakukan semuanya dalam satu pernyataan SQL

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

Masuk akal untuk menggunakan PL/SQL di tempat pertama jika Anda melakukan semacam manipulasi data dari tabel sumber yang tidak dapat Anda terapkan secara wajar di SQL.

Selain itu, saya menggunakan kursor eksplisit dalam contoh saya dengan sengaja. Jika Anda menggunakan kursor implisit, di Oracle versi terbaru, Anda mendapatkan manfaat dari BULK COLLECT dengan LIMIT dari 100 secara implisit. Ada pertanyaan StackOverflow lain yang membahas manfaat kinerja relatif dari kursor implisit dan eksplisit dengan operasi massal yang membahas lebih detail tentang kerutan tertentu.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Mengapa SQL*Plus melakukan komit saat keluar?

  2. Menggunakan Dapper dengan Oracle

  3. Menghubungkan Oracle ke SQL Server melalui Koneksi Aman

  4. RTRIM() Fungsi di Oracle

  5. SQL (ORACLE):ORDER BY dan LIMIT