กำจัดตัวดำเนินการค้นหาคีย์ (เป็นกลุ่ม) ที่ทำให้ประสิทธิภาพการทำงานช้าลง


16

ฉันจะกำจัดตัวดำเนินการค้นหาคีย์ (เป็นกลุ่ม) ในแผนปฏิบัติการได้อย่างไร

ตารางtblQuotesมีดัชนีแบบคลัสเตอร์ (เปิดQuoteID) และดัชนีที่ไม่ได้คลัสเตอร์27 รายการดังนั้นฉันจึงพยายามไม่สร้างอีกต่อไป

ฉันวางคอลัมน์ดัชนีแบบคลัสเตอร์QuoteIDในแบบสอบถามของฉันโดยหวังว่าจะช่วยได้ แต่ก็ยังเหมือนเดิม

แผนการดำเนินการที่นี่

หรือดูมัน:

ป้อนคำอธิบายรูปภาพที่นี่

นี่คือสิ่งที่ผู้ดำเนินการค้นหาคีย์พูดว่า:

ป้อนคำอธิบายรูปภาพที่นี่

ค้นหา:

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

แผนการดำเนินการ:

ป้อนคำอธิบายรูปภาพที่นี่


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

คำตอบ:


23

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

ยกตัวอย่างโค้ดต่อไปนี้ซึ่งเรากำลังสร้างตารางด้วยดัชนีเดี่ยว:

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

เราจะแทรก 1,000,000 แถวลงในตารางเพื่อให้เรามีข้อมูลที่จะทำงานกับ:

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

ตอนนี้เราจะค้นหาข้อมูลพร้อมตัวเลือกเพื่อแสดงแผนการดำเนินการ "จริง":

SELECT *
FROM dbo.Table1
WHERE Table1ID = 500000;

แผนแบบสอบถามแสดง:

ป้อนคำอธิบายรูปภาพที่นี่

แบบสอบถามจะค้นหาIX_Table1ดัชนีเพื่อค้นหาแถวด้วยTable1ID = 5000000เนื่องจากการดูดัชนีนั้นเร็วกว่าการสแกนทั้งตารางเพื่อค้นหาค่านั้น อย่างไรก็ตามเพื่อให้เป็นไปตามผลลัพธ์ของแบบสอบถามตัวประมวลผลแบบสอบถามจะต้องค้นหาค่าสำหรับคอลัมน์อื่น ๆ ในตารางเช่นกัน นี่คือที่ "การค้นหา RID" เข้ามามันจะดูในตารางสำหรับ ID แถว (RID ในการค้นหา RID) ที่เกี่ยวข้องกับแถวที่มีTable1IDค่า 500000 โดยรับค่าจากTable1Dataคอลัมน์ หากคุณวางเมาส์ไว้เหนือโหนด "การค้นหา RID" ในแผนคุณจะเห็นสิ่งนี้:

ป้อนคำอธิบายรูปภาพที่นี่

"รายการผลลัพธ์" มีคอลัมน์ที่ส่งคืนโดย RID Lookup

ตารางที่มีดัชนีคลัสเตอร์และดัชนีที่ไม่ได้ทำคลัสเตอร์เป็นตัวอย่างที่น่าสนใจ ตารางด้านล่างมีสามคอลัมน์ ID ซึ่งเป็นกุญแจสำคัญในการจัดกลุ่มDatซึ่งเป็นดัชนีโดยดัชนีไม่มีคลัสเตอร์และคอลัมน์ที่สามIX_TableOth

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

ใช้แบบสอบถามตัวอย่างนี้:

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

เรากำลังขอให้ SQL Server เพื่อกลับทุกคอลัมน์จากตารางที่คอลัมน์มีคำว่าDat Testเรามีทางเลือกสองทางที่นี่; เราสามารถดูตาราง (เช่นดัชนีคลัสเตอร์) - แต่นั่นจะเกี่ยวข้องกับการสแกนทุกสิ่งตั้งแต่ตารางถูกเรียงลำดับตามIDคอลัมน์ซึ่งไม่ได้บอกเราเกี่ยวกับแถวใดที่มีTestอยู่ในDatคอลัมน์ ตัวเลือกอื่น ๆ (และตัวเลือกที่เลือกโดย SQL Server) ประกอบด้วยการค้นหาในIX_Table1ดัชนีที่ไม่ใช่คลัสเตอร์เพื่อค้นหาแถวที่Dat = 'Test'อย่างไรก็ตามเนื่องจากเราต้องการOthคอลัมน์เช่นกัน SQL Server ต้องทำการค้นหาในดัชนีคลัสเตอร์โดยใช้ "คีย์ ค้นหา "การดำเนินการ นี่เป็นแผนสำหรับ:

ป้อนคำอธิบายรูปภาพที่นี่

ถ้าเราปรับเปลี่ยนดัชนีที่ไม่ใช่คลัสเตอร์เพื่อที่จะรวมถึงOthคอลัมน์:

DROP INDEX IX_Table1
ON dbo.Table1;
GO

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

จากนั้นเรียกใช้แบบสอบถามอีกครั้ง:

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

ตอนนี้เราเห็นการค้นหาดัชนีที่ไม่ใช่คลัสเตอร์เดียวเนื่องจาก SQL Server เพียงต้องการค้นหาแถวที่Dat = 'Test'อยู่ในIX_Table1ดัชนีซึ่งรวมถึงค่าOthและค่าสำหรับIDคอลัมน์ (คีย์หลัก) ซึ่งจะปรากฏโดยอัตโนมัติในทุก ๆ ดัชนีคลัสเตอร์ แผนการ:

ป้อนคำอธิบายรูปภาพที่นี่


6

การค้นหาคีย์เกิดขึ้นเนื่องจากเอ็นจินเลือกใช้ดัชนีที่ไม่มีคอลัมน์ทั้งหมดที่คุณพยายามดึงข้อมูล ดังนั้นดัชนีไม่ครอบคลุมคอลัมน์ในคำสั่ง select และ where

ในการกำจัดการค้นหาคีย์คุณต้องรวมคอลัมน์ที่หายไป (คอลัมน์ในรายการผลลัพธ์ของการค้นหาคีย์) = ProducerContactGuid, QuoteStatusID, PolicyTypeID และ ProducerLocationID หรือวิธีอื่นคือบังคับให้เคียวรีใช้ดัชนีคลัสเตอร์แทน

โปรดทราบว่า 27 ดัชนีที่ไม่ใช่คลัสเตอร์บนตารางอาจไม่ดีต่อประสิทธิภาพ เมื่อเรียกใช้การปรับปรุงแทรกหรือลบ SQL Server ต้องปรับปรุงดัชนีทั้งหมด งานพิเศษนี้อาจส่งผลเสียต่อประสิทธิภาพการทำงาน


นอกจากนี้โปรดทราบว่าดัชนีมากเกินไปสามารถสร้างความสับสนในการรวบรวมแผนการดำเนินการและอาจส่งผลให้การเลือกย่อยที่เหมาะสมที่สุดเช่นกัน
ลำเอียง

4

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

สำหรับวัตถุประสงค์ของแบบสอบถามtblQuotesนี้ไม่จำเป็นต้องมี 27 ดัชนีที่ไม่ทำคลัสเตอร์ จำเป็นต้องมีดัชนี 1 คลัสเตอร์และดัชนีที่ไม่ได้ทำคลัสเตอร์ 5 รายการหรืออาจเป็นดัชนีที่ไม่ใช่คลัสเตอร์ 6 รายการ

แบบสอบถามนี้ต้องการดัชนีในคอลัมน์เหล่านี้:

QuoteStatusID
ProducerLocationID
ProducerContactGuid
EffectiveDate
LineGUID
OriginalQuoteGUID

ฉันยังสังเกตเห็นรหัสต่อไปนี้:

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

คือNON Sargableไม่สามารถใช้ดัชนี

ในการทำให้โค้ดSARgableนั้นเปลี่ยนเป็น:

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

เพื่อตอบคำถามหลักของคุณ "ทำไมคุณถึงได้รับการค้นหาที่สำคัญ":

คุณได้รับKEY Look upเนื่องจากบางคอลัมน์ที่กล่าวถึงในแบบสอบถามไม่ปรากฏในดัชนีครอบคลุม

คุณสามารถ google และการศึกษาเกี่ยวกับหรือCovering IndexInclude index

ในตัวอย่างของฉันสมมติว่า tblQuotes.QuoteStatusID เป็นดัชนีที่ไม่เป็นคลัสเตอร์ดังนั้นฉันยังสามารถครอบคลุม DisplayStatus เนื่องจากคุณต้องการ DisplayStatus ใน Resultset คอลัมน์ใด ๆ ที่ไม่อยู่ในดัชนีและเป็นปัจจุบันใน resultset KEY Look Up or Bookmark lookupสามารถครอบคลุมเพื่อหลีกเลี่ยง นี่คือตัวอย่างที่ครอบคลุมดัชนี:

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

** ข้อสงวนสิทธิ์: ** โปรดจำไว้ว่าข้างต้นเป็นเพียงตัวอย่างของฉัน DisplayStatus อาจถูกปกคลุมด้วย Non CI อื่น ๆ หลังจากการวิเคราะห์

ในทำนองเดียวกันคุณจะต้องสร้างดัชนีและครอบคลุมดัชนีในตารางอื่น ๆ ที่เกี่ยวข้องในแบบสอบถาม

คุณได้รับIndex SCANในแผนของคุณด้วย

สิ่งนี้อาจเกิดขึ้นเนื่องจากไม่มีดัชนีบนโต๊ะหรือเมื่อมีข้อมูลจำนวนมากที่เครื่องมือเพิ่มประสิทธิภาพอาจตัดสินใจสแกนแทนที่จะทำการค้นหาดัชนี

High cardinalityนอกจากนี้ยังสามารถเกิดขึ้นได้เนื่องจาก รับจำนวนแถวมากกว่าที่ต้องการเนื่องจากการเข้าร่วมที่ผิดพลาด สิ่งนี้สามารถแก้ไขได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.