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

Bagaimana indeks yang difilter bisa menjadi fitur yang lebih kuat

Jangan salah paham; Saya suka indeks yang difilter. Mereka menciptakan peluang untuk penggunaan I/O yang jauh lebih efisien, dan akhirnya memungkinkan kami untuk menerapkan batasan unik yang sesuai dengan ANSI (di mana lebih dari satu NULL diperbolehkan). Namun, mereka jauh dari sempurna. Saya ingin menunjukkan beberapa area di mana indeks yang difilter dapat ditingkatkan dan membuatnya jauh lebih berguna dan praktis untuk sebagian besar beban kerja di luar sana.

Pertama, kabar baiknya

Indeks yang difilter dapat mengerjakan kueri yang sebelumnya mahal dengan sangat cepat, dan melakukannya dengan menggunakan lebih sedikit ruang (dan karenanya mengurangi I/O, bahkan saat dipindai).

Contoh cepat menggunakan Sales.SalesOrderDetailEnlarged (dibangun menggunakan skrip ini oleh Jonathan Kehayias (@SQLPoolBoy)). Tabel ini memiliki 4,8 MM baris, dengan 587 MB data dan 363 MB indeks. Hanya ada satu kolom yang dapat dibatalkan, CarrierTrackingNumber , jadi mari kita bermain dengan yang itu. Seperti, tabel saat ini memiliki sekitar setengah dari nilai-nilai ini (2.4MM) sebagai NULL. Saya akan menguranginya menjadi sekitar 240K untuk mensimulasikan skenario di mana sebagian kecil baris dalam tabel benar-benar memenuhi syarat untuk indeks, untuk menyoroti manfaat indeks yang difilter. Kueri berikut memengaruhi 2,17 MM baris, menyisakan 241.507 baris dengan nilai NULL untuk CarrierTrackingNumber :

UPDATE Sales.SalesOrderDetailEnlarged 
    SET CarrierTrackingNumber = 'x'
      WHERE CarrierTrackingNumber IS NULL
      AND SalesOrderID % 10 <> 3;

Sekarang, katakanlah ada persyaratan bisnis di mana kami terus-menerus ingin meninjau pesanan yang memiliki produk yang belum diberi nomor pelacakan (pikirkan pesanan yang dibagi dan dikirim secara terpisah). Pada tabel saat ini kami akan menjalankan kueri ini (dan saya telah menambahkan perintah DBCC untuk memastikan cache dingin dalam setiap kasus):

DBCC DROPCLEANBUFFERS;
DBCC FREEPROCCACHE;
 
SELECT COUNT(*)
  FROM Sales.SalesOrderDetailEnlarged 
  WHERE CarrierTrackingNumber IS NULL;
 
SELECT ProductID, SalesOrderID
  FROM Sales.SalesOrderDetailEnlarged
  WHERE CarrierTrackingNumber IS NULL;

Yang memerlukan pemindaian indeks berkerumun dan menghasilkan metrik runtime berikut (seperti yang ditangkap dengan SQL Sentry Plan Explorer):

Di masa "lama" (artinya sejak SQL Server 2005), kami akan membuat indeks ini (dan pada kenyataannya, bahkan di SQL Server 2012, ini adalah indeks yang direkomendasikan SQL Server):

CREATE INDEX IX_NotVeryHelpful
ON [Sales].[SalesOrderDetailEnlarged] ([CarrierTrackingNumber])
INCLUDE ([SalesOrderID],[ProductID]);

Dengan indeks tersebut, dan menjalankan kueri di atas lagi, berikut adalah metriknya, dengan kedua kueri menggunakan pencarian indeks seperti yang Anda harapkan:

Kemudian hapus indeks itu dan buat indeks yang sedikit berbeda, cukup tambahkan WHERE klausa:

CREATE INDEX IX_Filtered_CTNisNULL
ON [Sales].[SalesOrderDetailEnlarged] ([CarrierTrackingNumber])
INCLUDE ([SalesOrderID],[ProductID])
WHERE CarrierTrackingNumber IS NULL;

Kami mendapatkan hasil ini, dan kedua kueri menggunakan indeks yang difilter untuk pencarian mereka:

Berikut adalah ruang tambahan yang diperlukan oleh setiap indeks, dibandingkan dengan pengurangan runtime dan I/O dari kueri di atas:

Indeks Ruang indeks Menambahkan ruang Durasi Baca Tidak ada indeks khusus 363 MB 15.700 md ~164,000 Indeks tanpa filter 530 MB 167 MB (+46%) 169ms 1.084 Indeks yang difilter 367 MB 4 MB (+1%) 170 md 1.084


Jadi, seperti yang Anda lihat, indeks yang difilter memberikan peningkatan kinerja yang hampir identik dengan indeks yang tidak difilter (karena keduanya dapat memperoleh datanya menggunakan jumlah pembacaan yang sama), tetapi pada penyimpanan yang jauh lebih rendah biaya, karena indeks yang difilter hanya perlu menyimpan dan memelihara baris yang cocok dengan predikat filter.

Sekarang, mari kembalikan tabel ke keadaan semula:

UPDATE Sales.SalesOrderDetailEnlarged
  SET CarrierTrackingNumber = NULL
  WHERE CarrierTrackingNumber = 'x';
 
DROP INDEX IX_NotVeryHelpful ON Sales.SalesOrderDetailEnlarged;
DROP INDEX IX_Filtered_CTNisNULL ON Sales.SalesOrderDetailEnlarged;

Tim Chapman (@chapmandew) dan Michelle Ufford (@sqlfool) telah melakukan pekerjaan yang fantastis dengan menguraikan manfaat kinerja indeks yang difilter dengan cara mereka sendiri, dan Anda harus memeriksa posting mereka juga:

  • Michelle Ufford:Indeks yang Difilter:Yang Perlu Anda Ketahui
  • Tim Chapman:Kegembiraan Indeks yang Difilter

Juga, batasan unik yang sesuai dengan ANSI (semacam)

Saya pikir saya juga akan secara singkat menyebutkan batasan unik yang sesuai dengan ANSI. Di SQL Server 2005, kami akan membuat batasan unik seperti ini:

CREATE TABLE dbo.Personnel
(
  EmployeeID INT PRIMARY KEY,
  SSN CHAR(9) NULL,
  -- ... other columns ...
  CONSTRAINT UQ_SSN UNIQUE(SSN)
);

(Kami juga dapat membuat indeks non-cluster yang unik alih-alih kendala; implementasi dasarnya pada dasarnya sama.)

Sekarang, ini bukan masalah jika SSN diketahui pada saat masuk:

INSERT dbo.Personnel(EmployeeID, SSN)
VALUES(1,'111111111'),(2,'111111112');

Tidak masalah juga jika kita memiliki SSN sesekali yang tidak diketahui pada saat masuk (pikirkan pemohon Visa atau bahkan mungkin pekerja asing yang tidak memiliki SSN dan tidak akan pernah):

INSERT dbo.Personnel(EmployeeID, SSN)
VALUES(3,NULL);

Sejauh ini baik. Tapi apa yang terjadi ketika kita memiliki detik karyawan dengan SSN yang tidak dikenal?

INSERT dbo.Personnel(EmployeeID, SSN)
VALUES(4,NULL);

Hasil:

Msg 2627, Level 14, State 1, Line 1
Pelanggaran batasan UNIQUE KEY 'UQ_SSN'. Tidak dapat menyisipkan kunci duplikat di objek 'dbo.Personnel'. Nilai kunci duplikatnya adalah ().
Pernyataan telah dihentikan.

Jadi pada satu waktu, hanya satu nilai NULL yang bisa ada di kolom ini. Tidak seperti kebanyakan skenario, ini adalah satu kasus di mana SQL Server memperlakukan dua nilai NULL sebagai sama (daripada menentukan bahwa kesetaraan tidak diketahui dan, pada gilirannya, salah). Orang-orang telah mengeluh tentang ketidakkonsistenan ini selama bertahun-tahun.

Jika ini merupakan persyaratan, sekarang kita dapat mengatasinya menggunakan indeks yang difilter:

ALTER TABLE dbo.Personnel DROP CONSTRAINT UQ_SSN;
GO
 
CREATE UNIQUE INDEX UQ_SSN ON dbo.Personnel(SSN)
  WHERE SSN IS NOT NULL;

Sekarang sisipan ke-4 kami berfungsi dengan baik, karena keunikan hanya diterapkan pada nilai non-NULL. Ini adalah jenis kecurangan, tetapi memenuhi persyaratan dasar yang dimaksudkan oleh standar ANSI (walaupun SQL Server tidak mengizinkan kita untuk menggunakan ALTER TABLE ... ADD CONSTRAINT sintaks untuk membuat batasan unik yang difilter).

Tapi, pegang teleponnya

Ini adalah contoh yang bagus tentang apa yang dapat kita lakukan dengan indeks yang difilter, tetapi ada banyak hal yang masih tidak dapat kita lakukan, dan beberapa keterbatasan dan masalah yang muncul sebagai akibatnya.

Pembaruan statistik

Ini adalah salah satu batasan IMHO yang lebih penting. Indeks yang difilter tidak mendapat manfaat dari pembaruan otomatis statistik berdasarkan persentase perubahan subset tabel yang diidentifikasi oleh predikat filter; itu didasarkan (seperti semua indeks yang tidak difilter) pada churn terhadap seluruh tabel. Ini berarti, bergantung pada persentase tabel dalam indeks yang difilter, jumlah baris dalam indeks dapat menjadi empat kali lipat atau separuh dan statistik tidak akan diperbarui kecuali Anda melakukannya secara manual. Kimberly Tripp telah memberikan beberapa informasi hebat tentang ini (dan Gail Shaw mengutip contoh di mana dibutuhkan 257.000 pembaruan sebelum statistik diperbarui untuk indeks yang difilter yang hanya berisi 10.000 baris):

http://www.sqlskills.com/blogs/kimberly/filtered-indexes-and-filtered-stats-might-become-serius-out-of-date/
http://www.sqlskills.com/ blogs/kimberly/category/filtered-indexes/

Selain itu, rekan Kimberly, Joe Sack (@JosephSack), telah mengajukan item Connect yang menyarankan untuk memperbaiki perilaku ini untuk indeks yang difilter dan statistik yang difilter.

Filter batasan ekspresi

Ada beberapa konstruksi yang tidak dapat Anda gunakan dalam predikat filter, seperti NOT IN , OR dan predikat dinamis/non-deterministik seperti WHERE col >= DATEADD(DAY, -1, GETDATE()) . Selain itu, pengoptimal mungkin tidak mengenali indeks yang difilter jika predikatnya tidak sama persis dengan WHERE klausa dalam definisi indeks. Berikut adalah beberapa item Connect yang mencoba membujuk beberapa dukungan untuk cakupan yang lebih baik di sini:

Indeks yang difilter tidak mengizinkan filter pada disjungsi (ditutup:sesuai desain)
Pembuatan indeks yang difilter gagal dengan klausa NOT IN (ditutup:sesuai desain)
Dukungan untuk klausa WHERE yang lebih kompleks dalam indeks yang difilter (aktif)

Penggunaan potensial lainnya saat ini tidak memungkinkan

Saat ini kami tidak dapat membuat indeks yang difilter pada kolom terkomputasi yang bertahan, meskipun itu deterministik. Kami tidak dapat mengarahkan kunci asing ke indeks terfilter yang unik; jika kita ingin indeks mendukung kunci asing selain kueri yang didukung oleh indeks yang difilter, kita harus membuat indeks kedua, redundan, non-filter. Dan berikut adalah beberapa batasan serupa lainnya yang telah diabaikan atau belum dipertimbangkan:

Seharusnya dapat membuat indeks yang difilter pada kolom komputasi tetap deterministik (aktif)
Izinkan indeks unik yang difilter menjadi kunci kandidat untuk kunci asing (aktif)
kemampuan untuk membuat indeks filter pada tampilan yang diindeks (ditutup:tidak akan diperbaiki)
Kesalahan Partisi 1908 – Tingkatkan Partisi (ditutup:tidak akan diperbaiki)
BUAT INDEKS TOKO KOLOM "DIFILTER" (aktif)

Masalah dengan MERGE

Dan MERGE muncul lagi di daftar "hati-hati" saya:

MERGE mengevaluasi indeks yang difilter per baris, bukan setelah operasi, yang menyebabkan pelanggaran indeks yang difilter (ditutup:tidak akan diperbaiki)
MERGE gagal memperbarui dengan indeks yang difilter pada tempatnya (tutup:diperbaiki)
MERGE statement bug ketika INSERT/DELETE menggunakan dan memfilter indeks (aktif)
MERGE Salah Melaporkan Pelanggaran Kunci Unik (aktif)


Sementara salah satu bug ini (tampaknya terkait erat) mengatakan bahwa itu diperbaiki di SQL Server 2012, Anda mungkin perlu menghubungi PSS jika Anda menemukan variasi apa pun dari masalah ini, terutama pada versi sebelumnya (atau berhenti menggunakan MERGE , seperti yang telah saya sarankan sebelumnya).

Keterbatasan alat / DMV / bawaan

Ada banyak DMV, perintah DBCC, prosedur sistem, dan alat klien yang mulai kita andalkan seiring waktu. Namun, tidak semua hal ini diperbarui untuk memanfaatkan fitur baru; indeks yang difilter tidak terkecuali. Item Connect berikut menunjukkan beberapa masalah yang mungkin membuat Anda tersandung jika Anda mengharapkannya berfungsi dengan indeks yang difilter:

Tidak ada cara untuk membuat indeks yang difilter dari SSMS saat mendesain tabel baru (ditutup:tidak akan diperbaiki)
Ekspresi filter dari indeks yang difilter akan hilang saat tabel dimodifikasi oleh Perancang Tabel (ditutup:tidak akan diperbaiki)
Perancang tabel tidak membuat skrip klausa WHERE dalam indeks yang difilter (aktif)
Perancang tabel SSMS tidak mempertahankan ekspresi filter indeks pada pembuatan ulang tabel (ditutup:tidak akan diperbaiki)
DBCC PAGE keluaran salah dengan indeks yang difilter (aktif)
Saran Indeks yang Difilter SQL 2008 dari Tampilan DM dan DTA (ditutup:tidak akan diperbaiki)
Peningkatan pada indeks yang hilang DMV untuk indeks yang difilter (ditutup:tidak akan diperbaiki)
Kesalahan sintaks saat mereplikasi indeks terkompresi yang difilter (ditutup:tidak akan diperbaiki)
Agen:pekerjaan menggunakan opsi non-default saat menjalankan skrip T-SQL (ditutup:tidak akan diperbaiki)
Lihat Dependensi gagal dengan Transact-SQL Error 515 (aktif)
Gagal melihat Dependensi pada objek tertentu (ditutup:tidak akan diperbaiki)
Perbedaan opsi indeks tidak terdeteksi dalam skema perbandingan untuk dua database (tutup:eksternal)
Sarankan untuk mengekspos kondisi filter indeks di semua tampilan informasi indeks (ditutup:tidak akan diperbaiki)
hasil sp_helpIndex harus menyertakan ekspresi Filter dari Indeks Filter (aktif)
Kelebihan fitur sp_help, sp_columns, sp_helpindex untuk 2008 (ditutup:tidak akan diperbaiki)


Untuk tiga yang terakhir, jangan menahan nafas – Microsoft tidak mungkin menginvestasikan waktu dalam prosedur sp_, DMV, tampilan INFORMATION_SCHEMA, dll. Lihat penulisan ulang sp_helpindex Kimberly Tripp, yang mencakup informasi tentang indeks yang difilter di sepanjang dengan fitur baru lainnya yang ditinggalkan Microsoft.

Batasan Pengoptimal

Ada beberapa item Connect yang menjelaskan kasus di mana indeks yang difilter *dapat* digunakan oleh pengoptimal, tetapi diabaikan. Dalam beberapa kasus, ini tidak dianggap "bug" melainkan "celah dalam fungsionalitas"…

SQL tidak menggunakan indeks yang difilter pada kueri sederhana (ditutup:sesuai desain)
Rencana eksekusi Indeks yang Difilter tidak dioptimalkan (ditutup:tidak akan diperbaiki)
Indeks yang difilter tidak digunakan dan pencarian kunci tanpa keluaran (ditutup:tidak akan diperbaiki)
Penggunaan Filtered Index pada BIT Column bergantung pada ekspresi SQL yang digunakan dalam klausa WHERE (aktif)
Kueri server tertaut tidak dioptimalkan dengan benar saat ada indeks unik yang difilter (ditutup:tidak akan diperbaiki)
Row_Number() memberikan hasil yang tidak terduga pada Server Tertaut tempat Indeks Terfilter digunakan (Tutup:tidak ada pengulangan)
Indeks yang difilter jelas tidak digunakan oleh QP (ditutup:sesuai desain)
Mengenali indeks yang difilter unik sebagai unik (aktif)


Paul White (@SQL_Kiwi) baru-baru ini memposting di sini di SQLPerformance.com sebuah postingan yang menjelaskan secara detail tentang beberapa batasan pengoptimal di atas.

Dan Tim Chapman menulis posting bagus yang menguraikan beberapa batasan lain dari indeks yang difilter – seperti ketidakmampuan untuk mencocokkan predikat dengan variabel lokal (diperbaiki pada 2008 R2 SP1) dan ketidakmampuan untuk menentukan indeks yang difilter dalam petunjuk indeks.

Kesimpulan

Indeks yang difilter memiliki potensi besar dan saya memiliki harapan yang sangat tinggi saat pertama kali diperkenalkan di SQL Server 2008. Namun, sebagian besar batasan yang disertakan dengan versi pertama masih ada hingga saat ini, satu setengah (atau dua, tergantung pada Anda perspektif) rilis utama nanti. Di atas sepertinya daftar cucian yang cukup luas dari barang-barang yang perlu ditangani, tetapi saya tidak bermaksud untuk menemukan cara itu. Saya hanya ingin orang-orang menyadari sejumlah besar potensi masalah yang mungkin perlu mereka pertimbangkan saat memanfaatkan indeks yang difilter.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Menghubungkan ke Teradata di IRI Workbench

  2. Membuat lingkungan pengujian dari repositori produksi

  3. Hasilkan satu set atau urutan tanpa loop – bagian 3

  4. Resensi Buku :Benjamin Nevarez :Penyetelan &Pengoptimalan Kueri

  5. Jenis Data SQL VARCHAR Yang Harus dan Tidak Boleh Dilakukan untuk Basis Data yang Lebih Cepat