Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Masalah Halloween – Bagian 3

[ Bagian 1 | Bagian 2 | Bagian 3 | Bagian 4 ]

MERGE pernyataan (diperkenalkan di SQL Server 2008) memungkinkan kita untuk melakukan campuran INSERT , UPDATE , dan DELETE operasi menggunakan satu pernyataan. Masalah Perlindungan Halloween untuk MERGE sebagian besar merupakan kombinasi dari persyaratan operasi individu, tetapi ada beberapa perbedaan penting dan beberapa pengoptimalan menarik yang hanya berlaku untuk MERGE .

Menghindari Masalah Halloween dengan MERGE

Kita mulai dengan melihat kembali contoh Demo dan Staging dari bagian dua:

CREATE TABLE dbo.Demo
(
    SomeKey integer NOT NULL,
 
    CONSTRAINT PK_Demo
        PRIMARY KEY (SomeKey)
);
 
CREATE TABLE dbo.Staging
(
    SomeKey integer NOT NULL
);
 
INSERT dbo.Staging
    (SomeKey)
VALUES
    (1234),
    (1234);
 
CREATE NONCLUSTERED INDEX c 
ON dbo.Staging (SomeKey);
 
INSERT dbo.Demo
SELECT s.SomeKey
FROM dbo.Staging AS s
WHERE NOT EXISTS
(
    SELECT 1
    FROM dbo.Demo AS d
    WHERE d.SomeKey = s.SomeKey
);

Seperti yang Anda ingat, contoh ini digunakan untuk menunjukkan bahwa INSERT membutuhkan Perlindungan Halloween ketika tabel target penyisipan juga dirujuk dalam SELECT bagian dari kueri (EXISTS klausa dalam hal ini). Perilaku yang benar untuk INSERT pernyataan di atas adalah mencoba menambahkan keduanya 1234 nilai, dan akibatnya gagal dengan PRIMARY KEY pelanggaran. Tanpa pemisahan fase, INSERT akan salah menambahkan satu nilai, menyelesaikan tanpa kesalahan yang dilemparkan.

Rencana eksekusi INSERT

Kode di atas memiliki satu perbedaan dari yang digunakan di bagian dua; indeks nonclustered pada tabel Staging telah ditambahkan. INSERT rencana eksekusi tetap membutuhkan Perlindungan Halloween:

Rencana eksekusi MERGE

Sekarang coba sisipan logis yang sama yang diekspresikan menggunakan MERGE sintaks:

MERGE dbo.Demo AS d
USING dbo.Staging AS s ON
    s.SomeKey = d.SomeKey
WHEN NOT MATCHED BY TARGET THEN
    INSERT (SomeKey)
    VALUES (s.SomeKey);

Jika Anda tidak terbiasa dengan sintaks, logika yang ada untuk membandingkan baris di tabel Staging dan Demo pada nilai SomeKey, dan jika tidak ada baris yang cocok ditemukan di tabel target (Demo), kami menyisipkan baris baru. Ini memiliki semantik yang persis sama dengan INSERT...WHERE NOT EXISTS sebelumnya kode, tentu saja. Namun, rencana eksekusinya sangat berbeda:

Perhatikan kurangnya Eager Table Spool dalam rencana ini. Meskipun demikian, kueri masih menghasilkan pesan kesalahan yang benar. Tampaknya SQL Server telah menemukan cara untuk mengeksekusi MERGE rencanakan secara iteratif sambil menghormati pemisahan fase logis yang disyaratkan oleh standar SQL.

Optimasi pengisian lubang

Dalam situasi yang tepat, pengoptimal SQL Server dapat mengenali bahwa MERGE pernyataan adalah mengisi lubang , yang merupakan cara lain untuk mengatakan bahwa pernyataan hanya menambahkan baris jika ada celah yang ada di kunci tabel target.

Agar pengoptimalan ini dapat diterapkan, nilai-nilai yang digunakan dalam WHEN NOT MATCHED BY TARGET klausa harus tepat cocokkan dengan ON bagian dari USING ayat. Juga, tabel target harus memiliki kunci unik (persyaratan yang dipenuhi oleh PRIMARY KEY dalam kasus ini). Jika persyaratan ini terpenuhi, MERGE pernyataan tidak memerlukan perlindungan dari Masalah Halloween.

Tentu saja, MERGE pernyataan secara logis tidak lebih atau kurang mengisi lubang daripada INSERT...WHERE NOT EXISTS sintaksis. Perbedaannya adalah pengoptimal memiliki kendali penuh atas penerapan MERGE pernyataan, sedangkan INSERT sintaks akan mengharuskannya untuk mempertimbangkan semantik kueri yang lebih luas. Manusia dapat dengan mudah melihat bahwa INSERT juga mengisi lubang, tetapi pengoptimal tidak memikirkan hal-hal dengan cara yang sama seperti kita.

Untuk mengilustrasikan pencocokan persis persyaratan yang saya sebutkan, pertimbangkan sintaks kueri berikut, yang tidak manfaat dari optimasi pengisian lubang. Hasilnya adalah Perlindungan Halloween penuh yang disediakan oleh Eager Table Spool:

MERGE dbo.Demo AS d
USING dbo.Staging AS s ON
    s.SomeKey = d.SomeKey
WHEN NOT MATCHED THEN
    INSERT (SomeKey)
    VALUES (s.SomeKey * 1);

Satu-satunya perbedaan adalah perkalian dengan satu di VALUES klausa – sesuatu yang tidak mengubah logika kueri, tetapi cukup untuk mencegah penerapan pengoptimalan pengisian lubang.

Pengisian lubang dengan Loop Bersarang

Pada contoh sebelumnya, pengoptimal memilih untuk menggabungkan tabel menggunakan gabung Gabung. Pengoptimalan pengisian lubang juga dapat diterapkan jika gabungan Nested Loops dipilih, tetapi ini memerlukan jaminan keunikan ekstra pada tabel sumber, dan pencarian indeks di sisi dalam gabungan. Untuk melihat ini beraksi, kita dapat menghapus data staging yang ada, menambahkan keunikan pada indeks nonclustered, dan mencoba MERGE lagi:

-- Remove existing duplicate rows
TRUNCATE TABLE dbo.Staging;
 
-- Convert index to unique
CREATE UNIQUE NONCLUSTERED INDEX c 
ON dbo.Staging (SomeKey)
WITH (DROP_EXISTING = ON);
 
-- Sample data
INSERT dbo.Staging
    (SomeKey)
VALUES
    (1234),
    (5678);
 
-- Hole-filling merge
MERGE dbo.Demo AS d
USING dbo.Staging AS s ON
    s.SomeKey = d.SomeKey
WHEN NOT MATCHED THEN
    INSERT (SomeKey)
    VALUES (s.SomeKey);

Rencana eksekusi yang dihasilkan lagi-lagi menggunakan pengoptimalan pengisian lubang untuk menghindari Perlindungan Halloween, menggunakan gabungan loop bersarang dan pencarian sisi dalam ke tabel target:

Menghindari traversal indeks yang tidak perlu

Di mana optimasi pengisian lubang berlaku, mesin juga dapat menerapkan optimasi lebih lanjut. Itu dapat mengingat posisi indeks saat ini saat membaca tabel target (memproses satu baris pada satu waktu, ingat) dan menggunakan kembali informasi itu saat melakukan penyisipan, alih-alih mencari b-tree untuk menemukan lokasi penyisipan. Alasannya adalah bahwa posisi baca saat ini sangat mungkin berada di halaman yang sama di mana baris baru harus disisipkan. Memeriksa apakah baris tersebut memang milik halaman ini sangat cepat, karena hanya melibatkan pemeriksaan kunci terendah dan tertinggi yang saat ini disimpan di sana.

Kombinasi menghilangkan Eager Table Spool dan menyimpan navigasi indeks per baris dapat memberikan manfaat yang signifikan dalam beban kerja OLTP, asalkan rencana eksekusi diambil dari cache. Biaya kompilasi untuk MERGE pernyataan agak lebih tinggi daripada untuk INSERT , UPDATE dan DELETE , jadi rencana penggunaan kembali merupakan pertimbangan penting. Hal ini juga membantu untuk memastikan bahwa halaman memiliki ruang kosong yang cukup untuk menampung baris baru, menghindari pemisahan halaman. Ini biasanya dicapai melalui pemeliharaan indeks normal dan penetapan FILLFACTOR yang sesuai .

Saya menyebutkan beban kerja OLTP, yang biasanya menampilkan sejumlah besar perubahan yang relatif kecil, karena MERGE optimasi mungkin bukan pilihan yang baik di mana sejumlah besar baris diproses per pernyataan. Pengoptimalan lain seperti INSERTs yang di-log minimal saat ini tidak dapat digabungkan dengan pengisian lubang. Seperti biasa, karakteristik kinerja harus dijadikan tolok ukur untuk memastikan manfaat yang diharapkan terwujud.

Pengoptimalan pengisian lubang untuk MERGE sisipan dapat digabungkan dengan pembaruan dan penghapusan menggunakan MERGE tambahan klausa; setiap operasi pengubahan data dinilai secara terpisah untuk Masalah Halloween.

Menghindari bergabung

Optimalisasi terakhir yang akan kita lihat dapat diterapkan di mana MERGE pernyataan berisi operasi pembaruan dan penghapusan serta sisipan pengisi lubang, dan tabel target memiliki indeks berkerumun yang unik. Contoh berikut menunjukkan MERGE yang umum pola di mana baris yang tidak cocok dimasukkan, dan baris yang cocok diperbarui atau dihapus tergantung pada kondisi tambahan:

CREATE TABLE #T
(
    col1 integer NOT NULL,
    col2 integer NOT NULL,
 
    CONSTRAINT PK_T
        PRIMARY KEY (col1)
);
 
CREATE TABLE #S
(
    col1 integer NOT NULL,
    col2 integer NOT NULL,
 
    CONSTRAINT PK_S
        PRIMARY KEY (col1)
);
 
INSERT #T
    (col1, col2)
VALUES
    (1, 50),
    (3, 90);
 
INSERT #S
    (col1, col2)
VALUES
    (1, 40),
    (2, 80),
    (3, 90);

MERGE pernyataan yang diperlukan untuk membuat semua perubahan yang diperlukan sangat ringkas:

MERGE #T AS t
USING #S AS s ON t.col1 = s.col1
WHEN NOT MATCHED THEN INSERT VALUES (s.col1, s.col2)
WHEN MATCHED AND t.col2 - s.col2 = 0 THEN DELETE
WHEN MATCHED THEN UPDATE SET t.col2 -= s.col2;

Rencana eksekusinya cukup mengejutkan:

Tidak ada Perlindungan Halloween, tidak ada gabungan antara tabel sumber dan target, dan tidak sering Anda akan melihat operator Sisipan Indeks Cluster diikuti oleh Penggabungan Indeks Cluster ke tabel yang sama. Ini adalah pengoptimalan lain yang ditargetkan pada beban kerja OLTP dengan penggunaan ulang paket tinggi dan pengindeksan yang sesuai.

Idenya adalah untuk membaca satu baris dari tabel sumber dan segera mencoba memasukkannya ke dalam target. Jika pelanggaran kunci terjadi, kesalahan akan ditekan, operator Sisipkan menampilkan baris yang bertentangan yang ditemukannya, dan baris tersebut kemudian diproses untuk operasi pembaruan atau penghapusan menggunakan operator Gabungkan paket seperti biasa.

Jika penyisipan asli berhasil (tanpa pelanggaran kunci) pemrosesan dilanjutkan dengan baris berikutnya dari sumber (operator Gabung hanya memproses pembaruan dan penghapusan). Pengoptimalan ini terutama menguntungkan MERGE kueri di mana sebagian besar baris sumber menghasilkan sisipan. Sekali lagi, tolok ukur yang cermat diperlukan untuk memastikan kinerja lebih baik daripada menggunakan pernyataan terpisah.

Ringkasan

MERGE pernyataan memberikan beberapa peluang pengoptimalan yang unik. Dalam situasi yang tepat, ini dapat menghindari kebutuhan untuk menambahkan Perlindungan Halloween eksplisit dibandingkan dengan INSERT yang setara operasi, atau bahkan mungkin kombinasi INSERT , UPDATE , dan DELETE pernyataan. Tambahan MERGE -optimasi spesifik dapat menghindari traversal indeks b-tree yang biasanya diperlukan untuk menemukan posisi penyisipan untuk baris baru, dan juga dapat menghindari kebutuhan untuk menggabungkan tabel sumber dan target sepenuhnya.

Di bagian akhir seri ini, kita akan melihat bagaimana alasan pengoptimal kueri tentang perlunya perlindungan Halloween, dan mengidentifikasi beberapa trik lain yang dapat diterapkan untuk menghindari perlunya menambahkan Eager Table Spool ke rencana eksekusi yang mengubah data.

[ Bagian 1 | Bagian 2 | Bagian 3 | Bagian 4 ]


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Pengantar Statistik Tunggu

  2. Apakah operator string "+" begitu sederhana?

  3. Cara Mendapatkan Tanggal Kemarin di T-SQL

  4. Memahami Waktu Operator Rencana Eksekusi

  5. Operator Perbandingan SQL