Planifier la taille du cache et la mémoire réservée

18

Lors de l'exécution d'une requête incluant le plan d'exécution réel, l'opérateur racine ( SELECT) m'indique que la taille du plan mis en cache est de 32 Ko.

Une requête qui joint sys.dm_exec_cached_plansetsys.dm_os_memory_objects , en examinant le plan en question, indique que les valeurs de pages_in_byteset max_pages_in_bytessont 32768 (32 Ko), ce qui correspond à la taille du plan mis en cache.

Ce que je ne comprends pas, c'est ce que représente la valeur sys.dm_exec_cached_plans.size_in_bytes, qui est 49152 (48 Ko). J'ai lu BOL sur toutes ces colonnes, et surtoutsize_in_bytes qui dit:

" Nombre d'octets consommés par l'objet cache. "

Je ne peux pas mettre en place ce dernier morceau du puzzle, pour comprendre ce que cela signifie vraiment.

Je sais que tous les opérateurs (sans parler de l'allocation de mémoire supplémentaire utilisée pour les tris et les hachages) nécessitent une certaine quantité de mémoire fixe, pour stocker l'état, faire des calculs, etc., qui est stockée avec le plan optimisé dans le cache, mais où?

Donc, mes questions sont:

  • Qu'est-ce que size_in_bytes vraiment
  • Pourquoi est-ce une valeur plus élevée que "Taille du plan mis en cache"?
  • Où est réservée la quantité fixe de mémoire pour tous les opérateurs / itérateurs, est-ce avec la "taille du plan mis en cache" (32 Ko dans mon exemple), ou ailleurs?

Je sais que ce sont des DMV différents avec des fonctions différentes, mais ils sont liés. Les plans compilés (mis en cache) dans les sys.dm_exec_cached_plansjointures sys.dm_os_memory_objectssurmemory_object_address colonne. La raison pour laquelle je poste les questions ici, c'est que je demande de l'aide à ce sujet, en comprenant comment interpréter les DMV et leurs colonnes.

Si size_in_bytes la taille du plan mis en cache est, pourquoi SQL Server indique-t-il une autre valeur dans le plan d'exécution réel?

Nouvelle requête, nouveaux numéros:

  • Plan réel
    • Taille du plan en cache 16 Ko
    • CompileMemory 96KB
  • DMV:
    • sys.dm_exec_cached_plans.size_in_bytes 24 Ko
    • sys.dm_os_memory_objects.pages_in_bytes, .max_pages_in_bytes 16 Ko.

Notez également que cette requête ne nécessite aucune allocation de mémoire supplémentaire pour les opérations de tri et de hachage.

Microsoft SQL Server 2012 - 11.0.5343.0 (X64)
GordonLiddy
la source

Réponses:

12

La raison pour laquelle le size_in_byteschamp du sys.dm_exec_cached_plansDMV, au moins en termes de "plans compilés", est plus grand que l' CachedPlanSizeattribut du QueryPlannœud dans le plan XML, car un plan compilé n'est pas la même chose qu'un plan de requête. Un plan compilé est composé de plusieurs objets mémoire, dont la taille combinée équivaut au size_in_byteschamp. Ainsi, la description de " Nombre d'octets consommés par l'objet cache » que vous avez trouvée dans la documentation est exacte; c'est juste qu'il est facile de mal interpréter ce que l'on entend par "objet cache" étant donné le nom du DMV et que le terme "plan" a plusieurs significations.

Un plan compilé est un conteneur qui contient divers éléments d'information liés au lot de requêtes (c'est-à-dire pas seulement une seule instruction), un (ou plusieurs) de ces éléments étant le ou les plans de requête. Les plans compilés ont un objet mémoire de niveau supérieur de MEMOBJ_COMPILE_ADHOC qui est la ligne sys.dm_os_memory_objectsqui est liée via le memory_object_addresschamp dans les deux DMV. Cet objet mémoire contient la table des symboles, la collection de paramètres, les liens vers les objets associés, le cache d'accesseur, le cache de métadonnées TDS et éventuellement d'autres éléments. Les plans compilés sont partagés entre les sessions / utilisateurs qui exécutent le même lot avec les mêmes paramètres de session. Cependant, certains objets associés ne sont pas partagés entre les sessions / utilisateurs.

Les plans compilés ont également un ou plusieurs objets dépendants qui peuvent être trouvés en passant le plan_handle(in sys.dm_exec_cached_plans) dans le sys.dm_exec_cached_plan_dependent_objectsDMF. Il existe deux types d'objets dépendants: le plan exécutable (objet mémoire = MEMOBJ_EXECUTE ) et le curseur (objet mémoire = MEMOBJ_CURSOREXEC ). Il y aura 0 ou plusieurs objets Cursor, un pour chaque curseur. Il y aura également un ou plusieurs objets de plan exécutable, un pour chaque utilisateur exécutant ce même lot , donc les plans exécutables ne sont paspartagé entre les utilisateurs. Les plans exécutables contiennent des paramètres d'exécution et des informations sur les variables locales, l'état d'exécution tel que l'instruction en cours d'exécution, les ID d'objet pour les objets créés au moment de l'exécution (je suppose que cela fait référence aux variables de table, aux tables temporaires, aux procédures stockées temporaires, etc.) , et éventuellement d'autres éléments.

Chaque instruction d' un lot à instructions multiples est contenue dans une instruction compilée (objet mémoire = MEMOBJ_STATEMENT ). La taille de chaque instruction compilée (c.-à-d. pages_in_bytes) Divisée par 1024 doit correspondre aux CachedPlanSize="xx"valeurs des <QueryPlan>nœuds dans le plan XML. Les instructions compilées ont souvent un (éventuellement plus?) Plans d'exécution de requête associés (objet mémoire = MEMOBJ_XSTMT ). Enfin, pour chaque plan de requête d'exécution qui est une requête, il doit y avoir un contexte d'exécution de requête associé (objet mémoire = MEMOBJ_QUERYEXECCNTXTFORSE ).

En ce qui concerne les instructions compilées, les lots d'instructions uniques n'ont pas d' objets d' instructions compilées distinctes (par exemple MEMOBJ_STATEMENT ) ni de plan de requête d'exécution distinct (par exemple MEMOBJ_XSTMT ). La valeur de chacun de ces objets sera stockée dans l'objet principal du plan compilé (c'est-à-dire MEMOBJ_COMPILE_ADHOC ), et dans ce cas, la pages_in_bytesvaleur de cet objet principal divisée par 1024 devrait correspondre à la CachedPlanSizetaille du <QueryPlan>nœud du plan XML. Cependant, ces valeurs ne seront pas égales dans les lots à instructions multiples.


La size_in_bytesvaleur peut être dérivée en additionnant les entrées dans le sys.dm_os_memory_objectsDMV (les éléments indiqués ci-dessus en gras), tous liés par dm_os_memory_objects.page_allocator_addresspour ce plan compilé. L'astuce pour obtenir la valeur correcte consiste à obtenir d'abord le à memory_object_addresspartir sys.dm_exec_cached_plansd'un plan compilé particulier, puis à l'utiliser pour obtenir la ligne MEMOBJ_COMPILE_ADHOC correspondante en sys.dm_os_memory_objectsfonction de son memory_object_addresschamp. Ensuite, récupérez la page_allocator_addressvaleur de sys.dm_os_memory_objectspour cette ligne et utilisez-la pour extraire toutes les lignes sys.dm_os_memory_objectsqui ont la même page_allocator_addressvaleur. (Veuillez noter que cette technique ne fonctionne pas pour les autres types d'objets mis en cache: arbre d'analyse , proc étendu , CLR compilé Proc et CLR compilé Func.)

À l'aide de la memory_object_addressvaleur obtenue à partir de sys.dm_exec_cached_plans, vous pouvez voir tous les composants du plan compilé via la requête suivante:

DECLARE @CompiledPlanAddress VARBINARY(8) = 0x00000001DC4A4060;

SELECT obj.memory_object_address, obj.pages_in_bytes, obj.type
FROM   sys.dm_os_memory_objects obj
WHERE  obj.page_allocator_address = (
                               SELECT planobj.page_allocator_address
                               FROM   sys.dm_os_memory_objects planobj
                               WHERE  planobj.memory_object_address = @CompiledPlanAddress
                              )
ORDER BY obj.[type], obj.pages_in_bytes;

La requête ci-dessous répertorie tous les plans compilés sys.dm_exec_cached_plansainsi que le plan de requête et les instructions pour chaque lot. La requête directement ci-dessus est incorporée dans la requête ci-dessous via XML comme MemoryObjectschamp:

SELECT cplan.bucketid,
       cplan.pool_id,
       cplan.refcounts,
       cplan.usecounts,
       cplan.size_in_bytes,
       cplan.memory_object_address,
       cplan.cacheobjtype,
       cplan.objtype,
       cplan.plan_handle,
       '---' AS [---],
       qrypln.[query_plan],
       sqltxt.[text],
       '---' AS [---],
       planobj.pages_in_bytes,
       planobj.pages_in_bytes / 1024 AS [BaseSingleStatementPlanKB],
       '===' AS [===],
       cplan.size_in_bytes AS [TotalPlanBytes],
       bytes.AllocatedBytes,
       (SELECT CONVERT(VARCHAR(30), obj.memory_object_address, 1)
               AS [memory_object_address], obj.pages_in_bytes, obj.[type]
               --,obj.page_size_in_bytes
        FROM   sys.dm_os_memory_objects obj
        WHERE  obj.page_allocator_address = planobj.page_allocator_address
        FOR XML RAW(N'object'), ROOT(N'memory_objects'), TYPE) AS [MemoryObjects]
FROM   sys.dm_exec_cached_plans cplan
OUTER APPLY sys.dm_exec_sql_text(cplan.[plan_handle]) sqltxt
OUTER APPLY sys.dm_exec_query_plan(cplan.[plan_handle]) qrypln
INNER JOIN sys.dm_os_memory_objects planobj
        ON planobj.memory_object_address = cplan.memory_object_address
OUTER APPLY (SELECT SUM(domo.[pages_in_bytes]) AS [AllocatedBytes]
             FROM   sys.dm_os_memory_objects domo
             WHERE  domo.page_allocator_address = planobj.page_allocator_address) bytes
WHERE  cplan.parent_plan_handle IS NULL
AND    cplan.cacheobjtype IN (N'Compiled Plan', N'Compiled Plan Stub')
--AND cplan.plan_handle = 0x06000D0031CD572910529CE001000000xxxxxxxx
ORDER BY cplan.objtype, cplan.plan_handle;

Veuillez noter que:

  • le TotalPlanByteschamp est juste une ré-déclaration du sys.dm_exec_cached_plans.size_in_byteschamp,
  • le AllocatedByteschamp est la somme des objets de mémoire associés qui correspondent généralement TotalPlanBytes(c.-à-d. size_in_bytes)
  • le AllocatedByteschamp sera parfois supérieur à TotalPlanBytes(ie size_in_bytes) en raison de l'augmentation de la consommation de mémoire pendant l'exécution. Cela semble se produire principalement en raison de la recompilation (ce qui devrait être évident avec le usecountschamp affiché 1)
  • le BaseSingleStatementPlanKBchamp doit correspondre à l' CachedPlanSizeattribut du QueryPlannœud dans le XML, mais uniquement lors de l'utilisation d'un seul lot de requêtes.
  • pour les lots comportant plusieurs requêtes, il doit y avoir des lignes marquées comme MEMOBJ_STATEMENTdans sys.dm_os_memory_objects, une pour chaque requête. Le pages_in_byteschamp de ces lignes doit correspondre aux <QueryPlan>nœuds individuels du plan XML.

Ressources:

Solomon Rutzky
la source