การอนุญาตหน่วยความจำเรียงมากเกินไป


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 แถวตัวเพิ่มประสิทธิภาพจะสำรองเกือบ 500 MB สำหรับการเรียงลำดับ:

แผนโดยประมาณ

คำตอบ:


42

นี่เป็นข้อผิดพลาดใน SQL Server (รวมตั้งแต่ปี 2008 ถึงปี 2014)

รายงานข้อผิดพลาดของฉันคือที่นี่

เงื่อนไขการกรองจะถูกผลักลงไปในผู้ประกอบการสแกนเป็นกริยาที่เหลือ แต่ความทรงจำที่ได้รับสำหรับการจัดเรียงที่มีการคำนวณอย่างไม่ถูกต้องอยู่บนพื้นฐานของการประมาณการ cardinality กรองก่อน

เพื่อแสดงปัญหาเราสามารถใช้การตั้งค่าสถานะการสืบค้นกลับ(ที่ไม่มีเอกสารและไม่สนับสนุน) 9130 เพื่อป้องกันไม่ให้ตัวกรองถูกส่งลงไปยังผู้ดำเนินการสแกน ขณะนี้หน่วยความจำที่ให้กับการเรียงลำดับถูกต้องขึ้นอยู่กับ cardinality โดยประมาณของเอาต์พุต Filter ไม่ใช่การสแกน:

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!

แผนโดยประมาณ

สำหรับระบบที่ใช้งานจริงจะต้องดำเนินการตามขั้นตอนต่างๆเพื่อหลีกเลี่ยงรูปร่างของปัญหาที่มีปัญหา (ตัวกรองถูกผลักเข้าไปในการสแกนพร้อมจัดเรียงคอลัมน์อื่น) วิธีหนึ่งในการทำเช่นนี้คือการจัดทำดัชนีเกี่ยวกับเงื่อนไขตัวกรองและ / หรือเพื่อจัดลำดับการเรียงที่ต้องการ

-- 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 Service Pack 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 เป็นต้นไปคุณสามารถค้นหาความแตกต่างขนาดใหญ่ระหว่างSerialRequiredMemoryและSerialDesiredMemoryเช่นบางสิ่งเช่นนี้:

-- 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

บางบันทึกเพิ่มเติมเกี่ยวกับคุณลักษณะใหม่ ๆ เหล่านี้ที่นี่ แบบสอบถามนี้ค่อนข้างหยาบและพร้อมใช้งาน แต่รับคิวการเรียงลำดับที่มากเกินไปจากกล่อง dev SQL Server 2014 ของฉันด้วยอัตราส่วน 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.