Pertanyaan ini diposkan ke #sqlhelp oleh Jake Manske, dan dibawa ke perhatian saya oleh Erik Darling.
Saya tidak ingat pernah mengalami masalah kinerja dengan sys.partitions
. Pikiran awal saya (digaungkan oleh Joey D'Antoni) adalah bahwa filter pada data_compression
kolom harus hindari pemindaian yang berlebihan, dan kurangi waktu proses kueri hingga sekitar setengahnya. Namun, predikat ini tidak diturunkan, dan alasannya membutuhkan sedikit pembongkaran.
Mengapa sys.partitions lambat?
Jika Anda melihat definisi untuk sys.partitions
, pada dasarnya itulah yang Jake jelaskan – sebuah UNION ALL
dari semua partisi columnstore dan rowstore, dengan TIGA referensi eksplisit ke sys.sysrowsets
(sumber disingkat di sini):
BUAT LIHAT sys.partitions AS WITH partitions_columnstore(...cols...) AS ( SELECT ...cols..., cmprlevel AS data_compression ... FROM sys.sysrowsets rs OUTER APPLY OpenRowset(TABLE ALUCOUNT, rs .rowsetid, 0, 0, 0) ct-------- *** ^^^^^^^^^^^^^^^^ *** LEFT JOIN sys.syspalvalues cl ... MANA .. .sysconv(bit, rs.status &0x00010000) =1 -- Pertimbangkan hanya indeks dasar columnstore ), partitions_rowstore(...cols...) AS ( SELECT ...cols..., cmprlevel AS data_compression ... FROM sys.sysrowsets rs -------- *** ^^^^^^^^^^^^^^^^ *** KIRI BERGABUNG sys.syspalvalues cl ... WHERE ... sysconv(bit, rs .status &0x00010000) =0 -- Abaikan indeks dasar columnstore dan baris yatim. ) SELECT ...cols... from partitions_rowstore p OUTER APPLY OpenRowset(TABLE ALUCOUNT, p.partition_id, 0, 0, p.object_id) ct union semua SELECT ...cols... FROM partitions_columnstore sebagai P1 LEFT JOIN (SELECT ...cols... FROM sys.sysrowsets rs OUTER APP LY OpenRowset(TABEL ALUCOUNT, rs.rowsetid, 0, 0, 0) ct------- *** ^^^^^^^^^^^^^^ *** ) ...Pandangan ini tampaknya berbatu, mungkin karena masalah kompatibilitas ke belakang. Itu pasti bisa ditulis ulang agar lebih efisien, terutama hanya untuk referensi
sys.sysrowsets
danTABLE ALUCOUNT
objek sekali. Tapi tidak banyak yang bisa Anda atau saya lakukan tentang itu sekarang.Kolom
cmprlevel
berasal darisys.sysrowsets
(awalan alias pada referensi kolom akan sangat membantu). Anda akan berharap bahwa predikat terhadap kolom di sana akan terjadi secara logis sebelumOUTER APPLY
dan dapat mencegah salah satu pemindaian, tetapi bukan itu yang terjadi. Menjalankan kueri sederhana berikut:SELECT * FROM sys.partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0;Menghasilkan rencana berikut ketika ada indeks columnstore di database (klik untuk memperbesar):
Rencanakan partisi sys, dengan indeks columnstore yang ada
Dan berikut rencana bila tidak ada (klik untuk memperbesar):
Rencanakan partisi sys, tanpa indeks columnstore yang ada
Ini adalah perkiraan rencana yang sama, tetapi SentryOne Plan Explorer dapat menyorot ketika suatu operasi dilewati saat runtime. Ini terjadi untuk pemindaian ketiga dalam kasus terakhir, tetapi saya tidak tahu apakah ada cara untuk mengurangi jumlah pemindaian runtime itu lebih lanjut; pemindaian kedua terjadi bahkan ketika kueri mengembalikan nol baris.
Dalam kasus Jake, dia memiliki banyak objek, jadi melakukan pemindaian ini bahkan dua kali terlihat, menyakitkan, dan satu kali terlalu banyak. Dan sejujurnya saya tidak tahu apakah
TABLE ALUCOUNT
, panggilan loopback internal dan tidak terdokumentasi, juga harus memindai beberapa objek yang lebih besar ini beberapa kali.Melihat kembali ke sumbernya, saya bertanya-tanya apakah ada predikat lain yang dapat diteruskan ke tampilan yang dapat memaksa bentuk rencana, tetapi saya benar-benar tidak berpikir ada sesuatu yang dapat berdampak.
Apakah tampilan lain akan berfungsi?
Namun, kita bisa mencoba pandangan yang berbeda sama sekali. Saya mencari tampilan lain yang berisi referensi ke
sys.sysrowsets
danALUCOUNT
, dan ada beberapa yang muncul dalam daftar, tetapi hanya dua yang menjanjikan:sys.internal_partitions
dansys.system_internals_partitions
.sys.internal_partitions
Saya mencoba
sys.internal_partitions
pertama:SELECT * FROM sys.internal_partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0;Tapi rencananya tidak jauh lebih baik (klik untuk memperbesar):
Rencanakan untuk sys.internal_partitions
Hanya ada dua pemindaian terhadap
sys.sysrowsets
kali ini, tetapi pemindaian tetap tidak relevan karena kueri tidak mendekati menghasilkan baris yang kami minati. Kami hanya melihat baris untuk objek terkait columnstore (seperti yang dinyatakan dalam dokumentasi).sys.system_internals_partitions
Mari kita coba
sys.system_internals_partitions
. Saya sedikit khawatir tentang ini, karena tidak didukung (lihat peringatannya di sini), tetapi bersabarlah sebentar:SELECT * FROM sys.system_internals_partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0;Dalam database dengan indeks columnstore, ada pemindaian terhadap
sys.sysschobjs
, tapi sekarang hanya satu pindai terhadapsys.sysrowsets
(klik untuk memperbesar):Rencanakan sys.system_internals_partitions, dengan indeks columnstore yang ada
Jika kita menjalankan kueri yang sama dalam database tanpa indeks penyimpanan kolom, rencananya akan lebih sederhana, dengan pencarian terhadap
sys.sysschobjs
(klik untuk memperbesar):Rencanakan sys.system_internals_partitions, tanpa indeks columnstore yang ada
Namun, ini tidak cukup apa yang kita cari, atau setidaknya tidak seperti apa yang Jake kejar, karena ini juga menyertakan artefak dari indeks columnstore. Jika kita menambahkan filter ini, keluaran aktual sekarang cocok dengan kueri sebelumnya yang jauh lebih mahal:
SELECT * FROM sys.system_internals_partitions AS p INNER JOIN sys.objects AS o ON p.object_id =o.object_id WHERE o.is_ms_shipped =0 AND p.is_columnstore =0 AND p.is_orphaned =0;Sebagai bonus, pindai terhadap
sys.sysschobjs
telah menjadi pencarian bahkan di database dengan objek columnstore. Sebagian besar dari kita tidak akan melihat perbedaan itu, tetapi jika Anda berada dalam skenario seperti Jake, Anda mungkin (klik untuk memperbesar):Paket yang lebih sederhana untuk sys.system_internals_partitions, dengan filter tambahan
sys.system_internals_partitions
memperlihatkan kumpulan kolom yang berbeda darisys.partitions
(beberapa benar-benar berbeda, yang lain memiliki nama baru) jadi, jika Anda menggunakan output hilir, Anda harus menyesuaikannya. Anda juga ingin memvalidasi bahwa ia mengembalikan semua informasi yang Anda inginkan di seluruh indeks rowstore, memori yang dioptimalkan, dan columnstore, dan jangan lupa tentang tumpukan sial itu. Dan, akhirnya, bersiaplah untuk meninggalkans
diinternals
banyak, berkali-kali.Kesimpulan
Seperti yang saya sebutkan di atas, tampilan sistem ini tidak didukung secara resmi, sehingga fungsinya dapat berubah sewaktu-waktu; itu juga bisa dipindahkan di bawah Dedicated Administrator Connection (DAC), atau dihapus dari produk sama sekali. Jangan ragu untuk menggunakan pendekatan ini jika
sys.partitions
tidak bekerja dengan baik untuk Anda tetapi, tolong, pastikan Anda memiliki rencana cadangan. Dan pastikan itu didokumentasikan sebagai sesuatu yang Anda uji regresi saat Anda mulai menguji versi SQL Server mendatang, atau setelah Anda memutakhirkan, untuk berjaga-jaga.