Saya memiliki tabel dengan 250 ribu baris di basis data pengujian saya. (Ada beberapa ratus juta dalam produksi, kita dapat mengamati masalah yang sama di sana.) Tabel memiliki pengidentifikasi string nvarchar2 (50), bukan nol, dengan indeks unik di atasnya (bukan PK).
Pengidentifikasi terdiri dari bagian pertama yang memiliki 8 nilai berbeda di basis data pengujian saya (dan sekitar seribu dalam produksi), kemudian tanda @, dan akhirnya angka, 1 hingga 6 digit. Misalnya mungkin ada 50 ribu baris yang dimulai dengan 'ABCD_BGX1741F_2006_13_20110808.xml @', dan diikuti oleh 50 ribu angka yang berbeda.
Ketika saya meminta satu baris berdasarkan pengenalnya, kardinalitas diperkirakan 1, biayanya sangat rendah, itu berfungsi dengan baik. Ketika saya meminta lebih dari satu baris dengan beberapa pengidentifikasi dalam ekspresi IN atau ekspresi OR, estimasi untuk indeks benar-benar salah, sehingga pemindaian tabel penuh digunakan. Jika saya memaksakan indeks dengan petunjuk, itu sangat cepat, pemindaian tabel penuh sebenarnya dieksekusi urutan besarnya lebih lambat (dan lebih banyak lebih lambat dalam produksi). Jadi ini adalah masalah pengoptimal.
Sebagai tes, saya menduplikasi tabel (dalam skema yang sama + tablespace) dengan DDL yang sama persis dan konten yang sama persis. Saya menciptakan kembali indeks unik di tabel pertama untuk ukuran yang baik, dan membuat indeks yang sama persis di tabel klon. Saya melakukan DBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);
. Anda bahkan dapat melihat bahwa nama indeks berurutan. Jadi sekarang satu-satunya perbedaan antara dua tabel adalah bahwa yang pertama dimuat secara acak selama periode waktu yang lama, dengan blok yang tersebar di disk (dalam tablespace bersama dengan beberapa tabel besar lainnya), yang kedua dimuat sebagai satu batch INSERT-SELECT. Selain itu, saya tidak bisa membayangkan perbedaan apa pun. (Tabel asli telah menyusut sejak penghapusan besar terakhir, dan tidak ada satu penghapusan pun setelah itu.)
Berikut adalah rencana permintaan untuk orang sakit dan tabel klon (String di bawah kuas hitam sama di seluruh gambar, dan juga di bawah kuas abu-abu.):
(Dalam contoh ini, ada 1867 baris yang dimulai dengan pengenal yang disikat hitam. Kueri 2-baris menghasilkan kardinalitas 1867 * 2, kueri 3-baris menghasilkan kardinalitas 1867 * 3, dll. Tidak dapat secara kebetulan, Oracle tampaknya tidak peduli dengan akhir dari pengidentifikasi.)
Apa yang bisa menyebabkan perilaku ini? Jelas akan sangat mahal untuk membuat ulang tabel dalam produksi.
USER_TABLES: http://i.stack.imgur.com/nDWze.jpg USER_INDEXES: http://i.stack.imgur.com/DG9um.jpg Saya hanya mengubah nama skema dan tablespace. Anda dapat melihat bahwa tabel dan nama indeks sama seperti pada tangkapan layar rencana kueri.
sumber
in
), bukan? Saya pikir CBO membuat asumsi kardinalitas 1, tetapi hanya dalam kasus yang sangat sederhana. Saya berasumsi Anda bisa mengatasi semuanya dengan menggunakan besar,UNION ALL
tetapi mungkin ada alasan lain untuk tidak melakukan itu dan JL menyebutkan solusi lain yang mungkin dalam posting blog terkait.method_opt=>'for all indexed columns'
?Saya menemukan solusinya! Sangat indah dan saya benar-benar belajar BANYAK tentang Oracle.
Dalam satu kata: histogram.
Saya mulai membaca banyak tentang cara kerja Oracle CBO dan saya menemukan histogram. Saya tidak sepenuhnya mengerti jadi saya melihat tabel USER_HISTOGRAMS, dan voilá. Ada beberapa baris untuk meja sakit, dan praktis tidak ada untuk tabel kloning. Untuk tabel sakit, ada satu baris untuk masing-masing dari 8 bagian pengenal-awal yang berbeda. Dan ini kuncinya: mereka terpotong di 32 karakter, sebelum tanda @. Seperti yang saya katakan, bagian pertama kunci sangat berulang, mereka menjadi berbeda setelah tanda @.
Tampaknya histogram bisa lebih kuat daripada fakta sederhana bahwa indeks unik selalu memiliki kardinalitas 0 atau 1 untuk nilai yang diberikan. Ketika saya mencari 2+ baris, Oracle melihat histogram, ia berpikir bahwa mungkin ada puluhan ribu nilai untuk bagian pengenal awal, dan itu melemparkan CBO di luar jalur.
Saya menghapus histogram untuk kolom itu di tabel lama dan masalahnya hilang!
Lebih banyak membaca: https://blogs.oracle.com/optimizer/entry/how_do_i_drop_an_existing_histogram_on_a_column_and_stop_the_auto_stats_gathering_job_from_creating
sumber
Saya mengirim email ke Jonathan Lewis tentang ini dan mendapat balasan yang sangat membantu:
Saya sangat merekomendasikan membaca posting blog yang dia tautkan, mereka menjelaskan secara rinci batasan histogram yang Anda jalankan, misalnya:
sumber