Atasi MERGE JOIN (INDEX SCAN) dengan nilai KUNCI tunggal eksplisit pada KUNCI ASING

9

Ditambahkan 7/11 Masalahnya adalah kebuntuan terjadi karena pemindaian indeks selama Gabung BERGABUNG. Dalam hal ini transaksi mencoba untuk mendapatkan kunci S pada seluruh indeks di tabel induk FK, tetapi sebelumnya transaksi lain menempatkan kunci X pada nilai kunci indeks.

Mari saya mulai dengan contoh kecil (TSQL2012 DB dari 70-461 cource digunakan):

CREATE TABLE [Sales].[Orders](
[orderid] [int] IDENTITY(1,1) NOT NULL,
[custid] [int] NULL,
[empid] [int] NOT NULL,
[shipperid] [int] NOT NULL,
... )

Kolom [custid], [empid], [shipperid]adalah parameter terkait untuk [Sales].[Customers], [HR].[Employees], [Sales].[Shippers]sesuai. Dalam setiap kasus kami memiliki indeks berkerumun pada kolom yang dirujuk dalam tabel parrent.

ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Customers] FOREIGN KEY([custid]) REFERENCES [Sales].[Customers] ([custid])
ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Employees] FOREIGN KEY([empid]) REFERENCES [HR].[Employees] ([empid])
ALTER TABLE [Sales].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Shippers] FOREIGN KEY([shipperid])REFERENCES [Sales].[Shippers] ([shipperid])

Saya mencoba ke INSERT [Sales].[Orders] SELECT ... FROMmeja lain yang disebut [Sales].[OrdersCache]yang memiliki struktur yang sama dengan [Sales].[Orders]kecuali kunci asing. Hal lain yang mungkin penting untuk menyebutkan tabel [Sales].[OrdersCache]adalah indeks berkerumun.

CREATE CLUSTERED INDEX idx_c_OrdersCache ON Sales.OrdersCache ( custid, empid )

Seperti yang diharapkan ketika saya mencoba untuk memasukkan data volume rendah LOOP JOIN berfungsi dengan baik membuat indeks pencarian pada kunci asing.

Dengan volume data yang tinggi, MERGE BERGABUNG digunakan oleh pengoptimal query sebagai cara paling efisien untuk mempertahankan kunci foregn dalam kueri.

Dan tidak ada hubungannya dengan itu kecuali menggunakan OPTION (LOOP JOIN) dalam kasus kami dengan kunci asing atau INNER LOOP JOIN dalam kasus JOIN eksplisit.

Di bawah ini adalah permintaan yang saya coba jalankan di lingkungan saya:

INSERT Sales.Orders (
        custid, empid, shipperid, ... )
SELECT  custid, empid, 2, ...
FROM Sales.OrdersCache

Melihat rencana itu, kita dapat melihat bahwa ketiga kunci foreing divalidasi dengan MERGE JOIN. Itu bukan cara yang tepat bagi saya karena menggunakan INDEX SCAN dengan penguncian seluruh indeks. MERGE BERGABUNG selama validasi kunci asing

Menggunakan OPTION (LOOP JOIN) tidak cocok karena biayanya hampir 15% lebih banyak daripada MERGE JOIN (saya pikir regresi akan lebih besar dengan volume data yang tumbuh).

Dalam pernyataan SELECT Anda dapat melihat nilai tunggal untuk shipperidatribut untuk seluruh set yang dimasukkan. Menurut pendapat saya harus ada cara untuk membuat fase validasi untuk set yang dimasukkan lebih cepat setidaknya untuk atribut yang tidak dapat diubah. Sesuatu seperti:

  • buat LOOP BERGABUNG, GABUNG GABUNG, HASH GABUNG jika kami memiliki subset yang tidak ditentukan untuk validasi GABUNG
  • jika hanya ada satu nilai eksplisit dari kolom yang divalidasi, kami membuat validasi hanya sekali (INDEKS SEEK).

Apakah ada pola umum untuk melewati situasi di atas menggunakan struktur kode, objek DDL tambahan, dll?

Ditambahkan 20/07. Larutan. Pengoptimal Permintaan sudah membuat optimasi validasi 'kunci tunggal - kunci asing' dengan menggunakan MERGE BERGABUNG. Dan hanya dibuat untuk tabel Sales.Shippers, meninggalkan LOOP BERGABUNG untuk bergabung dalam kueri pada saat yang sama. Karena saya memiliki beberapa baris dalam tabel induk Query Optimizer menggunakan algoritme gabungan Gabung-gabungkan dan bandingkan setiap baris di tabel dalam dengan tabel induk hanya sekali. Jadi itulah jawaban pada pertanyaan saya jika ada mekanisme tertentu untuk secara efektif memproses nilai-nilai tunggal dalam satu set selama validasi kunci tunggal. Itu bukan keputusan yang sempurna tapi itulah cara SQL Server mengoptimalkan kasus ini.

Investigasi yang memengaruhi kinerja mengungkapkan bahwa dalam kasus saya, pernyataan penyatuan MERGE JOIN dan LOOP JOIN menjadi kira-kira sama dengan 750 baris yang disisipkan secara simultan dengan superioritas MERGE JOIN berikut (dalam sumber daya waktu CPU). Jadi menggunakan OPTION (LOOP JOIN) adalah solusi yang tepat untuk proses bisnis saya.

Oleg I
sumber

Jawaban:

8

Menggunakan OPTION (LOOP JOIN) tidak cocok karena harganya hampir 15% lebih tinggi dari MERGE JOIN

Persentase biaya yang ditampilkan dalam output showplan selalu merupakan estimasi model pengoptimal, bahkan dalam rencana pasca-eksekusi (aktual). Biaya-biaya ini kemungkinan tidak mencerminkan kinerja runtime aktual pada perangkat keras khusus Anda. Satu-satunya cara untuk memastikan adalah menguji alternatif dengan beban kerja Anda, mengukur metrik mana yang paling penting bagi Anda (waktu yang berlalu, penggunaan CPU, dan sebagainya).

Dalam pengalaman saya, pengoptimal beralih dari loop bergabung untuk bergabung bergabung untuk validasi kunci asing terlalu dini. Dalam semua kecuali operasi terbesar, saya telah menemukan loop bergabung lebih disukai. Dan dengan "besar" di sana maksud saya puluhan juta baris setidaknya, tentu bukan ribuan atau lebih yang Anda tunjukkan dalam komentar untuk pertanyaan Anda.

Menurut pendapat saya harus ada cara untuk membuat fase validasi untuk set yang dimasukkan lebih cepat setidaknya untuk atribut yang tidak dapat diubah.

Ini masuk akal secara prinsip, tetapi tidak ada logika seperti itu hari ini di dalam logika rencana-bangunan yang digunakan validasi kunci asing. Logika saat ini sengaja sangat generik dan ortogonal dengan permintaan yang lebih luas; optimisasi spesifik mempersulit pengujian dan meningkatkan kemungkinan bug tepi-kasus.

Apakah ada pola umum untuk melewati situasi di atas menggunakan struktur kode, objek DDL tambahan, dll?

Bukannya aku sadar. Risiko kebuntuan dengan penggabungan validasi kunci asing telah dikenal , dengan solusi yang paling banyak digunakan adalah OPTION (LOOP JOIN)petunjuknya. Tidak ada cara untuk menghindari kunci bersama diambil selama validasi kunci asing, karena ini diperlukan untuk kebenaran , bahkan di bawah tingkat isolasi versi baris.

Tidak ada jawaban umum yang lebih baik (daripada petunjuk bergabung loop) jika Anda ingin mempertahankan kemampuan untuk beberapa proses bersamaan untuk menambahkan baris ke tabel induk dan anak secara transaksi. Tetapi, jika Anda ingin membuat serial modifikasi data (tidak dibaca), menggunakan sp_getapplock adalah teknik yang andal dan sederhana.

Paul White 9
sumber