Loại bỏ toán tử Tra cứu khóa (Clustered) làm chậm hiệu suất


16

Làm cách nào tôi có thể loại bỏ toán tử Tra cứu khóa (Cụm) trong kế hoạch thực hiện của mình?

Bảng tblQuotesđã có một chỉ mục được nhóm (bật QuoteID) và 27 chỉ mục không bao gồm, vì vậy tôi đang cố gắng không tạo thêm nữa.

Tôi đặt cột chỉ mục cụm QuoteIDtrong truy vấn của mình, hy vọng nó sẽ giúp - nhưng thật không may vẫn như vậy.

Kế hoạch thực hiện tại đây .

Hoặc xem nó:

nhập mô tả hình ảnh ở đây

Đây là những gì toán tử Tra cứu khóa nói:

nhập mô tả hình ảnh ở đây

Truy vấn:

declare
        @EffDateFrom datetime ='2017-02-01',
        @EffDateTo   datetime ='2017-08-28'

SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

IF OBJECT_ID('tempdb..#Data') IS NOT NULL
    DROP TABLE #Data 
CREATE TABLE #Data
(
    QuoteID int NOT NULL,   --clustered index

    [EffectiveDate] [datetime] NULL, --not indexed
    [Submitted] [int] NULL,
    [Quoted] [int] NULL,
    [Bound] [int] NULL,
    [Exonerated] [int] NULL,
    [ProducerLocationId] [int] NULL,
    [ProducerName] [varchar](300) NULL,
    [BusinessType] [varchar](50) NULL,
    [DisplayStatus] [varchar](50) NULL,
    [Agent] [varchar] (50) NULL,
    [ProducerContactGuid] uniqueidentifier NULL
)
INSERT INTO #Data
    SELECT 
        tblQuotes.QuoteID,

          tblQuotes.EffectiveDate,
          CASE WHEN lstQuoteStatus.QuoteStatusID >= 1   THEN 1 ELSE 0 END AS Submitted,
          CASE WHEN lstQuoteStatus.QuoteStatusID = 2 or lstQuoteStatus.QuoteStatusID = 3 or lstQuoteStatus.QuoteStatusID = 202 THEN 1 ELSE 0 END AS Quoted,
          CASE WHEN lstQuoteStatus.Bound = 1 THEN 1 ELSE 0 END AS Bound,
          CASE WHEN lstQuoteStatus.QuoteStatusID = 3 THEN 1 ELSE 0 END AS Exonareted,
          tblQuotes.ProducerLocationID,
          P.Name + ' / '+ P.City as [ProducerName], 
        CASE WHEN tblQuotes.PolicyTypeID = 1 THEN 'New Business' 
             WHEN tblQuotes.PolicyTypeID = 3 THEN 'Rewrite'
             END AS BusinessType,
        tblQuotes.DisplayStatus,
        tblProducerContacts.FName +' '+ tblProducerContacts.LName as Agent,
        tblProducerContacts.ProducerContactGUID
FROM    tblQuotes 
            INNER JOIN lstQuoteStatus 
                on tblQuotes.QuoteStatusID=lstQuoteStatus.QuoteStatusID
            INNER JOIN tblProducerLocations P 
                On P.ProducerLocationID=tblQuotes.ProducerLocationID
            INNER JOIN tblProducerContacts 
                ON dbo.tblQuotes.ProducerContactGuid = tblProducerContacts.ProducerContactGUID

WHERE   DATEDIFF(D,@EffDateFrom,tblQuotes.EffectiveDate)>=0 AND DATEDIFF(D, @EffDateTo, tblQuotes.EffectiveDate) <=0
        AND dbo.tblQuotes.LineGUID = '6E00868B-FFC3-4CA0-876F-CC258F1ED22D'--Surety
        AND tblQuotes.OriginalQuoteGUID is null

select * from #Data

Kế hoạch thực hiện:

nhập mô tả hình ảnh ở đây


Các hàng ước tính và thực tế cho thấy một sự khác biệt đáng chú ý. Có lẽ SQL chọn một kế hoạch tồi vì nó không có dữ liệu để ước tính tốt. Bạn có thường xuyên cập nhật số liệu thống kê của mình không?
RDFozz

Câu trả lời:


23

Tra cứu khóa của các hương vị khác nhau xảy ra khi bộ xử lý truy vấn cần lấy các giá trị từ các cột không được lưu trữ trong chỉ mục được sử dụng để định vị các hàng cần thiết cho truy vấn để trả về kết quả.

Lấy ví dụ mã sau đây, trong đó chúng tôi đang tạo một bảng với một chỉ mục duy nhất:

USE tempdb;

IF OBJECT_ID(N'dbo.Table1', N'U') IS NOT NULL
DROP TABLE dbo.Table1
GO

CREATE TABLE dbo.Table1
(
    Table1ID int NOT NULL IDENTITY(1,1)
    , Table1Data nvarchar(30) NOT NULL
);

CREATE INDEX IX_Table1
ON dbo.Table1 (Table1ID);
GO

Chúng tôi sẽ chèn 1.000.000 hàng vào bảng để chúng tôi có một số dữ liệu để làm việc với:

INSERT INTO dbo.Table1 (Table1Data)
SELECT TOP(1000000) LEFT(c.name, 30)
FROM sys.columns c
    CROSS JOIN sys.columns c1
    CROSS JOIN sys.columns c2;
GO

Bây giờ, chúng tôi sẽ truy vấn dữ liệu với tùy chọn hiển thị kế hoạch thực hiện "thực tế":

SELECT *
FROM dbo.Table1
WHERE Table1ID = 500000;

Kế hoạch truy vấn hiển thị:

nhập mô tả hình ảnh ở đây

Truy vấn nhìn vào IX_Table1chỉ mục để tìm hàng Table1ID = 5000000vì nhìn vào chỉ mục đó nhanh hơn nhiều so với việc quét toàn bộ bảng tìm kiếm giá trị đó. Tuy nhiên, để đáp ứng kết quả truy vấn, bộ xử lý truy vấn cũng phải tìm giá trị cho các cột khác trong bảng; đây là nơi "Tra cứu RID" xuất hiện. Nó tìm trong bảng cho ID hàng (RID trong RID Tra cứu) được liên kết với hàng chứa Table1IDgiá trị 500000, lấy các giá trị từ Table1Datacột. Nếu bạn di chuột qua nút "Tra cứu RID" trong kế hoạch, bạn sẽ thấy điều này:

nhập mô tả hình ảnh ở đây

"Danh sách đầu ra" chứa các cột được trả về bởi Tra cứu RID.

Một bảng có chỉ mục được nhóm và chỉ mục không được nhóm làm cho một ví dụ thú vị. Bảng dưới đây có ba cột; ID là khóa phân cụm, Datđược lập chỉ mục bởi một chỉ mục không phân cụm IX_Tablevà cột thứ ba , Oth.

USE tempdb;

IF OBJECT_ID(N'dbo.Table1', N'U') IS NOT NULL
DROP TABLE dbo.Table1
GO

CREATE TABLE dbo.Table1
(
    ID int NOT NULL IDENTITY(1,1) 
        PRIMARY KEY CLUSTERED
    , Dat nvarchar(30) NOT NULL
    , Oth nvarchar(3) NOT NULL
);

CREATE INDEX IX_Table1
ON dbo.Table1 (Dat);
GO

INSERT INTO dbo.Table1 (Dat, Oth)
SELECT TOP(1000000) CRYPT_GEN_RANDOM(30), CRYPT_GEN_RANDOM(3)
FROM sys.columns c
    CROSS JOIN sys.columns c1
    CROSS JOIN sys.columns c2;
GO

Lấy ví dụ này truy vấn:

SELECT *
FROM dbo.Table1
WHERE Dat = 'Test';

Chúng tôi đang yêu cầu SQL Server trả về mỗi cột từ bảng Datchứa cột chứa từ đó Test. Chúng tôi có một vài lựa chọn ở đây; chúng ta có thể nhìn vào bảng (tức là chỉ mục được nhóm) - nhưng điều đó sẽ đòi hỏi phải quét toàn bộ mọi thứ vì bảng được sắp xếp theo IDcột, điều này cho chúng ta không biết gì về hàng nào chứa Testtrong Datcột. Tùy chọn khác (và được chọn bởi SQL Server) bao gồm tìm kiếm vào IX_Table1chỉ mục không được phân cụm để tìm hàng trong đó Dat = 'Test', tuy nhiên vì chúng tôi cũng cần Othcột, SQL Server phải thực hiện tra cứu vào chỉ mục được phân cụm bằng cách sử dụng "Khóa" Tra cứu "hoạt động. Đây là kế hoạch cho điều đó:

nhập mô tả hình ảnh ở đây

Nếu chúng ta sửa đổi các chỉ số phi clustered để nó bao gồm các Othcột:

DROP INDEX IX_Table1
ON dbo.Table1;
GO

CREATE INDEX IX_Table1
ON dbo.Table1 (Dat)
INCLUDE (Oth);        <---- This is the only change
GO

Sau đó chạy lại truy vấn:

SELECT *
FROM dbo.Table1
WHERE Dat = 'Test';

Bây giờ chúng ta thấy một chỉ mục không phân cụm tìm kiếm vì SQL Server chỉ cần xác định vị trí hàng Dat = 'Test'trong IX_Table1chỉ mục, bao gồm giá trị cho Othvà giá trị cho IDcột (khóa chính), tự động xuất hiện trong mọi không phải chỉ số cụm. Kế hoạch:

nhập mô tả hình ảnh ở đây


5

Tra cứu khóa được gây ra bởi vì công cụ đã chọn sử dụng một chỉ mục không chứa tất cả các cột bạn đang cố gắng tìm nạp. Vì vậy, chỉ mục không bao gồm các cột trong câu lệnh select và where.

Để loại bỏ tra cứu khóa, bạn cần bao gồm các cột bị thiếu (các cột trong danh sách đầu ra của tra cứu khóa) = ProducerContactGuid, quoteStatusID, PolicyTypeID và ProducerLocationID hoặc một cách khác là buộc truy vấn sử dụng chỉ mục được nhóm thay thế.

Lưu ý rằng 27 chỉ mục không được nhóm trên một bảng có thể không tốt cho hiệu suất. Khi chạy cập nhật, chèn hoặc xóa, SQL Server phải cập nhật tất cả các chỉ mục. Công việc làm thêm này có thể ảnh hưởng tiêu cực đến hiệu suất.


Cũng lưu ý rằng quá nhiều chỉ mục có thể gây nhầm lẫn cho việc biên dịch kế hoạch thực hiện và cũng có thể dẫn đến các lựa chọn tối ưu phụ.
Bỏ qua

4

Bạn đã quên đề cập đến khối lượng dữ liệu liên quan đến truy vấn này. Ngoài ra tại sao bạn chèn vào một bảng tạm thời? Nếu chỉ bạn cần hiển thị thì đừng chạy câu lệnh chèn.

Đối với mục đích của truy vấn này, tblQuoteskhông cần 27 chỉ mục không được nhóm. Nó cần 1 chỉ mục cụm và 5 chỉ mục không phân cụm hoặc, có lẽ là 6 chỉ mục không phân cụm.

Truy vấn này muốn lập chỉ mục trên các cột này:

QuoteStatusID
ProducerLocationID
ProducerContactGuid
EffectiveDate
LineGUID
OriginalQuoteGUID

Tôi cũng nhận thấy đoạn mã sau:

DATEDIFF(D, @EffDateFrom, tblQuotes.EffectiveDate) >= 0 AND 
DATEDIFF(D, @EffDateTo, tblQuotes.EffectiveDate) <= 0

NON Sargabletức là nó không thể sử dụng các chỉ số.

Để làm cho mã SARgableđó thay đổi nó thành này:

tblQuotes.EffectiveDate >= @EffDateFrom 
AND  tblQuotes.EffectiveDate <= @EffDateFrom

Để trả lời câu hỏi chính của bạn, "tại sao bạn nhận được một chìa khóa Tra cứu":

Bạn đang nhận được KEY Look upvì một số cột được đề cập trong truy vấn không có trong một chỉ mục bao trùm.

Bạn có thể google và nghiên cứu về Covering Indexhoặc Include index.

Trong ví dụ của tôi, giả sử tblQuotes.QuoteStatusID là chỉ mục Non Clustered thì tôi cũng có thể bao gồm DisplayStatus. Vì bạn muốn DisplayStatus trong resultset. Bất kỳ cột nào không có trong một chỉ mục và hiện diện trong resultset đều có thể được đề cập để tránh KEY Look Up or Bookmark lookup. Đây là một ví dụ bao gồm chỉ số:

create nonclustered index tblQuotes_QuoteStatusID 
on tblQuotes(QuoteStatusID)
include(DisplayStatus);

** Tuyên bố miễn trừ trách nhiệm: ** Hãy nhớ ở trên chỉ là ví dụ của tôi DisplayStatus có thể được bảo vệ bằng Non CI khác sau khi phân tích.

Tương tự, bạn sẽ phải tạo chỉ mục và bao gồm chỉ mục trên các bảng khác có liên quan đến truy vấn.

Bạn cũng nhận được Index SCANtrong kế hoạch của bạn.

Điều này có thể xảy ra do không có Chỉ mục trên bảng hoặc khi có khối lượng dữ liệu lớn, trình tối ưu hóa có thể quyết định quét thay vì thực hiện tìm kiếm chỉ mục.

Điều này cũng có thể xảy ra do High cardinality. Nhận số lượng hàng nhiều hơn yêu cầu do tham gia bị lỗi. Điều này cũng có thể được sửa chữa.

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.