Insertion TempDB spécifique de la sélection UserDB dans SOS_SCHEDULER_YIELD à ENCRYPTION_SCAN

8

L'un de nos systèmes de production a présenté un problème avec une seule instruction d'insertion dans une table temporaire de notre base de données d'utilisateurs. Lorsque je commente l'insertion / sélection, le processus stocké en question s'exécute en temps opportun, je suis donc confiant dans mon isolement du problème.

La série de procs stockés invoqués s'arrête fondamentalement lorsque je commente l'insertion / sélection en question. Je ne vois rien dans Top Transactions by Age dans tempdb ou dans nos bases de données utilisateur. Je ne vois rien dans le Moniteur d'activité qui s'écarte des informations du Moniteur d'activité lorsque la base de données est "au repos", autre que le CPU étant plat à ~ 20%.

Le comportement est le suivant: lorsque j'installe puis exécute le cas de reproduction, à l'arrivée à l'insert / sélection en question, je vois un SOS_SCHEDULER_YIELD et il y a un ENCRYPTION_SCAN. Environ cinq heures plus tard, je verrai le traitement de notre procédure stockée reprendre et l'activité se terminera (je mets des instructions de journal rapides et sales autour de chaque opération distincte).

J'ai également remplacé les variables dans la partie sélection de l'insert par les valeurs telles qu'exécutées, et j'ai exécuté la requête de sélection elle-même, et elle est revenue en cinq secondes.

La base de données utilisateur en question a FALSE comme valeur de cryptage activée, tout comme tempdb. L'opération en question se produit sur environ 65k lignes de données, et je l'ai essayé avec seulement 1k lignes, et le comportement a persisté, bien que le temps qu'il a fallu était beaucoup moins.

Une base de données mono-utilisateur est la seule instance de ce comportement. Je l'ai reproduit localement sur une sauvegarde de cette base de données utilisateur. Nous avons environ 70 autres utilisateurs du logiciel qui ne présentent pas ce problème.

Compte tenu des informations ci-dessus, ma question est la suivante: pourquoi le traitement de nos procédures stockées s'arrête-t-il? Comme il est probablement optimiste d'attendre une réponse précise, quelle est la bonne étape pour déboguer cela? Peut-être qu'il y a quelque chose dans l'un des DMV comme dm_tran_locks, dm_exec_requests, dm_tran_database_transactions, dm_os_schedulers, dm_exec_sessions qui, alors qu'ils m'ont fourni des informations, je n'interprète pas ou ne comprends pas la sortie d'une manière qui pointe vers une solution.

Ci-dessous est l'insertion / sélection en question:

INSERT INTO #TS_EVENT_DATA 
        (   EVENT_FK,
            EVENT_TYPE_CR_FK,
            EVENT_ENTITY_CLASS_CR_FK,
            userDatabase_ID,
            DATA_NAME_FK,               
            IMPORT_JOB_FK,              
            PRODUCT_STRUCTURE_FK,
            ORG_ENTITY_STRUCTURE_FK,
            ENTITY_CLASS_CR_FK,
            ENTITY_DATA_NAME_FK,
            ENTITY_STRUCTURE_FK,                
            DATA_SET_FK,
            DATA_TYPE_CR_FK,
            ORG_IND,
            TABLE_NAME,
            NET_VALUE1_NEW,
            NET_VALUE2_NEW,
            NET_VALUE3_NEW,
            NET_VALUE4_NEW,
            NET_VALUE5_NEW,
            NET_VALUE6_NEW,                                                     
            NET_VALUE1_CUR,
            NET_VALUE2_CUR,
            NET_VALUE3_CUR,
            NET_VALUE4_CUR,
            NET_VALUE5_CUR,
            NET_VALUE6_CUR,                         
            PERCENT_CHANGE1,
            PERCENT_CHANGE2,
            PERCENT_CHANGE3,
            PERCENT_CHANGE4,
            PERCENT_CHANGE5,
            PERCENT_CHANGE6,                            
            VALUE_UOM_CODE_FK,
            ASSOC_UOM_CODE_FK,
            VALUES_SHEET_NAME,
            UOM_CONVERSION_FACTOR,
            END_DATE_CUR,                           
            END_DATE_NEW,   
            EFFECTIVE_DATE_CUR,                                     
            EVENT_EFFECTIVE_DATE,
            EVENT_ACTION_CR_FK,
            EVENT_STATUS_CR_FK, 
            EVENT_CONDITION_CR_FK,
            EVENT_SOURCE_CR_FK,
            EVENT_PRIORITY_CR_FK,
            RESULT_TYPE_CR_FK,
            TABLE_ID_FK,
            BATCH_NO,
            IMPORT_BATCH_NO,
            RULES_FK,
            RECORD_STATUS_CR_FK,                
            UPDATE_TIMESTAMP
             )  
   SELECT
            A.EVENT_ID,                    
            A.EVENT_TYPE_CR_FK,
            A.EVENT_ENTITY_CLASS_CR_FK,
            A.userDatabase_ID,
            A.DATA_NAME_FK,             
            A.IMPORT_JOB_FK,
            A.PRODUCT_STRUCTURE_FK,
            A.ORG_ENTITY_STRUCTURE_FK,
            A.ENTITY_CLASS_CR_FK,
            A.ENTITY_DATA_NAME_FK,
            A.ENTITY_STRUCTURE_FK,
            A.DATA_SET_FK,
            A.DATA_TYPE_CR_FK,
            A.ORG_IND,
            A.TABLE_NAME,
            A.NET_VALUE1_NEW,
            A.NET_VALUE2_NEW,
            A.NET_VALUE3_NEW,
            A.NET_VALUE4_NEW,
            A.NET_VALUE5_NEW,
            A.NET_VALUE6_NEW,                                                   
            A.NET_VALUE1,  
            A.NET_VALUE2,
            A.NET_VALUE3,
            A.NET_VALUE4,
            A.NET_VALUE5,
            A.NET_VALUE6,                       
            CASE ISNULL (A.NET_VALUE1, 0 )
             WHEN 0 THEN 0  
             ELSE ( ( A.NET_VALUE1_NEW  - A.NET_VALUE1) / A.NET_VALUE1 )
             END,
            CASE ISNULL (A.NET_VALUE2, 0 )
             WHEN 0 THEN 0  
             ELSE ( ( A.NET_VALUE2_NEW  - A.NET_VALUE2 ) / A.NET_VALUE2 )
             END,
            CASE ISNULL (A.NET_VALUE3, 0 )
             WHEN 0 THEN 0  
             ELSE ( ( A.NET_VALUE3_NEW  - A.NET_VALUE3 ) / A.NET_VALUE3 )
             END,
            CASE ISNULL (A.NET_VALUE4, 0 )
             WHEN 0 THEN 0  
             ELSE ( ( A.NET_VALUE4_NEW  - A.NET_VALUE4 ) / A.NET_VALUE4 )
             END,
            CASE ISNULL (A.NET_VALUE5, 0 )
             WHEN 0 THEN 0  
             ELSE ( ( A.NET_VALUE5_NEW  - A.NET_VALUE5 ) / A.NET_VALUE5 )
             END,
            CASE ISNULL (A.NET_VALUE6, 0 )
             WHEN 0 THEN 0  
             ELSE ( ( A.NET_VALUE6_NEW  - A.NET_VALUE6 ) / A.NET_VALUE6 )
             END,
        A.VALUE_UOM_CODE_FK,
        A.ASSOC_UOM_CODE_FK,
        A.VALUES_SHEET_NAME,
        ( SELECT CASE ISNULL ( A.VALUE_UOM_CODE_FK, 0 ) 
        WHEN 0 THEN 1
        ELSE
        CASE ISNULL ( A.ASSOC_UOM_CODE_FK, 0 ) 
        WHEN 0 THEN 1
        ELSE
        ( ISNULL (
        ( SELECT 
        ( SELECT ISNULL (
           (SELECT uc.primary_qty
            FROM userDatabase.UOM_CODE uc WITH (NOLOCK)
            WHERE uc.UOM_CODE_ID = A.VALUE_UOM_CODE_FK  ), 1 )
        )
        /
        ISNULL (   (SELECT uc.primary_qty
                    FROM userDatabase.UOM_CODE uc WITH (NOLOCK)
                    WHERE uc.UOM_CODE_ID = A.ASSOC_UOM_CODE_FK )
                 , ISNULL ( (SELECT uc.primary_qty
                             FROM userDatabase.UOM_CODE uc WITH (NOLOCK)
                             WHERE uc.UOM_CODE_ID = A.VALUE_UOM_CODE_FK) , 1 ) 
                 )
        ) , 1 ) )
        END END
        )  AS UOM_CONVERSION_FACTOR,
            A.END_DATE,
            A.END_DATE_NEW, 
            A.EFFECTIVE_DATE,                                       
            A.EVENT_EFFECTIVE_DATE,
            A.EVENT_ACTION_CR_FK,
            A.EVENT_STATUS_CR_FK,   
            A.EVENT_CONDITION_CR_FK,
            A.EVENT_SOURCE_CR_FK,
            A.EVENT_PRIORITY_CR_FK,
            A.RESULT_TYPE_CR_FK,
            A.SHEET_RESULTS_ID,
            A.BATCH_NO,
            A.IMPORT_BATCH_NO,
            A.RULES_FK,
            A.RECORD_STATUS_CR_FK,              
            @L_SYSDATE

FROM ( SELECT
            ED.EVENT_ID,                    
            DS.EVENT_TYPE_CR_FK,
            DS.ENTITY_CLASS_CR_FK AS EVENT_ENTITY_CLASS_CR_FK,
            ED.PRODUCT_STRUCTURE_FK AS userDatabase_ID,
            ED.DATA_NAME_FK,
            ED.IMPORT_JOB_FK,           
            ED.PRODUCT_STRUCTURE_FK,
            CASE ISNULL ( DS.ORG_IND, 0 )
            WHEN 0 THEN ISNULL ( ED.ORG_ENTITY_STRUCTURE_FK, 1 )
            ELSE ED.ORG_ENTITY_STRUCTURE_FK         
            END AS ORG_ENTITY_STRUCTURE_FK,
            DS.ENTITY_STRUCTURE_EC_CR_FK AS ENTITY_CLASS_CR_FK,
            DN.ENTITY_DATA_NAME_FK,
            ED.ENTITY_STRUCTURE_FK,                                                                                 
            DN.DATA_SET_FK,
            DS.DATA_TYPE_CR_FK,
            DS.ORG_IND,
            DS.TABLE_NAME,
            ED.NET_VALUE1_NEW,
            ED.NET_VALUE2_NEW,
            ED.NET_VALUE3_NEW,
            ED.NET_VALUE4_NEW,
            ED.NET_VALUE5_NEW,
            ED.NET_VALUE6_NEW,                                                      
            SR.NET_VALUE1,
            SR.NET_VALUE2,
            SR.NET_VALUE3,
            SR.NET_VALUE4,
            SR.NET_VALUE5,
            SR.NET_VALUE6,  
        ED.VALUE_UOM_CODE_FK,               
        ( SELECT TOP 1 PUC.UOM_CODE_FK
          FROM userDatabase.PRODUCT_UOM_CLASS PUC WITH (NOLOCK)
          WHERE ( PUC.DATA_NAME_FK = DN.UOM_CLASS_DATA_NAME_FK
          AND     PUC.PRODUCT_STRUCTURE_FK = ED.PRODUCT_STRUCTURE_FK
          AND   (  (   DS.ORG_IND = 1
                   AND PUC.ORG_ENTITY_STRUCTURE_FK = ED.ORG_ENTITY_STRUCTURE_FK )
                  OR PUC.ORG_ENTITY_STRUCTURE_FK = 1 ) 
          AND ISNULL ( PUC.ENTITY_STRUCTURE_FK, -999 ) = ISNULL ( ED.ENTITY_STRUCTURE_FK, -999 )   )
        ) AS ASSOC_UOM_CODE_FK,
        ED.VALUES_SHEET_NAME,
            SR.END_DATE,
            ED.END_DATE_NEW,
            SR.EFFECTIVE_DATE,              
            ED.EVENT_EFFECTIVE_DATE, 
            CASE WHEN ED.EVENT_ACTION_CR_FK = 59
            THEN 59
            ELSE
            CASE WHEN SR.SHEET_RESULTS_ID IS NULL
            THEN 51
            ELSE 52
            END
            END  AS EVENT_ACTION_CR_FK,
            ED.EVENT_STATUS_CR_FK,  
            ED.EVENT_CONDITION_CR_FK,
            ED.EVENT_SOURCE_CR_FK,
            ED.EVENT_PRIORITY_CR_FK,
            ISNULL ( ED.RESULT_TYPE_CR_FK, 711 ) AS RESULT_TYPE_CR_FK,                  
            SR.SHEET_RESULTS_ID,
            ED.BATCH_NO,
            ED.IMPORT_BATCH_NO,
            ED.RULES_FK,
            ED.RECORD_STATUS_CR_FK          
 FROM SYNCHRONIZER.EVENT_DATA ED WITH (NOLOCK)
    INNER JOIN userDatabase.DATA_NAME DN WITH (NOLOCK)
     ON ( DN.DATA_NAME_ID = ED.DATA_NAME_FK )
    INNER JOIN userDatabase.DATA_SET DS WITH (NOLOCK)
     ON ( DS.DATA_SET_ID = DN.DATA_SET_FK )
    LEFT JOIN marginmgr.SHEET_RESULTS SR WITH (NOLOCK)
     ON ( SR.DATA_NAME_FK = ED.DATA_NAME_FK
     AND  ISNULL ( SR.PRODUCT_STRUCTURE_FK, 0 ) = ISNULL ( ED.PRODUCT_STRUCTURE_FK, 0 )
     AND  ISNULL ( SR.ORG_ENTITY_STRUCTURE_FK, 0 ) = ISNULL ( ED.ORG_ENTITY_STRUCTURE_FK, 1 )
     AND  ISNULL ( SR.ENTITY_STRUCTURE_FK, 0 ) = ISNULL ( ED.ENTITY_STRUCTURE_FK, 0 )  
    )                                                    
    WHERE 1 = 1 
    AND  EVENT_STATUS_CR_FK = 88        
    AND (   
           (    ISNULL ( @in_event_fk, -999 ) = -999
            AND ISNULL ( ED.BATCH_NO, -999 ) = ISNULL ( @in_batch_no, -999 )
            AND ISNULL ( ED.import_job_fk, -999 ) = ISNULL (@in_import_job_fk, -999 )
            AND isnull ( ED.event_priority_cr_fk, -999 ) = isnull (@in_event_priority_cr_fk, -999)
            AND ISNULL ( ds.table_name, 'NULL DATA' )  = ISNULL ( @in_table_name, 'NULL DATA' ) )
        OR  ED.EVENT_ID = ISNULL (@in_event_fk, -999 ) 
        )
    AND   (   @in_data_name_fk = -999           
          OR  ED.data_name_fk = @in_data_name_fk )          
) A 

Résultats de sp_configure:

access check cache bucket count 0   65536   0   0
access check cache quota    0   2147483647  0   0
Ad Hoc Distributed Queries  0   1   0   0
affinity I/O mask   -2147483648 2147483647  0   0
affinity mask   -2147483648 2147483647  0   0
affinity64 I/O mask -2147483648 2147483647  0   0
affinity64 mask -2147483648 2147483647  0   0
Agent XPs   0   1   1   1
allow updates   0   1   0   0
backup compression default  0   1   0   0
blocked process threshold (s)   0   86400   0   0
c2 audit mode   0   1   0   0
clr enabled 0   1   1   1
common criteria compliance enabled  0   1   0   0
contained database authentication   0   1   0   0
cost threshold for parallelism  0   32767   5   5
cross db ownership chaining 0   1   0   0
cursor threshold    -1  2147483647  -1  -1
Database Mail XPs   0   1   1   1
default full-text language  0   2147483647  1033    1033
default language    0   9999    0   0
default trace enabled   0   1   1   1
disallow results from triggers  0   1   0   0
EKM provider enabled    0   1   0   0
filestream access level 0   2   0   0
fill factor (%) 0   100 0   0
ft crawl bandwidth (max)    0   32767   100 100
ft crawl bandwidth (min)    0   32767   0   0
ft notify bandwidth (max)   0   32767   100 100
ft notify bandwidth (min)   0   32767   0   0
index create memory (KB)    704 2147483647  0   0
in-doubt xact resolution    0   2   0   0
lightweight pooling 0   1   0   0
locks   5000    2147483647  0   0
max degree of parallelism   0   32767   0   0
max full-text crawl range   0   256 4   4
max server memory (MB)  128 2147483647  5120    5120
max text repl size (B)  -1  2147483647  65536   65536
max worker threads  128 65535   0   0
media retention 0   365 0   0
min memory per query (KB)   512 2147483647  1024    1024
min server memory (MB)  0   2147483647  128 128
nested triggers 0   1   1   1
network packet size (B) 512 32767   4096    4096
Ole Automation Procedures   0   1   0   0
open objects    0   2147483647  0   0
optimize for ad hoc workloads   0   1   0   0
PH timeout (s)  1   3600    60  60
precompute rank 0   1   0   0
priority boost  0   1   0   0
query governor cost limit   0   2147483647  0   0
query wait (s)  -1  2147483647  -1  -1
recovery interval (min) 0   32767   0   0
remote access   0   1   1   1
remote admin connections    0   1   0   0
remote login timeout (s)    0   2147483647  10  10
remote proc trans   0   1   0   0
remote query timeout (s)    0   2147483647  600 600
Replication XPs 0   1   0   0
scan for startup procs  0   1   0   0
server trigger recursion    0   1   1   1
set working set size    0   1   0   0
show advanced options   0   1   1   1
SMO and DMO XPs 0   1   1   1
transform noise words   0   1   0   0
two digit year cutoff   1753    9999    2049    2049
user connections    0   32767   0   0
user options    0   32767   0   0
xp_cmdshell 0   1   1   1

Lien vers XML d'Exec Plan (trop volumineux pour être intégré).

Robert Gannon
la source
Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
Paul White 9

Réponses:

5

Vous ne verrez pas seulement la ressource ENCRYPTION_SCAN dans votre liste d'attente lorsque le chiffrement (comme TDE) est utilisé.

Certaines opérations prendront un verrou partagé sur cette ressource pour s'assurer que la base de données n'est pas chiffrée pendant l'opération.

Au moment où vous crypteriez une base de données utilisateur avec TDE, le tempdb sera également crypté (sinon, vous auriez un risque de sécurité lorsque les données utilisateur sont utilisées dans temp db).

Par conséquent, certaines opérations prendront un verrou partagé sur ENCRYPTION_SCAN dans Tempdb pour empêcher Tempdb d'être crypté.

Voici deux exemples:

INSERT EN VRAC

IF object_id('tempdb..##NumberCreation') IS NOT NULL
    drop table ##NumberCreation
GO

--create temp table to hold numbers
create table ##NumberCreation (C int NOT NULL);
GO

-- CREATE Numbers by using trick from Itzik -> http://sqlmag.com/sql-server/virtual-auxiliary-table-numbers 
WITH L1 AS ( SELECT 1 as C UNION SELECT 0 ),
    L2 AS ( SELECT 1 as C FROM L1 CROSS JOIN L1 as B ),
    L3 AS ( SELECT 1 as C FROM L2 CROSS JOIN L2 as B ),
    L4 AS ( SELECT 1 as C FROM L3 CROSS JOIN L3 as B ),
    L5 AS ( SELECT 1 as C FROM L4 CROSS JOIN L4 as B ),
    L6 AS ( SELECT 1 as C FROM L5 CROSS JOIN L5 as B),
    Nums as (SELECT ROW_NUMBER() OVER (ORDER BY C) as C FROM L6) 
insert ##NumberCreation(C)
SELECT TOP 500000 C
FROM Nums

Le code ci-dessus générera 500k enregistrements dans une table temporaire globale, vous pouvez les exporter avec les commandes suivantes. Si vous l'exécutez à partir de SSMS, assurez-vous que vous êtes en mode SQLCMD:

--Export
!!bcp ##NumberCreation out "E:\SQLServer\Backup\test\export.dat" -T -n

--format file
!!bcp ##NumberCreation format nul -T -n  -f "E:\SQLServer\Backup\test\export.fmt"

Assurez-vous de choisir un répertoire dans lequel le compte de service SQL Server dispose d'autorisations d'écriture et si vous l'exécutez à partir de SSMS, exécutez-le localement sur SQL Server.

La prochaine étape consiste à démarrer une boucle d'insertion en bloc. Pendant que la boucle est en cours d'exécution, ouvrez un deuxième écran et commencez à exécuter sp_lock jusqu'à ce que vous voyiez le verrou partagé ENCRYPTION_SCAN dans DB_ID 2 (qui est Tempdb).

La boucle d'importation en masse:

BEGIN
    IF OBJECT_ID('tempdb..#Import') IS NOT NULL
        DROP TABLE #Import ;

    CREATE TABLE #Import (C INT) ;
    BULK INSERT #Import
    FROM 'E:\SQLServer\Backup\test\export.dat' WITH (FORMATFILE='E:\SQLServer\Backup\test\export.fmt', FIRSTROW=1, TABLOCK) ;
END
GO 500 --run it 500 times

Voir le résultat de sp_lock dans la deuxième fenêtre:

entrez la description de l'image ici

TRIER À TEMPDB

Avec la même table Temp en place, lancez cette boucle très simple:

SELECT * from #Import order by C
go 50

Il produira le plan d'exécution suivant:

entrez la description de l'image ici

(Assurez-vous que #Import est réellement rempli, car selon le moment où vous avez arrêté la boucle d'importation en bloc précédente, il peut être vide!)

Encore une fois, exécutez sp_lock dans une deuxième fenêtre jusqu'à ce que la ressource ENCRYPTION_SCAN apparaisse:

entrez la description de l'image ici

Vous savez maintenant pourquoi cette attente de ressource apparaît. Il se pourrait très bien que ce ne soit pas votre problème. Je voulais simplement souligner les autres raisons qui font apparaître ENCRYPTION_SCAN. La raison du ralentissement de votre requête pourrait être autre chose. Je laisserai l'amélioration de votre plan de requête aux experts du plan de requête sur ce site ;-) Cependant, pourriez-vous également publier le plan d'exécution réel au lieu du plan estimé?

Edward Dortland
la source
Donc, ENCRYPTION_SCANc'est un peu un hareng rouge alors. C'est une Sserrure. La théorie est donc que pendant la période de 5 heures où le PO voit le, ENCRYPTION_SCAN il est en fait juste occupé à faire l'insertion. Pas bloqué par ça?
Martin Smith
Oui, je pense que oui. Pourrait-il être bon de voir les attentes cumulées pour ce spid pendant l'exécution, en utilisant des événements étendus? Pour confirmer que l'utilisateur attend en fait autre chose? Ce qui me dérange, c'est la "base de données à utilisateur unique est la seule instance de ce comportement ....... 70 autres utilisateurs du logiciel qui ne présentent pas ce problème." Serait-ce un mauvais plan? (Je ne vois aucun paramètre dans cette partie de la requête) Nous aurions besoin du plan réel ..
Edward Dortland
@EdwardDortland Je vais essayer de publier le plan de requête réel. Re: Martin Smith, je ne comprends toujours pas pourquoi je ne verrais pas l'encart dans le rapport des transactions par âge pour tempdb ou notre base de données d'utilisateurs, bien que le hareng rouge le décrirait. Dans la base de données de reproduction, j'ai réécrit la clause where à utiliser ou les instructions au lieu de isnull, et lors des tests jusqu'à présent (+ déployé au début de la semaine dernière), l'insertion s'exécute en quelques minutes sans ENCRYPTION_SCAN.
Robert Gannon
@RobertGannon serait vraiment sympa de voir les différences entre le plan réel de la requête d'origine et le plan retravaillé.
Edward Dortland
2
Un article de blog récent pertinent confirmant toutes les informations ici blogs.msdn.microsoft.com/psssql/2016/04/19/…
Martin Smith