Cấp bộ nhớ sắp xếp quá mức


45

Tại sao truy vấn đơn giản này được cấp rất nhiều bộ nhớ?

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

Đối với khoảng 50 hàng ước tính, trình tối ưu hóa dự trữ gần 500 MB cho loại:

Kế hoạch dự kiến

Câu trả lời:


42

Đây là một lỗi trong SQL Server (bao gồm từ 2008 đến 2014).

Báo cáo lỗi của tôi là ở đây .

Điều kiện lọc được đẩy xuống toán tử quét dưới dạng một biến vị ngữ còn lại, nhưng bộ nhớ được cấp cho loại được tính toán sai dựa trên ước tính số lượng thẻ lọc trước .

Để minh họa vấn đề, chúng ta có thể sử dụng cờ theo dõi (không có giấy tờ và không được hỗ trợ) 9130 để ngăn Bộ lọc không bị đẩy xuống toán tử quét . Bộ nhớ được cấp cho sắp xếp hiện chính xác dựa trên số lượng ước tính của đầu ra Bộ lọc, chứ không phải quét:

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!

Kế hoạch dự kiến

Đối với một hệ thống sản xuất , các bước sẽ cần phải được thực hiện để tránh hình dạng kế hoạch có vấn đề (bộ lọc được đẩy vào quét với một sắp xếp trên cột khác). Một cách để làm điều này là cung cấp một chỉ mục về điều kiện lọc và / hoặc để cung cấp thứ tự sắp xếp cần thiết.

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

Với chỉ số này, vị trí cấp bộ nhớ mong muốn cho loại chỉ là 928KB :

Với chỉ số bộ lọc

Đi xa hơn, chỉ mục sau đây có thể tránh được việc sắp xếp hoàn toàn ( cấp bộ nhớ bằng 0 ):

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

Với bộ lọc và chỉ mục sắp xếp

Đã kiểm tra và xác nhận lỗi trên các bản dựng sau của 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)

Điều này đã được sửa trong SQL Server 2016 Gói dịch vụ 1 . Các ghi chú phát hành bao gồm:

Số lỗi VSTS 8024987 Quét
bảng và quét chỉ mục với vị từ đẩy xuống có xu hướng đánh giá quá cao việc cấp bộ nhớ cho toán tử cha.

Đã kiểm tra và xác nhận cố định trên:

  • 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

Cả hai mô hình CE.


5

Từ SQL 2012 trở đi, bạn có thể tìm kiếm sự khác biệt lớn giữa SerialRequiredMemorySerialDesiredMemory, ví dụ như một cái gì đó như thế này:

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

Một số lưu ý thêm về các thuộc tính mới ở đây . Truy vấn này hơi khó hiểu và sẵn sàng nhưng đã nhận được truy vấn sắp xếp quá mức từ hộp dev SQL Server 2014 của tôi với tỷ lệ 975,47 cộng với một vài kế hoạch bắt mắt khác. Tỷ lệ 'bình thường' (ít nhất là từ thử nghiệm giới hạn của tôi) dường như là ~ 1.

HTH


3

Cảm ơn vì sự giúp đỡ. Tôi nghĩ rằng tôi sẽ gửi một phiên bản cập nhật của truy vấn trên mà chúng tôi thấy hữu ích.

-- 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.