Eliminate Key Lookup operator (Clustered) yang memperlambat kinerja


16

Bagaimana saya bisa menghilangkan operator Pencarian Kunci (Clustered) dalam rencana eksekusi saya?

Tabel tblQuotessudah memiliki indeks berkerumun (on QuoteID) dan 27 indeks nonclustered, jadi saya mencoba untuk tidak membuat lagi.

Saya memasukkan kolom indeks berkerumun QuoteIDdi kueri saya, berharap itu akan membantu - tapi sayangnya masih sama.

Rencana eksekusi di sini .

Atau melihatnya:

masukkan deskripsi gambar di sini

Inilah yang dikatakan operator Pencarian Kunci:

masukkan deskripsi gambar di sini

Pertanyaan:

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

Rencana eksekusi:

masukkan deskripsi gambar di sini


Baris Estimasi vs Aktual menunjukkan perbedaan penting. Mungkin SQL memilih rencana yang buruk karena tidak memiliki data untuk membuat estimasi yang baik. Seberapa sering Anda memperbarui statistik Anda?
RDFozz

Jawaban:


23

Pencarian kunci dari berbagai rasa terjadi ketika prosesor kueri perlu mendapatkan nilai dari kolom yang tidak disimpan dalam indeks yang digunakan untuk menemukan baris yang diperlukan agar permintaan mengembalikan hasil.

Ambil contoh kode berikut, tempat kami membuat tabel dengan indeks tunggal:

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

Kami akan memasukkan 1.000.000 baris ke tabel sehingga kami memiliki beberapa data untuk dikerjakan:

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

Sekarang, kami akan meminta data dengan opsi untuk menampilkan rencana eksekusi "aktual":

SELECT *
FROM dbo.Table1
WHERE Table1ID = 500000;

Rencana kueri menunjukkan:

masukkan deskripsi gambar di sini

Kueri melihat IX_Table1indeks untuk menemukan baris dengan Table1ID = 5000000karena melihat indeks itu jauh lebih cepat daripada memindai seluruh tabel mencari nilai itu. Namun, untuk memenuhi hasil kueri, prosesor kueri juga harus menemukan nilai untuk kolom lain dalam tabel; ini adalah tempat "RID Lookup" masuk. Itu terlihat di tabel untuk ID baris (RID di RID Lookup) yang terkait dengan baris yang berisi Table1IDnilai 500000, memperoleh nilai-nilai dari Table1Datakolom. Jika Anda mengarahkan mouse ke simpul "RID Lookup" di paket, Anda melihat ini:

masukkan deskripsi gambar di sini

"Daftar Keluaran" berisi kolom yang dikembalikan oleh Pencarian RID.

Tabel dengan indeks berkerumun dan indeks non-berkerumun membuat contoh yang menarik. Tabel di bawah ini memiliki tiga kolom; ID yang merupakan kunci pengelompokan, Datyang diindeks oleh indeks yang tidak dikelompokkan IX_Table, dan kolom ketiga 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

Ambil kueri contoh ini:

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

Kami meminta SQL Server untuk mengembalikan setiap kolom dari tabel tempat Datkolom berisi kata Test. Kami punya beberapa pilihan di sini; kita dapat melihat tabel (yaitu indeks berkerumun) - tetapi itu akan memerlukan pemindaian seluruh hal karena tabel dipesan oleh IDkolom, yang tidak memberi tahu kita apa pun tentang baris yang berisi Testdalam Datkolom. Opsi lain (dan yang dipilih oleh SQL Server) terdiri dari mencari ke dalam IX_Table1indeks non-cluster untuk menemukan baris di mana Dat = 'Test', namun karena kita memerlukan Othkolom juga, SQL Server harus melakukan pencarian ke dalam indeks yang dikelompokkan menggunakan "Kunci". Operasi pencarian ". Ini adalah rencana untuk itu:

masukkan deskripsi gambar di sini

Jika kita memodifikasi indeks non-clustered sehingga termasuk yang Othkolom:

DROP INDEX IX_Table1
ON dbo.Table1;
GO

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

Kemudian jalankan kembali kueri:

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

Kita sekarang melihat pencarian indeks non-clustered tunggal karena SQL Server hanya perlu menemukan baris Dat = 'Test'di mana dalam IX_Table1indeks, yang termasuk nilai untuk Oth, dan nilai untuk IDkolom (kunci utama), yang secara otomatis hadir di setiap non-cluster . indeks berkerumun. Rencana:

masukkan deskripsi gambar di sini


6

Pencarian kunci disebabkan karena mesin memilih untuk menggunakan indeks yang tidak mengandung semua kolom yang Anda coba ambil. Jadi indeks tidak mencakup kolom pada pernyataan select and where.

Untuk menghilangkan pencarian kunci Anda harus menyertakan kolom yang hilang (kolom dalam daftar Output pencarian kunci) = ProducerContactGuid, QuoteStatusID, PolicyTypeID dan ProducerLocationID atau cara lain adalah dengan memaksa permintaan untuk menggunakan indeks cluster sebagai gantinya.

Perhatikan bahwa 27 indeks yang tidak berkerumun di atas meja mungkin buruk untuk kinerja. Saat menjalankan pembaruan, masukkan, atau hapus, SQL Server harus memperbarui semua indeks. Pekerjaan ekstra ini dapat memengaruhi kinerja secara negatif.


Perhatikan juga bahwa terlalu banyak indeks dapat mengacaukan penyusunan rencana eksekusi, dan dapat mengakibatkan pemilihan yang kurang optimal juga.
Lopsided

4

Anda lupa menyebutkan volume data yang terlibat dalam kueri ini. Juga mengapa Anda memasukkan ke tabel temp? Kalau saja Anda perlu menampilkan maka jangan menjalankan pernyataan menyisipkan.

Untuk keperluan kueri ini, tblQuotestidak perlu 27 indeks non-cluster. Dibutuhkan 1 indeks berkerumun dan 5 indeks tidak-berkerumun atau, mungkin 6 indekseks tidak-berkerumun.

Kueri ini ingin indeks pada kolom ini:

QuoteStatusID
ProducerLocationID
ProducerContactGuid
EffectiveDate
LineGUID
OriginalQuoteGUID

Saya juga memperhatikan kode berikut:

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

adalah NON Sargableyakni tidak dapat memanfaatkan indeks.

Untuk SARgablemengubahnya, ubah kode ini menjadi:

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

Untuk menjawab pertanyaan utama Anda, "mengapa Anda mendapatkan kunci Cari":

Anda mendapatkan KEY Look upkarena beberapa kolom yang disebutkan dalam kueri tidak ada dalam indeks penutup.

Anda dapat google dan belajar tentang Covering Indexatau Include index.

Dalam contoh saya anggap tblQuotes.QuoteStatusID adalah indeks Non Clustered maka saya juga dapat mencakup DisplayStatus. Karena Anda ingin DisplayStatus di Resultset. Kolom apa pun yang tidak ada dalam indeks dan ada di resultset dapat ditutup untuk menghindari KEY Look Up or Bookmark lookup. Ini adalah contoh yang meliputi indeks:

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

** Penafian: ** Ingat di atas hanya contoh saya DisplayStatus dapat ditutupi dengan Non CI lainnya setelah analisis.

Anda juga harus membuat indeks dan mencakup indeks pada tabel lain yang terlibat dalam kueri.

Anda mendapatkan Index SCANjuga dalam rencana Anda.

Ini dapat terjadi karena tidak ada Indeks di atas meja atau ketika ada volume data yang besar, pengoptimal dapat memutuskan untuk memindai daripada melakukan pencarian indeks.

Ini juga dapat terjadi karena High cardinality. Mendapatkan lebih banyak jumlah baris daripada yang dibutuhkan karena kesalahan bergabung. Ini juga bisa diperbaiki.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.