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

GUNAKAN PETUNJUK dan DISABLE_OPTIMIZED_NESTED_LOOP

Salah satu algoritma yang tersedia untuk menggabungkan dua tabel di SQL Server adalah Nested Loops. Gabung loop bersarang menggunakan satu input gabung sebagai tabel input luar dan satu sebagai tabel input dalam. Loop luar mengulangi baris tabel input luar demi baris. Loop dalam, dieksekusi untuk setiap baris luar, mencari baris yang cocok di tabel input dalam.

Ini disebut naif nested loop join.

Jika Anda memiliki indeks pada kondisi gabungan di tabel input dalam, maka tidak perlu melakukan loop dalam untuk setiap baris tabel luar. Sebagai gantinya, Anda dapat meneruskan nilai dari tabel eksternal sebagai argumen pencarian, dan menghubungkan semua baris yang dikembalikan dari tabel dalam ke baris dari tabel luar.

Pencarian dengan tabel bagian dalam adalah akses acak. SQL Server, mulai dari versi 2005, memiliki pengoptimalan pengurutan batch (jangan bingung dengan operator Urutkan dalam Mode Batch untuk indeks Columnstore). Tujuan dari optimasi adalah untuk memesan kunci pencarian dari tabel eksternal sebelum mendapatkan data dari tabel internal. Dengan demikian, akses acak akan berurutan.

Rencana eksekusi tidak menampilkan operasi pengurutan batch sebagai operator terpisah. Sebagai gantinya, Anda dapat melihat properti Optimized=true di operator Nested Loops. Jika memungkinkan untuk melihat sortir batch sebagai operator terpisah dalam rencana, itu akan terlihat seperti berikut:

Dalam rencana semu ini, kami membaca data dari indeks ix_CustomerID non-cluster dalam urutan kunci indeks CustomerID ini. Kemudian, kita perlu melakukan Pencarian Kunci di indeks berkerumun, karena ix_CustomerID bukan indeks penutup. Pencarian Kunci adalah operasi pencarian kunci indeks berkerumun – akses acak. Untuk membuatnya berurutan, SQL Server dapat mengeksekusi pengurutan batch berdasarkan kunci indeks berkerumun.

Untuk mempelajari lebih lanjut tentang pengurutan batch, silakan lihat artikel saya Pengurutan Batch dan Loop Bersarang.

Pengoptimalan ini memberikan dorongan besar dengan jumlah baris yang cukup. Anda dapat membaca lebih lanjut tentang hasil pengujiannya di blog OPTIMIZED Nested Loops Joins, yang dibuat oleh Craig Freedman, seorang pengembang pengoptimal.

Namun, jika jumlah baris sebenarnya kurang dari yang diharapkan, maka biaya tambahan CPU untuk membuat jenis ini dapat menyembunyikan manfaatnya, meningkatkan konsumsi CPU, dan mengurangi kinerjanya.

Pertimbangkan contoh khusus ini:

use tempdb;
go
-- create a test table (SalesOrderID - clustered PK)
create table dbo.SalesOrder(SalesOrderID int identity primary key, CustomerID int not null, SomeData char(200) not null);
go
-- add test data
with n as (select top(1000000) rn = row_number() over(order by (select null)) from sys.all_columns c1,sys.all_columns c2)
insert dbo.SalesOrder(CustomerID, SomeData) select rn%500000, str(rn,100) from n;
-- create a clustered index
create index ix_c on dbo.Salesorder(CustomerID);
go
-- the batch sort optimization is enabled by default (Nested Loops: Optimized = true)
select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000;
-- disable it with the DISABLE_OPTIMIZED_NESTED_LOOP hint (Nested Loops: Optimized = false)
select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000 option(use hint('DISABLE_OPTIMIZED_NESTED_LOOP'));
go

Hasil yang dikembalikan:

Saya ingin menarik perhatian Anda pada urutan baris yang berbeda dalam output. Server mengembalikan baris dalam urutan yang memprosesnya karena kami belum secara eksplisit menentukan ORDER BY. Dalam kasus pertama, kami secara bertahap membaca dari indeks ix_c. Namun, untuk mengoptimalkan pembacaan acak dari indeks berkerumun, kami memfilter baris menurut kunci indeks SalesOrderID yang dikelompokkan. Dalam kasus kedua, tidak ada pengurutan, serta pembacaan dilakukan dalam urutan kunci ID Pelanggan dari indeks nonclustered ix_c.

Perbedaan dari bendera jejak 2340

Terlepas dari kenyataan bahwa dokumentasi menetapkan bendera pelacakan 2340 sebagai setara dengan petunjuk DISABLE_OPTIMIZED_NESTED_LOOP, itu sebenarnya tidak benar.

Perhatikan contoh berikut di mana saya akan menggunakan UPDATE STATISTICS ... WITH PAGECOUNT perintah tidak berdokumen untuk menipu pengoptimal dengan mengatakan bahwa tabel membutuhkan lebih banyak halaman daripada yang sebenarnya. Kemudian, lihat pertanyaan berikut:

  1. Tanpa petunjuk apa pun (saya telah menambahkan MAXDOP untuk membuat rencana sederhana);
  2. Dengan petunjuk DISABLE_OPTIMIZED_NESTED_LOOP;
  3. Dengan bendera jejak 2340.
-- create a huge table
update statistics dbo.SalesOrder with pagecount = 100000;
go
set showplan_xml on;
go
-- 1. without hints
select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000000 option(maxdop 1);
-- 2. hint
select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000000 option(use hint('DISABLE_OPTIMIZED_NESTED_LOOP'), maxdop 1);
-- 3. trace flag
select * from dbo.SalesOrder with(index(ix_c)) where CustomerID < 1000000 option(querytraceon 2340, maxdop 1);
go
set showplan_xml off;
go

Hasilnya, kami memiliki rencana berikut:

Loop Bersarang memiliki properti Optimized =false di ketiga paket. Faktanya adalah bahwa dengan meningkatkan lebar tabel, kami juga meningkatkan biaya akses data. Ketika biayanya cukup tinggi, SQL Server dapat menggunakan operator Sortir eksplisit, daripada operator pengurutan batch implisit. Kita bisa melihat ini di rencana kueri pertama.

Dalam kueri kedua, kami menggunakan petunjuk DISABLE_OPTIMIZED_NESTED_LOOP yang menonaktifkan pengurutan batch implisit. Namun, ini menghapus penyortiran eksplisit oleh operator terpisah.

Dalam rencana ketiga, kita dapat melihat bahwa meskipun menambahkan tanda jejak 2340, operator pengurutan tetap ada.

Jadi, perbedaan petunjuk dari flag adalah sebagai berikut:petunjuk menonaktifkan pengoptimalan dengan mengubah akses acak menjadi akses serial tergantung pada fakta apakah server mengimplementasikannya dengan pengurutan batch implisit atau dengan operator Sortir yang terpisah.

P.S. Rencana mungkin tergantung pada peralatan. Jadi, jika Anda gagal menjalankan kueri ini, coba tambah atau kurangi ukuran kolom SomeData char(200) di deskripsi tabel dbo.SalesOrder.

Artikel diterjemahkan oleh tim Codingsight dengan izin penulis.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagian 2 – Cara Mengatur Diagram Basis Data Besar

  2. Tip Cepat – Mempercepat Pemulihan Lambat dari Log Transaksi

  3. Ketuk dan Parkir:Model Data Aplikasi Parkir

  4. Labu dengan Contoh – Menyiapkan Postgres, SQLAlchemy, dan Alembic

  5. Bagaimana tidak Menampilkan Duplikat dalam SQL