Мабуть, моя функція складання CLR викликає тупики?


9

Нашому додатку потрібно однаково добре працювати з базою даних Oracle або базою даних Microsoft SQL Server. Щоб полегшити це, ми створили кілька UDF для гомогенізації нашого синтаксису запитів. Наприклад, SQL Server має GETDATE (), а Oracle - SYSDATE. Вони виконують ту саму функцію, але це різні слова. Ми написали обгортку UDF під назвою NOW () для обох платформ, яка поєднує відповідний синтаксис певної платформи у загальну назву функції. У нас є й інші такі функції, деякі з яких по суті нічого не роблять, але існують виключно заради гомогенізації. На жаль, це має вартість для SQL Server. Вбудовані скалярні АДС викликають погіршення продуктивності та повністю відключають паралелізм. В якості альтернативи ми написали функції складання CLR для досягнення тих же цілей. Коли ми розгорнули це до клієнта, вони почали часто зустрічатися з тупиками. Цей конкретний клієнт використовує методи реплікації та високої доступності, і мені цікаво, чи відбувається тут якась взаємодія. Я просто не розумію, як введення функції CLR може викликати подібні проблеми. Для довідки я включив оригінальне скалярне визначення UDF, а також визначення заміну CLR в C # і SQL-декларацію для нього. У мене також є XML з тупиком, який я можу надати, якщо це допоможе.

Оригінал UDF

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

Функція складання CLR

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

Декларація SQL Server для функції CLR

CREATE FUNCTION [fn].[APAD]
(
    @Value NVARCHAR(4000),
    @TableName NVARCHAR(4000),
    @ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO

9
Детерміновані скалярні функції CLR не повинні сприяти тупикам. Звичайно, функції CLR, які читають базу даних, можуть. Ви повинні включити у своє запитання XML з тупиком.
Девід Браун - Майкрософт

Відповіді:


7

Яку версію (-ів) SQL Server ви використовуєте?

Я пам’ятаю, що не так давно бачив незначну зміну поведінки в SQL Server 2017. Мені доведеться повернутися назад і побачити, чи зможу я знайти, де я зробив помітку про це, але, думаю, це було пов'язано з блокуванням схеми, ініційованим під час доступу до об'єкта SQLCLR.

Поки я шукаю це, я скажу наступне щодо вашого підходу:

  1. Будь ласка, використовуйте Sql*типи для вхідних параметрів, типів повернення. Ви повинні використовувати SqlStringзамість цього string. SqlStringдуже схожий на нульову рядок (ваш value?, але в ньому вбудований інший функціонал, який є специфічним для SQL Server. Усі Sql*типи мають Valueвластивість, яка повертає очікуваний тип .NET (наприклад, SqlString.Valueповернення string, SqlInt32повернення int, SqlDateTimeповернення DateTimeтощо).
  2. Я б рекомендував проти цього всього підходу почати з того, пов'язані чи не тупики. Я говорю це тому, що:

    1. Навіть маючи детерміновані SQLCLR UDF, здатні брати участь у паралельних планах, ви, швидше за все, отримаєте хіти для продуктивності для імітації спрощених вбудованих функцій.
    2. API SQLCLR не дозволяє VARCHAR. Ви все гаразд з неявним перетворенням у все, NVARCHARа потім знову VARCHARдля простого виконання операцій?
    3. API SQLCLR не дозволяє перевантажувати, тому вам можуть знадобитися кілька версій функцій, які дозволяють використовувати різні підписи в T-SQL та / або PL / SQL.
    4. Як і не допускати перевантаження, існує велика різниця між NVARCHAR(4000)і NVARCHAR(MAX): MAXтип (маючи навіть один один із них у підписі) робить виклик SQLCLR займає вдвічі більше часу, ніж MAXу підписі немає жодного типу (я вважаю, що це має місце також для VARBINARY(MAX)vs VARBINARY(4000)). Отже, вам потрібно вибрати:
      • використовуючи лише NVARCHAR(MAX)спрощений API, але прийміть ефективність, коли ви використовуєте 8000 байт або менше рядкових даних, або
      • створення двох варіантів для всіх / більшості / багатьох рядкових функцій: одна з MAXтипами і одна без (бо коли вам гарантовано ніколи не переходити або виходити за 8000 байт рядкових даних). Цей підхід я обрав для більшості функцій моєї бібліотеки SQL # : є Trim()функція, яка, ймовірно, має один або більше MAXтипів, і Trim4k()версія, яка ніколи не має MAXтипу ніде в схемі набору підписів чи результатів. Версії "4k" абсолютно ефективніші.
    5. Ви не стежите за тим, щоб імітувати функціональність, наведений у прикладі запитання. LTRIMі RTRIMлише обрізати пробіли, тоді як .NET String.Trim()обробляє пробіл (принаймні пробіл, вкладки та нові рядки). Наприклад:

        PRINT LTRIM(RTRIM(N'      a       '));
    6. Крім того, я щойно помітив, що ваша функція, як в T-SQL, так і в C #, використовує лише 1 з 3 вхідних параметрів. Це лише доказ поняття чи відредагований код?

1. Дякую за пораду щодо використання типів Sql. Я зараз це зміню. 2. Тут працюють зовнішні сили, які потребують їх використання. Я не в захваті від цього, але повірте, це краще, ніж альтернатива. Моє первісне запитання містить трохи пояснень того, чому, здавалося б, функція асиніну існує і використовується.
Russ Suter

@RussSuter Зрозуміло: зовнішні сили. Я тільки вказував на деякі підводні камені, які, можливо, не були відомі, коли було прийнято це рішення. Так чи інакше, я не можу знайти своїх заміток або відтворити сценарій з кількох деталей, які я пам’ятаю про нього. Я просто пам’ятаю щось, що виразно змінювалось у 2017 році щодо транзакцій та виклику коду з збірки , і мене це справді дратувало, оскільки це здавалося зайвою зміною на гірше, і мені довелося обійтись над тим, що я тестував, що працював штраф у попередніх версіях. Тож, будь ласка, опублікуйте посилання у питанні на тупик XML.
Соломон Руцький

Дякуємо за додаткову інформацію. Ось посилання на XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
Russ Suter

@RussSuter Ви пробували це з вбудованим T-SQL? Дивлячись на тупик XML (що непросто, оскільки це єдиний рядок - всі нові рядки якось видалено), схоже, це серія блокувань PAGE між сесіями 60 та 78. Між обома сеансами заблоковано 8 сторінок: 3 для одного SPID та 5 для іншого. Кожен має інший ідентифікатор процесу, тому це питання паралелізму. Якщо це пов'язано з SQLCLR, це може бути іронічним фактом, що SQLCLR не перешкоджає паралелізму. Ось чому я запитав, чи намагалися ви поставити просту функцію в рядок, оскільки це також може показати тупик.
Соломон Руцький
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.