Apparemment, ma fonction d'assemblage CLR provoque des blocages?


9

Notre application doit fonctionner aussi bien avec une base de données Oracle ou une base de données Microsoft SQL Server. Pour faciliter cela, nous avons créé une poignée d'UDF pour homogénéiser notre syntaxe de requête. Par exemple, SQL Server a GETDATE () et Oracle a SYSDATE. Ils remplissent la même fonction mais ce sont des mots différents. Nous avons écrit un UDF wrapper appelé NOW () pour les deux plates-formes qui encapsule la syntaxe spécifique à la plate-forme appropriée dans un nom de fonction commun. Nous avons d'autres fonctions de ce type, dont certaines n'ont pratiquement rien d'autre mais n'existent que dans un but d'homogénéisation. Malheureusement, cela a un coût pour SQL Server. Les FDU scalaires en ligne font des ravages sur les performances et désactivent complètement le parallélisme. Comme alternative, nous avons écrit des fonctions d'assemblage CLR pour atteindre les mêmes objectifs. Lorsque nous avons déployé cela sur un client, il a commencé à rencontrer des blocages fréquents. Ce client particulier utilise des techniques de réplication et de haute disponibilité et je me demande s'il y a une sorte d'interaction en cours ici. Je ne comprends tout simplement pas comment l'introduction d'une fonction CLR pourrait causer des problèmes comme celui-ci. Pour référence, j'ai inclus la définition UDF scalaire d'origine ainsi que la définition CLR de remplacement en C # et la déclaration SQL correspondante. J'ai également un XML de blocage que je peux fournir si cela aide.

UDF d'origine

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

Fonction d'assemblage CLR

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

Déclaration SQL Server pour la fonction 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
Les fonctions CLR scalaires déterministes ne devraient pas contribuer aux blocages. Bien sûr, les fonctions CLR qui lisent la base de données pourraient. Vous devez inclure le code XML de blocage dans votre question.
David Browne - Microsoft

Réponses:


7

Quelle (s) version (s) de SQL Server utilisez-vous?

Je me souviens avoir vu un léger changement de comportement dans SQL Server 2017 il n'y a pas si longtemps. Je devrai revenir en arrière et voir si je peux trouver où j'en ai pris note, mais je pense que cela avait à voir avec un verrouillage de schéma initié lors de l'accès à un objet SQLCLR.

Pendant que je recherche cela, je dirai ce qui suit concernant votre approche:

  1. Veuillez utiliser les Sql*types pour les paramètres d'entrée, les types de retour. Vous devriez utiliser SqlStringau lieu de string. SqlStringest très similaire à une chaîne nullable (la vôtre value?, mais elle comporte d'autres fonctionnalités spécifiques à SQL Server. Tous les Sql*types ont une Valuepropriété qui renvoie le type .NET attendu (par exemple, SqlString.Valueretours string, SqlInt32retours int, SqlDateTimeretours DateTime, etc.).
  2. Je déconseille d'abord toute cette approche, que les impasses soient liées ou non. Je dis cela parce que:

    1. Même avec des UDF SQLCLR déterministes pouvant participer à des plans parallèles, vous obtiendrez très probablement des résultats de performance pour émuler des fonctions intégrées simplistes.
    2. L'API SQLCLR ne le permet pas VARCHAR. Êtes-vous d'accord avec la conversion implicite de tout en NVARCHARpuis de nouveau en VARCHARpour des opérations simples?
    3. L'API SQLCLR ne permet pas la surcharge, vous pouvez donc avoir besoin de plusieurs versions de fonctions qui autorisent différentes signatures dans T-SQL et / ou PL / SQL.
    4. Semblable à ne pas autoriser la surcharge, il y a une grande différence entre NVARCHAR(4000)et NVARCHAR(MAX): le MAXtype (en ayant même un seul dans la signature) fait que l'appel SQLCLR prend deux fois plus longtemps que de ne pas avoir de MAXtype dans la signature (je crois que cela tient vrai pour VARBINARY(MAX)vs VARBINARY(4000)aussi). Vous devez donc choisir entre:
      • utiliser uniquement NVARCHAR(MAX)pour avoir une API simplifiée, mais prendre le coup de performance lorsque vous utilisez 8000 octets ou moins de données de chaîne, ou
      • créer deux variantes pour toutes / la plupart / plusieurs fonctions de chaîne: une avec des MAXtypes et une sans (pour quand vous êtes assuré de ne jamais dépasser 8 000 octets de données de chaîne en entrée ou en sortie). C'est l'approche que j'ai choisie pour la plupart des fonctions de ma bibliothèque SQL # : il y a une Trim()fonction qui a probablement un ou plusieurs MAXtypes, et une Trim4k()version qui n'a jamais de MAXtype nulle part dans le schéma de signature ou de jeu de résultats. Les versions "4k" sont absolument plus efficaces.
    5. Vous ne faites pas attention à émuler les fonctionnalités étant donné l'exemple de la question. LTRIMet RTRIMne découper que les espaces, tandis que .NET String.Trim()supprime les espaces blancs (au moins l'espace, les tabulations et les nouvelles lignes). Par exemple:

        PRINT LTRIM(RTRIM(N'      a       '));
    6. De plus, je viens de remarquer que votre fonction, à la fois en T-SQL et en C #, n'utilise que 1 des 3 paramètres d'entrée. S'agit-il simplement d'une preuve de concept ou d'un code expurgé?

1. Merci pour l'astuce sur l'utilisation des types Sql. Je vais faire ce changement maintenant. 2. Il y a des forces externes à l'œuvre ici qui nécessitent leur utilisation. Je n'en suis pas ravi mais croyez-moi, c'est mieux que l'alternative. Ma question d'origine contient un peu d'explication sur la raison pour laquelle une fonction apparemment asinine existe et est utilisée.
Russ Suter

@RussSuter Compris concernant les forces externes. Je ne faisais que signaler quelques écueils qui n'étaient peut-être pas connus au moment où cette décision a été prise. De toute façon, je ne peux pas trouver mes notes ou reproduire le scénario à partir des quelques détails dont je me souviens. Je me souviens juste de quelque chose qui a définitivement changé en 2017 en ce qui concerne les transactions et l'appel de code à partir d'un assembly, et qui m'a vraiment ennuyé car cela semblait être un changement inutile pour le pire, et j'ai dû contourner ce que je testais qui fonctionnait. très bien dans les versions précédentes. Alors, veuillez poster un lien dans la question vers le XML de blocage.
Solomon Rutzky

Merci pour ces informations supplémentaires. Voici un lien vers le XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
Russ Suter

@RussSuter Avez-vous essayé cela en intégrant le T-SQL? En regardant le blocage XML (ce qui n'est pas facile car il s'agit d'une seule ligne - toutes les nouvelles lignes ont été supprimées d'une manière ou d'une autre), il semble y avoir une série de verrous de PAGE entre les sessions 60 et 78. Il y a 8 pages verrouillées entre les deux sessions: 3 pour une SPID et 5 pour l'autre. Chacun avec un ID de processus différent, c'est donc un problème de parallélisme. Si cela est lié à SQLCLR, cela pourrait ironiquement être le fait que SQLCLR n'empêche pas le parallélisme. C'est pourquoi j'ai demandé si vous avez essayé de mettre la fonction simple en ligne car cela pourrait également montrer le blocage.
Solomon Rutzky
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.