과도한 정렬 메모리 부여


45

이 간단한 쿼리에 왜 그렇게 많은 메모리가 부여됩니까?

-- Demo table
CREATE TABLE dbo.Test
(
    TID integer IDENTITY NOT NULL,
    FilterMe integer NOT NULL,
    SortMe integer NOT NULL,
    Unused nvarchar(max) NULL,

    CONSTRAINT PK_dbo_Test_TID
    PRIMARY KEY CLUSTERED (TID)
);
GO
-- 100,000 example rows
INSERT dbo.Test WITH (TABLOCKX)
    (FilterMe, SortMe)
SELECT TOP (100 * 1000)
    CHECKSUM(NEWID()) % 1000,
    CHECKSUM(NEWID())
FROM sys.all_columns AS AC1
CROSS JOIN sys.all_columns AS AC2;
GO    
-- Query
SELECT
    T.TID,
    T.FilterMe,
    T.SortMe,
    T.Unused
FROM dbo.Test AS T 
WHERE 
    T.FilterMe = 567
ORDER BY 
    T.SortMe;

약 50 개의 행에 대해 최적화 프로그램은 정렬을 위해 거의 500MB를 예약합니다.

예상 계획

답변:


42

이것은 SQL Server 의 버그 입니다 (2008 년부터 2014 년까지).

내 버그 리포트는 여기에 있습니다 .

필터링 조건이 잔존 술어로 스캔 연산자로 푸시 다운되지만 정렬에 부여 된 메모리는 사전 필터 카디널리티 추정치 에 따라 잘못 계산 됩니다 .

이 문제를 설명하기 위해 (문서화되지 않은 및 지원되지 않는) 추적 플래그 9130을 사용하여 필터 가 검색 연산자 로 푸시 다운 되는 것을 방지 할 수 있습니다. 정렬에 부여 된 메모리는 이제 스캔이 아니라 필터 출력의 예상 카디널리티를 기반으로 올바르게 설정됩니다.

SELECT
    T.TID,
    T.FilterMe,
    T.SortMe,
    T.Unused
FROM dbo.Test AS T 
WHERE 
    T.FilterMe = 567
ORDER BY 
    T.SortMe
OPTION (QUERYTRACEON 9130); -- Not for production systems!

예상 계획

A의 생산 시스템 , 단계가주의해야 할 필요가 방지 문제가 평면 형상 (다른 열 상에 정렬하여 주사로 가압 필터). 이를 수행하는 한 가지 방법은 필터 조건에 대한 색인을 제공하거나 필요한 정렬 순서를 제공하는 것입니다.

-- Index on the filter condition only
CREATE NONCLUSTERED INDEX IX_dbo_Test_FilterMe
ON dbo.Test (FilterMe);

이 인덱스가 있으면 정렬에 필요한 메모리 부여는 928KB 에 불과합니다 .

필터 인덱스

더 나아가, 다음 인덱스는 일종의 완전히 (피할 수 제로 메모리 부여를)

-- Provides filtering and sort order
-- nvarchar(max) column deliberately not INCLUDEd
CREATE NONCLUSTERED INDEX IX_dbo_Test_FilterMe_SortMe
ON dbo.Test (FilterMe, SortMe);

필터 및 정렬 색인

다음 SQL Server x64 Developer Edition 빌드에서 테스트 및 버그 확인 :

2014   : 12.00.2430 (RTM CU4)
2012   : 11.00.5556 (SP2 CU3)
2008R2 : 10.50.6000 (SP3)
2008   : 10.00.6000 (SP4)

이것은 SQL Server 2016 서비스 팩 1 에서 수정 되었습니다 . 릴리스 정보는 다음과 같습니다.

VSTS 버그 번호 8024987
푸시 다운 술어가있는 테이블 스캔 및 인덱스 스캔은 상위 연산자에 대한 메모리 부여를 과대 평가하는 경향이 있습니다.

테스트 및 고정 확인 :

  • Microsoft SQL Server 2016 (SP1) - 13.0.4001.0 (X64) Developer Edition
  • Microsoft SQL Server 2014 (SP2-CU3) 12.0.5538.0 (X64) Developer Edition

두 CE 모델.


5

SQL 2012 년 이후부터 당신은 많은 사이의 불일치를 찾아 SerialRequiredMemorySerialDesiredMemory,이 같은 예를 들어 뭔가 :

-- Search plan cache for Memory Grant issues
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp

-- Collect more info about the plan here if required, eg usecounts, objtype etc, 
SELECT IDENTITY( INT, 1, 1 ) rowId, query_plan
INTO #tmp
FROM sys.dm_exec_cached_plans cp WITH(NOLOCK)
    CROSS APPLY sys.dm_exec_query_plan(plan_handle)
GO


;WITH cte AS
(
SELECT
    rowId,
    query_plan,
    m.c.value ('@SerialRequiredMemory', 'INT' ) AS SerialRequiredMemory,
    m.c.value ('@SerialDesiredMemory', 'INT' ) AS SerialDesiredMemory

FROM #tmp t
    CROSS APPLY t.query_plan.nodes ( '//*:MemoryGrantInfo[@SerialDesiredMemory[. > 0]]' ) m(c)
), cte2 AS (
SELECT *,
    CAST( CAST( SerialDesiredMemory AS DECIMAL(10,2) ) / CAST( SerialRequiredMemory AS DECIMAL(10,2) ) AS DECIMAL(10,2) ) Desired_to_Required_ratio
FROM cte
)
SELECT TOP 20
    rowId,
    query_plan,
    SerialRequiredMemory SerialRequiredMemory_KB,
    SerialDesiredMemory SerialDesiredMemory_KB,
    CAST( SerialRequiredMemory / 1024. AS DECIMAL(10,2) ) SerialRequiredMemory_MB,
    CAST( SerialDesiredMemory / 1024. AS DECIMAL(10,2) ) SerialDesiredMemory_MB,
    Desired_to_Required_ratio
FROM cte2
WHERE Desired_to_Required_ratio > 100
ORDER BY Desired_to_Required_ratio DESC

이러한 새로운 속성에 대한 추가 정보는 여기를 참조하십시오 . 이 쿼리는 약간 거칠고 준비되었지만 SQL Server 2014 dev 상자에서 975.47의 비율과 몇 가지 다른 시선을 끄는 계획으로 과도한 정렬 쿼리를 선택했습니다. '정상적인'비율 (적어도 제한된 테스트 결과)은 ~ 1 인 것 같습니다.

HTH


3

모든 도움을 주셔서 감사합니다. 도움이되는 위의 쿼리의 업데이트 된 버전을 보내겠다고 생각했습니다.

-- Search plan cache for Memory Grant issues
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp

-- Collect more info about the plan here if required, eg usecounts, objtype etc, 
SELECT IDENTITY( INT, 1, 1 ) rowId, query_plan, db = DB_NAME(CAST(pa.value AS int))
INTO #tmp
FROM sys.dm_exec_cached_plans cp WITH(NOLOCK)
    CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle)
    OUTER APPLY sys.dm_exec_plan_attributes(cp.plan_handle) pa 
    WHERE pa.attribute = 'dbid' 
GO

;WITH cte AS
(
SELECT
    rowId,
    query_plan,
    m.c.value ('@SerialRequiredMemory', 'INT' ) AS SerialRequiredMemory,
    m.c.value ('@SerialDesiredMemory', 'INT' ) AS SerialDesiredMemory,
    db
FROM #tmp t
    CROSS APPLY t.query_plan.nodes ( '//*:MemoryGrantInfo[@SerialDesiredMemory[. > 0]]' ) m(c)
), cte2 AS (
SELECT *,
    CAST( CAST( SerialDesiredMemory AS DECIMAL(10,2) ) / CAST( SerialRequiredMemory AS DECIMAL(10,2) ) AS DECIMAL(10,2) ) Desired_to_Required_ratio
FROM cte
)
SELECT TOP 20
    rowId,
    query_plan,
    SerialRequiredMemory SerialRequiredMemory_KB,
    SerialDesiredMemory SerialDesiredMemory_KB,
    CAST( SerialRequiredMemory / 1024. AS DECIMAL(10,2) ) SerialRequiredMemory_MB,
    CAST( SerialDesiredMemory / 1024. AS DECIMAL(10,2) ) SerialDesiredMemory_MB,
    Desired_to_Required_ratio,
    db
FROM cte2
WHERE Desired_to_Required_ratio > 100
ORDER BY Desired_to_Required_ratio DESC
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.