Apakah ada cara untuk mencegah Scalar UDFs di kolom yang dikomputasi dari menghambat paralelisme?

29

Banyak yang telah ditulis tentang bahaya Scalar UDFs di SQL Server. Pencarian biasa akan menghasilkan banyak sekali hasil.

Ada beberapa tempat di mana UDF Skalar adalah satu-satunya pilihan.

Sebagai contoh: ketika berhadapan dengan XML: XQuery tidak dapat digunakan sebagai definisi kolom yang dikomputasi. Salah satu opsi yang didokumentasikan oleh Microsoft adalah menggunakan Scalar UDF untuk merangkum XQuery Anda dalam Scalar UDF, dan kemudian menggunakannya dalam kolom yang dihitung.

Ini memiliki berbagai efek, dan beberapa solusi.

  • Menjalankan baris demi baris saat tabel ditanyai
  • Memaksa semua kueri terhadap tabel untuk dijalankan secara seri

Anda bisa menyiasati eksekusi baris-demi-baris dengan meringkas fungsi, dan mempertahankan kolom yang dihitung, atau mengindeksnya. Tidak satu pun dari metode tersebut dapat mencegah serialisasi paksa dari kueri yang mengenai tabel, bahkan ketika skalar UDF tidak direferensikan.

Apakah ada cara yang diketahui untuk melakukan itu?

Erik Darling
sumber

Jawaban:

31

Ya jika Anda:

  • menjalankan SQL Server 2014 atau yang lebih baru; dan
  • dapat menjalankan kueri dengan tanda jejak 176 aktif; dan
  • kolom yang dihitung adalah PERSISTED

Secara khusus, setidaknya diperlukan versi berikut :

  • Pembaruan Kumulatif 2 untuk SQL Server 2016 SP1
  • Pembaruan kumulatif 4 untuk SQL Server 2016 RTM
  • Pembaruan kumulatif 6 untuk SQL Server 2014 SP2

TETAPI untuk menghindari bug (referensi untuk 2014 , dan untuk 2016 dan 2017 ) yang diperkenalkan di perbaikan itu, sebaliknya berlaku:

Bendera jejak efektif sebagai –Topsi start-up , baik menggunakan lingkup global dan sesi DBCC TRACEON, dan per kueri dengan OPTION (QUERYTRACEON)atau panduan paket.

Bendera jejak 176 mencegah perluasan kolom komputer tetap.

Pemuatan metadata awal yang dilakukan saat kompilasi kueri menghadirkan semua kolom, bukan hanya yang direferensikan secara langsung. Ini membuat semua definisi kolom yang dihitung tersedia untuk pencocokan, yang umumnya merupakan hal yang baik.

Sebagai efek samping yang tidak menguntungkan, jika salah satu kolom yang dimuat (dihitung) menggunakan fungsi skalar yang ditentukan pengguna, keberadaannya menonaktifkan paralelisme untuk seluruh kueri, bahkan ketika kolom yang dikomputasi tidak benar-benar digunakan .

Bendera jejak 176 membantu dengan ini, jika kolom tetap ada, dengan tidak memuat definisi (karena ekspansi dilewati). Dengan cara ini, fungsi skalar yang ditentukan pengguna tidak pernah ada di pohon permintaan kompilasi, sehingga paralelisme tidak dinonaktifkan.

Kelemahan utama dari bendera jejak 176 (selain hanya didokumentasikan dengan ringan) adalah bahwa ia juga mencegah pencocokan ekspresi kueri ke kolom yang dihitung tetap: Jika kueri berisi ekspresi yang cocok dengan kolom yang dihitung tetap, jejak bendera 176 akan mencegah ekspresi diganti oleh referensi ke kolom yang dihitung.

Untuk detail lebih lanjut, lihat artikel SQLPerformance.com saya, Kolom Dihitung yang Ditetap dengan Benar .

Karena pertanyaan menyebutkan XML, sebagai alternatif untuk mempromosikan nilai menggunakan kolom yang dikomputasi dan fungsi skalar, Anda juga dapat melihat menggunakan Indeks XML Selektif, seperti yang Anda tulis dalam Indeks XML Selektif: Tidak Buruk Sama sekali .

Paul White mengatakan GoFundMonica
sumber
10

Selain dari Ya Paul # 1 yang sangat baik , sebenarnya ada Ya # 2 yang:

  • bekerja sejauh SQL Server 2005,
  • tidak memerlukan pengaturan tanda jejak,
  • tidak tidak mengharuskan kolom dihitung menjadi PERSISTED, dan
  • (karena tidak memerlukan jejak bendera 176), tidak mencegah pencocokan ekspresi kueri ke kolom yang dihitung tetap

Satu-satunya kelemahan (sejauh yang saya tahu) adalah:

  • tidak bekerja pada Azure SQL Database (setidaknya belum, meskipun itu berfungsi pada Amazon RDS SQL Server serta SQL Server di Linux), dan
  • sedikit di luar zona kenyamanan banyak DBA

Dan opsi ini adalah: SQLCLR

Betul sekali. Salah satu aspek keren dari SQLCLR skalar UDFS adalah bahwa, jika mereka tidak melakukan apa pun akses data (tidak pengguna atau sistem), maka mereka tidak melarang paralelisme. Dan itu bukan hanya teori atau pemasaran. Walaupun saya tidak punya waktu (saat ini) untuk melakukan penulisan yang terperinci, saya telah menguji dan membuktikannya.

Saya menggunakan pengaturan awal dari posting blog berikut (semoga OP tidak menganggap ini sebagai sumber yang tidak dapat diandalkan 🙃):

Jeans Ide Buruk: Beberapa Petunjuk Indeks

Dan melakukan tes berikut:

  1. Jalankan kueri awal seperti ─⇾ Paralelisme (seperti yang diharapkan)
  2. Menambahkan kolom yang dihitung tetap yang didefinisikan sebagai ([c2] * [c3])─⇾ Paralelisme (seperti yang diharapkan)
  3. Menghapus kolom yang dikomputasi dan menambahkan kolom yang dikomputasi non-persisten yang mereferensikan T-SQL Scalar UDF (dibuat dengan SCHEMABINDING) yang didefinisikan sebagai RETURN (@First * @Second);─⇾ TIDAK Paralelisme (seperti yang diharapkan)
  4. Menghapus kolom yang dihitung T-SQL UDF dan menambahkan kolom komputasi yang tidak bertahan yang merujuk pada SQLCLR Scalar UDF (dicoba dengan keduanya IsDeterministic = truedan = false) didefinisikan sebagai return SqlInt32.Multiply(First, Second);─⇾ Paralelisme (woo hoo !!)

Jadi, sementara SQLCLR tidak akan bekerja untuk semua orang, SQLCLR pasti memiliki keuntungan bagi orang-orang / situasi / lingkungan yang cocok. Dan, karena berkaitan dengan pertanyaan khusus ini - contoh yang diberikan berkaitan dengan menggunakan XQuery - itu akan bekerja untuk itu (dan, tergantung pada apa yang spesifik sedang dilakukan, itu bahkan mungkin sedikit lebih cepat 😎).

Solomon Rutzky
sumber