[SQL Server] In-Memory OLTP : présentation des fonctions UDF scalaires nativement compilées en mémoire

Ce billet présente les fonctions scalaires nativement compilées en mémoire, une des nouvelles fonctionnalités de SQL Server 2016.

Pour comprendre ce qu’est l’In-Memory OLTP (Hekaton) vous pouvez aller ici. Et si vous souhaitez en savoir plus sur les tables optimisées en mémoire et leur mode de durabilité, vous pouvez aller . Ainsi qu’ici, si vous souhaitez comprendre la différence entre un index scan et un index seek. Ou encore, ici pour les procédures stockées nativement compilées en mémoire.

Présentation des fonctions scalaires nativement compilées en mémoire

    Notions de fonctions scalaires nativement compilées en mémoire

Les fonctions scalaires nativement compilées en mémoire sont comparables à des fonctions scalaires UDF classiques qui retournent simplement une valeur, à la différence qu’elles sont compilées – en mémoire – en code natif (code C, pour être exact). Là où une fonction scalaire classique est compilée lors de sa première exécution, avec création et réutilisation d’un plan d’exécution en cache, une fonction scalaire nativement compilée en mémoire est, elle, « parsée » et compilée à sa création, et exécutée comme un composant DDL externe par le moteur In-Memory OLTP.

De cette façon, à l’instar des procédures stockées nativement compilées en mémoire, l’utilisation d’une fonction scalaire nativement compilée en mémoire s’accompagne d’un gain de performances non-négligeable en termes de temps d’exécution.

Pour en savoir plus, vous pouvez jeter un coup d’œil ici : http://research.microsoft.com/pubs/193594/Hekaton%20-%20Sigmod2013%20final.pdf (et principalement dans la section 5.2).

Notez qu’une fonction scalaire nativement compilée en mémoire peut être appelée au sein d’une procédure stockée nativement compilée en mémoire ou même une autre fonction scalaire nativement optimisée en mémoire.

Intérêt d’une fonction scalaire nativement compilée en mémoire

Dans notre exemple de tests visible en fin de billet, la fonction scalaire nativement compilée en mémoire offre de bien meilleures performances par rapport à celle classique (qui utilise, en fait, le mode interop pour accéder à la table optimisée en mémoire).

Cela ne signifie en aucun cas, cependant, qu’il vaille remplacer toutes les fonctions scalaires classiques par des fonctions scalaires nativement compilées en mémoire. Principalement du fait de très nombreuses contraintes et limitations (voir principales limitations, plus loin).

En règle générale, l’intérêt d’une fonction scalaire nativement compilée en mémoire est son utilisation dans un contexte où une table subit fréquemment des blocages, voire deadlocks, indépendamment des considérations matérielles.

Dans tous les cas, il est de bon ton de procéder à une série de benchmarks avant de décider de l’utilisation ou non d’une fonction scalaire nativement compilée en mémoire, surtout qu’il existe quelques limitations (voir plus loin).

Quelques informations subsidiaires sont consultables ici : https://msdn.microsoft.com/en-us/library/dn935012.aspx.

Principales limitations

Comme toute fonctionnalité qui se respecte, les fonctions scalaires optimisées en mémoire possèdent, bien sûr, quelques limitations, dont voici une liste non-exhaustive :

  • Utilisation limitée aux tables optimisées en mémoire.
  • Non-support des curseurs.
  • Non-support des vues.
  • Non-support des CTEs (Common Table Expressions).
  • Non-support des instructions d’insertion multi-lignes.
  • Non-support des niveaux d’isolation READ UNCOMMITTED et READ COMMITTED.
  • Non-support des tables temporaires (stockées dans tempDB), contrairement aux tables variables
  • Non-support d’un certain nombre d’options : RECOMPILE, COMPUTE,…
  • Non-support d’un certain nombre d’opérateurs : PIVOT, UNPIVOT, EXCEPT, INTERSECT, EXISTS, CONTAINS,
  • Non-support de RAISERROR (contrairement à TRY… CATCH et THROW), pour la gestion d’erreurs.

Pour finir, la DMV sys.sql_modules permet d’identifier les fonctions scalaires qui ont accès aux tables optimisées en mémoire (sachant que la colonne uses_native_compilation indique s’il d’une fonction scalaire nativement compilée en mémoire (valeur 1) ou non (0)).

Utilisation technique d’une fonction scalaire nativement compilée en mémoire

La syntaxe de création d’une fonction scalaire nativement compilée en mémoire est la suivante :

CREATE FUNCTION [ schema. ] udf_name
( [ { @parameter_name [ AS ][ type_schema. ] param_data_type
    [ NULL | NOT NULL ] [ = default ] [ READONLY ] }
    [ ,...n ]
  ]
)
RETURNS data_type
   WITH <options> [ ,...n ]
   [ AS ]
   BEGIN ATOMIC WITH (set_option [ ,... n ])
      ...
      RETURN scalar_expression
   END
<options>::=
{
  | NATIVE_COMPILATION
  | SCHEMABINDING
  | EXECUTE_AS_Clause
  | [ RETURNS NULL ON NULL INPUT | CALLED ON NULL INPUT ]
}

Comme on peut le souligner, la création d’une fonction scalaire nativement compilée en mémoire est très simple. Et ce que l’on doit retenir sont les éléments suivants qui constituent les principales options de création :

  • NATIVE_COMPILATION : sert de condition sine qua non pour la création d’une fonction scalaire nativement compilée en mémoire.
  • SCHEMABINDING : permet de référencer les tables optimisées en mémoire utilisées par la fonction scalaireen mémoire. De cette façon, lesdites tables ne pourront pas être supprimées tant que leur lien avec la fonction scalaire ne sera pas rompu.
  • EXECUTE AS … : spécifie le contexte d’exécution de la fonction scalaire nativement compilée en mémoire, le contexte d’exécution par défaut (EXECUTE AS CALLER)
    n’étant pas supporté, pour des raisons de performances (un tel contexte d’exécution nécessitant, au préalable, une série de vérifications de la sécurité au cours de l’exécution). Les contextes d’exécution acceptés sont : EXECUTE AS OWNER (propriétaire de la fonction scalaire nativement compilée en mémoire), EXECUTE AS <utilisateur> (utilisateur ayant des droits d’exécution) et EXECUTE AS SELF (soi-même, si l’on possède les droits idoines).
  • RETURNS : permet de retourner la valeur de la fonction scalaire.
  • BEGIN ATOMIC … END : constitue le corps de la fonction scalaire nativement compilée en mémoire, sous la forme d’un seul bloc de code atomique. L’intérêt de cette option est de garantir l’atomicité (voir principes ACID) de la fonction scalairedurant son exécution. Si une transaction est appelée en dehors du contexte d’une transaction active, une nouvelle transaction sera alors démarrée. Transaction qui ne sera validée qu’à la fin du bloc atomique.
  • RETURNS NULL ON NULL INPUT | CALLED ON NULL INPUT: sert à specifier l’attribut OnNULLCall. En cas de non-spécification, CALLED ON NULL INPUT sera utilisé par défaut, ce qui signifie que la function va s’exécuter, même si une valeur NULL est passée en paramètre.

Pour aller plus loin…

Ce billet a permis d’avoir un aperçu de ce qu’est une fonction scalaire nativement compilée en mémoire.

Gardez un œil ici pour d’autres billets sur le même sujet, notamment du point de vue des performances et de la gestion des collisions.

Vous pouvez également jeter un coup d’œil ici, pour plus de lectures : http://msdn.microsoft.com/en-us/library/dn511014.aspx.

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s