web-dev-qa-db-fra.com

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

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 taille du plan mis en cache est de 32 Ko.

Une requête qui rejoint sys.dm_exec_cached_plans et sys.dm_os_memory_objects, en regardant le plan en question, dit que les valeurs de pages_in_bytes et max_pages_in_bytes sont 32 768 (32 Ko), ce qui correspond à la taille du plan mis en cache.

Ce que je ne comprends pas, c'est la valeur de sys.dm_exec_cached_plans.size_in_bytes, qui représente 49152 (48 Ko). J'ai lu BOL sur toutes ces colonnes, et surtout size_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é avec le plan optimisé dans le cache, mais où?

Donc, mes questions sont:

  • Qu'est-ce que size_in_bytes Vraiment méchant
  • Pourquoi est-ce une valeur plus élevée que "Taille du plan mis en cache"?
  • Où est la quantité fixe de mémoire réservée à 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 sys.dm_exec_cached_plans rejoint sys.dm_os_memory_objects sur memory_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 est la taille du plan mis en cache, 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)
18
GordonLiddy

La raison pour laquelle le size_in_bytes champ du sys.dm_exec_cached_plans DMV, au moins en termes de "plans compilés", est plus grand que l'attribut CachedPlanSize du nœud QueryPlan dans le plan XML parce qu'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 à size_in_bytes champ. 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 diverses informations relatives à la requête batch (c'est-à-dire pas seulement une seule instruction), une (ou plusieurs) de ces pièces é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 dans sys.dm_os_memory_objects qui est lié via le memory_object_address champ 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 batch avec les mêmes paramètres de session. Cependant, certains objets associés 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 (dans sys.dm_exec_cached_plans) dans le sys.dm_exec_cached_plan_dependent_objects DMF. Il existe deux types d'objets dépendants: Plan exécutable (objet mémoire = MEMOBJ_EXECUTE ) et 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 Plan exécutable, un pour chaque utilisateur exécutant le même batch, donc les plans exécutables sont pas partagés 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 dans un lot multi-instructions est contenue dans une instruction compilée (objet mémoire = MEMOBJ_STATEMENT ). La taille de chaque instruction compilée (c'est-à-dire pages_in_bytes) divisé par 1024 devrait correspondre à CachedPlanSize="xx" valeurs de <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'instruction compilée distincte (c.-à-d. MEMOBJ_STATEMENT ) ou un plan de requête d'exécution distinct (c.-à-d. MEMOBJ_XSTMT ) objets. 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, le pages_in_bytes la valeur de cet objet principal divisée par 1024 doit correspondre à la taille CachedPlanSize dans le <QueryPlan> noeud du plan XML. Cependant, ces valeurs ne seront pas égales dans les lots à instructions multiples.


Le size_in_bytes la valeur peut être dérivée en additionnant les entrées dans le sys.dm_os_memory_objects DMV (les éléments indiqués ci-dessus en gras), tous liés par dm_os_memory_objects.page_allocator_address pour ce plan compilé. L'astuce pour obtenir la valeur correcte consiste à obtenir d'abord le memory_object_address de sys.dm_exec_cached_plans pour un plan compilé particulier, puis utilisez-le pour obtenir la ligne correspondante MEMOBJ_COMPILE_ADHOC de sys.dm_os_memory_objects en fonction de son memory_object_address champ. Ensuite, prenez le page_allocator_address valeur de sys.dm_os_memory_objects pour cette ligne, et utilisez-la pour récupérer toutes les lignes de sys.dm_os_memory_objects qui ont le même page_allocator_address valeur. (Veuillez noter que cette technique ne fonctionne pas pour les autres types d'objets mis en cache: Parse Tree , Extended Proc , CLR Compiled Proc , et CLR Compiled Func .)

En utilisant le memory_object_address valeur obtenue 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 dans sys.dm_exec_cached_plans avec 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 en tant que champ MemoryObjects:

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 champ TotalPlanBytes n'est qu'une ré-déclaration du sys.dm_exec_cached_plans.size_in_bytes champ,
  • le champ AllocatedBytes est la somme des objets mémoire associés qui correspond généralement à TotalPlanBytes (c'est-à-dire size_in_bytes)
  • le champ AllocatedBytes sera parfois supérieur à TotalPlanBytes (c'est-à-dire 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 champ usecounts affichant 1)
  • le champ BaseSingleStatementPlanKB devrait correspond à l'attribut CachedPlanSize du nœud QueryPlan dans le XML, mais seulement = 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_STATEMENT dans sys.dm_os_memory_objects, un pour chaque requête. Le pages_in_bytes le champ de ces lignes doit correspondre à l'individu <QueryPlan> nœuds du plan XML.

Ressources:

12
Solomon Rutzky

Pro SQL Server Internals a examiné ce problème.

dbcc freeproccache
GO
DECLARE
@SQL nvarchar (MAX)
,@I INT = 0
while @I < 1000
BEGIN
    SELECT @SQL = N'declare @C int;select @C=ID from dbo.Employees where ID='
           + CONVERT (nvarchar (10), @I);

    EXEC (@SQL);
    SELECT @I += 1;
END
GO


SELECT
p.usecounts, p.cacheobjtype, p.objtype, p.size_in_bytes, t.[text]
FROM
sys.dm_exec_cached_plans p CROSS apply
sys.dm_exec_sql_text (p.plan_handle) t
WHERE
p.cacheobjtype LIKE 'Compiled Plan%' AND
t.[text] LIKE '%Employees%'
ORDER BY
p.objtype DESC;

1 000 plans sont mis en cache, chacun utilisant 32 Ko de mémoire ou 32 Mo au total. Comme vous pouvez le deviner , les requêtes ad hoc dans les systèmes occupés peuvent entraîner une utilisation excessive de la mémoire cache du plan.

exec sys.sp_configure N'optimize for ad hoc workloads', N'1';
reconfigure with override;

avec le paramètre Optimiser pour les charges de travail ad-hoc activé, vous verriez le contenu du cache du plan. Comme vous pouvez le voir, il utilise désormais seulement 272 Ko de mémoire au lieu des 32 Mo qu'il utilisait auparavant

enter image description here

enter image description here

1
Ali varzeshi