Причина, по которой size_in_bytesполе sys.dm_exec_cached_plansDMV, по крайней мере в терминах «Скомпилированные планы», больше, чем CachedPlanSizeатрибут QueryPlanузла в плане XML, заключается в том, что скомпилированный план - это не то же самое, что план запроса. Скомпилированный план состоит из нескольких объектов памяти, общий размер которых равен size_in_bytesполю. Итак, описание « Количество байтов, использованных объектом кэша », которое вы нашли в документации, является точным; это просто, что легко неверно истолковать, что подразумевается под «объектом кэша», заданным именем DMV, и что термин «план» имеет несколько значений.
Скомпилированный план представляет собой контейнер , который содержит различные элементы информации , связанные с запросом пакета (то есть не только одно заявление), один (или более) из этих частей , являющихся план запроса (ов). Скомпилированные планы имеют объект памяти верхнего уровня MEMOBJ_COMPILE_ADHOC, который является строкой sys.dm_os_memory_objects, связанной через memory_object_addressполе в обоих DMV. Этот объект памяти содержит таблицу символов, набор параметров, ссылки на связанные объекты, кэш доступа, кэш метаданных TDS и, возможно, некоторые другие элементы. Скомпилированные планы распределяются между сеансами / пользователями, которые выполняют один и тот же пакет с одинаковыми настройками сеанса. Однако некоторые связанные объекты не являются общими для сеансов / пользователей.
Скомпилированные планы также имеют один или несколько зависимых объектов, которые можно найти, передав plan_handle(в sys.dm_exec_cached_plans) в sys.dm_exec_cached_plan_dependent_objectsDMF. Существует два типа зависимых объектов: исполняемый план (объект памяти = MEMOBJ_EXECUTE ) и курсор (объект памяти = MEMOBJ_CURSOREXEC ). Там будет 0 или более объектов Cursor, по одному на каждый курсор. Также будет один или несколько объектов исполняемого плана, по одному на каждого пользователя, выполняющего тот же пакет , следовательно, исполняемые планы не являютсяделится между пользователями. Исполняемые планы содержат параметр времени выполнения и информацию о локальной переменной, состояние времени выполнения, такое как текущий выполняемый оператор, идентификаторы объектов для объектов, созданных во время выполнения (я предполагаю, что это относится к переменным таблиц, временным таблицам, временным хранимым процедурам и т. Д.) и, возможно, другие предметы.
Каждый оператор в пакете с несколькими операторами содержится в скомпилированном операторе (объект памяти = MEMOBJ_STATEMENT ). Размер каждого скомпилированного pages_in_bytesоператора (т. Е. ), Деленный на 1024, должен соответствовать CachedPlanSize="xx"значениям <QueryPlan>узлов в плане XML. Скомпилированные операторы часто имеют один (возможно, более?) Связанный план выполнения во время выполнения (объект памяти = MEMOBJ_XSTMT ). Наконец, для каждого плана выполнения во время выполнения, который является запросом, должен быть связанный контекст выполнения запроса (объект памяти = MEMOBJ_QUERYEXECCNTXTFORSE ).
Что касается скомпилированных операторов, то пакеты с одним оператором не имеют отдельных объектов скомпилированного оператора (например, MEMOBJ_STATEMENT ) или отдельного плана выполнения (например, MEMOBJ_XSTMT ). Значение для каждого из этих объектов будет храниться в основном объекте скомпилированного плана (т.е. MEMOBJ_COMPILE_ADHOC ), и в этом случае pages_in_bytesзначение этого основного объекта, деленное на 1024, должно соответствовать CachedPlanSizeразмеру в <QueryPlan>узле плана XML. Однако эти значения не будут равны в пакетах с несколькими утверждениями.
size_in_bytesЗначение может быть получено путем суммирования записей в sys.dm_os_memory_objectsDMV (пункты отмечено выше жирным шрифтом), все связаны dm_os_memory_objects.page_allocator_addressдля этого Составитель плана. Хитрость , чтобы получить правильное значение является первым получить memory_object_addressот sys.dm_exec_cached_plansдля конкретного Составитель плана, а затем использовать это , чтобы получить соответствующую MEMOBJ_COMPILE_ADHOC строку из sys.dm_os_memory_objectsосновываясь на своем memory_object_addressполе. Затем возьмите page_allocator_addressзначение sys.dm_os_memory_objectsдля этой строки и используйте его, чтобы получить все строки с sys.dm_os_memory_objectsодинаковым page_allocator_addressзначением. (Пожалуйста , обратите внимание , что этот метод не работает для других кэшированных типов объектов: дерево разбора , Extended Proc , CLR Составитель Proc и CLR Составитель Func.)
Используя memory_object_addressзначение, полученное из sys.dm_exec_cached_plans, вы можете увидеть все компоненты скомпилированного плана с помощью следующего запроса:
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;
В приведенном ниже запросе перечислены все скомпилированные планы sys.dm_exec_cached_plansвместе с планом запроса и инструкциями для каждого пакета. Запрос непосредственно выше включен в запрос ниже через XML как 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;
Обратите внимание, что:
TotalPlanBytesполе просто повторно заявление sys.dm_exec_cached_plans.size_in_bytesполя,
AllocatedBytesполе является суммой соответствующих объектов памяти , которые обычно матчей TotalPlanBytes(т.е. size_in_bytes)
AllocatedBytesполе иногда будет больше TotalPlanBytes(то есть size_in_bytes) в связи с потреблением памяти увеличивающегося во время выполнения. Похоже, что это происходит в основном из-за перекомпиляции (что должно быть видно при usecountsотображении поля 1)
BaseSingleStatementPlanKBполе должно соответствовать CachedPlanSizeатрибут QueryPlanузла в XML, но только при использовании одной партии запроса.
- для партий с несколькими запросами, должны быть помечены как строки
MEMOBJ_STATEMENTв sys.dm_os_memory_objects, по одному для каждого запроса. pages_in_bytesПоле для этих строк должны соответствовать отдельным <QueryPlan>узлам плана XML.
Ресурсы: