Rupanya, fungsi perakitan CLR saya menyebabkan kebuntuan?

9

Aplikasi kita perlu bekerja sama baiknya dengan database Oracle atau database Microsoft SQL Server. Untuk memfasilitasi ini, kami membuat beberapa UDF untuk menyeragamkan sintaksis kueri kami. Sebagai contoh, SQL Server memiliki GETDATE () dan Oracle memiliki SYSDATE. Mereka melakukan fungsi yang sama tetapi kata-kata mereka berbeda. Kami menulis pembungkus UDF yang disebut SEKARANG () untuk kedua platform yang membungkus sintaksis platform spesifik yang relevan dengan nama fungsi umum. Kami memiliki fungsi lain seperti itu, beberapa di antaranya pada dasarnya tidak ada tetapi hanya ada demi homogenisasi. Sayangnya, ini memiliki biaya untuk SQL Server. UDF skalar sebaris mendatangkan malapetaka pada kinerja dan sepenuhnya menonaktifkan paralelisme. Sebagai alternatif, kami menulis fungsi perakitan CLR untuk mencapai tujuan yang sama. Ketika kami menyebarkan ini ke klien, mereka mulai sering mengalami kebuntuan. Klien khusus ini menggunakan replikasi dan teknik ketersediaan tinggi dan saya bertanya-tanya apakah ada semacam interaksi yang terjadi di sini. Saya hanya tidak mengerti bagaimana memperkenalkan fungsi CLR akan menyebabkan masalah seperti ini. Sebagai referensi, saya telah memasukkan definisi skalar UDF asli serta definisi CLR pengganti di C # dan deklarasi SQL untuk itu. Saya juga memiliki kebuntuan XML yang bisa saya berikan jika itu membantu.

UDF asli

CREATE FUNCTION [fn].[APAD]
(
    @Value VARCHAR(4000)
    , @tablename VARCHAR(4000) = NULL
    , @columnname VARCHAR(4000) = NULL
)

RETURNS VARCHAR(4000)
WITH SCHEMABINDING
AS

BEGIN
    RETURN LTRIM(RTRIM(@Value))
END
GO

Fungsi Perakitan CLR

[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
    return value?.Trim();
}

Deklarasi SQL Server untuk Fungsi CLR

CREATE FUNCTION [fn].[APAD]
(
    @Value NVARCHAR(4000),
    @TableName NVARCHAR(4000),
    @ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO
Russ Suter
sumber
9
Fungsi CLR skalar deterministik seharusnya tidak berkontribusi pada kebuntuan. Tentu saja fungsi CLR yang membaca database mungkin. Anda harus memasukkan XML deadlock dalam pertanyaan Anda.
David Browne - Microsoft

Jawaban:

7

Versi SQL Server apa yang Anda gunakan?

Saya ingat melihat sedikit perubahan perilaku di SQL Server 2017 belum lama ini. Saya harus kembali dan melihat apakah saya dapat menemukan di mana saya membuat catatan itu, tapi saya pikir itu ada hubungannya dengan kunci skema yang dimulai ketika objek SQLCLR sedang diakses.

Sementara saya mencari itu, saya akan mengatakan hal berikut mengenai pendekatan Anda:

  1. Silakan gunakan Sql*tipe untuk parameter input, tipe kembali. Anda harus menggunakan SqlStringbukan string. SqlStringsangat mirip dengan string nullable (Anda value?, tetapi memiliki fungsi lain yang dibangun di dalamnya adalah SQL Server-spesifik. Semua Sql*jenis memiliki Valueproperti yang mengembalikan tipe .NET yang diharapkan (misalnya SqlString.Valuepengembalian string, SqlInt32pengembalian int, SqlDateTimepengembalian DateTime, dll).
  2. Saya akan merekomendasikan terhadap seluruh pendekatan ini untuk memulai, apakah kebuntuan terkait atau tidak. Saya mengatakan ini karena:

    1. Bahkan dengan SQLCLR UDF yang deterministik dapat berpartisipasi dalam rencana paralel, Anda kemungkinan besar akan mendapatkan hit kinerja untuk meniru fungsi bawaan yang sederhana.
    2. API SQLCLR tidak memungkinkan untuk VARCHAR. Apakah Anda setuju dengan secara implisit mengubah segalanya menjadi NVARCHARdan kemudian kembali ke VARCHARuntuk operasi sederhana?
    3. SQLCLR API tidak memungkinkan untuk kelebihan, jadi Anda mungkin perlu beberapa versi fungsi yang memungkinkan untuk tanda tangan yang berbeda di T-SQL dan / atau PL / SQL.
    4. Mirip dengan tidak mengizinkan overloading, ada perbedaan besar antara NVARCHAR(4000)dan NVARCHAR(MAX): MAXtipe (bahkan memiliki satu pun dari mereka dalam tanda tangan) membuat panggilan SQLCLR memakan waktu dua kali selama tidak memiliki MAXjenis tanda tangan (saya percaya ini berlaku berlaku untuk VARBINARY(MAX)vs VARBINARY(4000)juga). Jadi, Anda perlu memutuskan antara:
      • menggunakan hanya NVARCHAR(MAX)untuk memiliki API yang disederhanakan, tetapi ambil hit kinerja ketika Anda menggunakan 8000 byte atau kurang dari data string, atau
      • membuat dua variasi untuk semua / kebanyakan / banyak fungsi string: satu dengan MAXtipe, dan satu tanpa (untuk saat Anda dijamin tidak akan pernah melewati 8000 byte data string masuk atau keluar). Ini adalah pendekatan yang saya pilih untuk mengambil sebagian besar fungsi dalam pustaka SQL # saya : ada Trim()fungsi yang kemungkinan memiliki satu atau lebih MAXtipe, dan Trim4k()versi yang tidak pernah memiliki MAXtipe di mana pun dalam skema set tanda tangan atau hasil. Versi "4k" benar-benar lebih efisien.
    5. Anda tidak berhati-hati untuk meniru fungsionalitas yang diberikan contoh dalam pertanyaan. LTRIMdan RTRIMhanya memangkas ruang, sementara .NET String.Trim()memangkas ruang putih (setidaknya ruang, tab, dan baris baru). Sebagai contoh:

        PRINT LTRIM(RTRIM(N'      a       '));
    6. Juga, saya hanya memperhatikan bahwa fungsi Anda, baik dalam T-SQL dan di C #, hanya menggunakan 1 dari 3 parameter input. Apakah ini hanya bukti konsep, atau kode redacted?
Solomon Rutzky
sumber
1. Terima kasih atas tipnya menggunakan jenis Sql. Saya akan melakukan perubahan itu sekarang. 2. Ada kekuatan eksternal yang bekerja di sini yang mengharuskan penggunaannya. Saya tidak senang tentang itu tetapi percayalah, itu lebih baik daripada alternatifnya. Pertanyaan asli saya berisi sedikit penjelasan tentang mengapa fungsi yang tampaknya bodoh ada dan sedang digunakan.
Russ Suter
@RussSuter Dipahami: kekuatan eksternal. Saya baru saja menunjukkan beberapa jebakan yang mungkin belum diketahui saat keputusan itu dibuat. Either way, saya tidak dapat menemukan catatan saya atau mereproduksi skenario dari beberapa detail yang saya ingat. Saya hanya ingat sesuatu yang pasti berubah pada tahun 2017 sehubungan dengan transaksi dan kode panggilan dari sebuah majelis, dan menjadi benar - benar kesal karena sepertinya perubahan yang tidak perlu menjadi lebih buruk, dan saya harus mengatasinya untuk apa yang saya uji yang berfungsi. baik di versi sebelumnya. Jadi, silakan posting tautan dalam pertanyaan ke XML deadlock.
Solomon Rutzky
Terima kasih atas info tambahannya. Berikut ini tautan ke XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
Russ Suter
@RussSuter Sudahkah Anda mencoba ini dengan inlining T-SQL? Melihat kebuntuan XML (yang tidak mudah karena hanya satu baris - semua baris baru bisa dihapus entah bagaimana) tampaknya merupakan serangkaian kunci PAGE antara sesi 60 dan 78. Ada 8 halaman terkunci di antara kedua sesi: 3 untuk satu SPID dan 5 untuk yang lainnya. Masing-masing dengan ID proses yang berbeda, jadi ini adalah masalah paralelisme. Jika ini terkait dengan SQLCLR, mungkin ironisnya fakta bahwa SQLCLR tidak mencegah paralelisme. Inilah sebabnya saya bertanya apakah Anda telah mencoba menempatkan fungsi sederhana sebaris karena itu mungkin juga menunjukkan kebuntuan.
Solomon Rutzky