Indeks tidak membuat eksekusi lebih cepat, dan dalam beberapa kasus memperlambat permintaan. Kenapa gitu?

34

Saya sedang bereksperimen dengan indeks untuk mempercepat hal-hal, tetapi dalam kasus bergabung, indeks tidak meningkatkan waktu eksekusi permintaan dan dalam beberapa kasus memperlambat hal-hal.

Permintaan untuk membuat tabel uji dan mengisinya dengan data adalah:

CREATE TABLE [dbo].[IndexTestTable](
    [id] [int] IDENTITY(1,1) PRIMARY KEY,
    [Name] [nvarchar](20) NULL,
    [val1] [bigint] NULL,
    [val2] [bigint] NULL)

DECLARE @counter INT;
SET @counter = 1;

WHILE @counter < 500000
BEGIN
    INSERT INTO IndexTestTable
      (
        -- id -- this column value is auto-generated
        NAME,
        val1,
        val2
      )
    VALUES
      (
        'Name' + CAST((@counter % 100) AS NVARCHAR),
        RAND() * 10000,
        RAND() * 20000
      );

    SET @counter = @counter + 1;
END

-- Index in question
CREATE NONCLUSTERED INDEX [IndexA] ON [dbo].[IndexTestTable]
(
    [Name] ASC
)
INCLUDE (   [id],
    [val1],
    [val2])

Sekarang kueri 1, yang ditingkatkan (hanya sedikit tetapi peningkatannya konsisten) adalah:

SELECT *
FROM   IndexTestTable I1
       JOIN IndexTestTable I2
            ON  I1.ID = I2.ID
WHERE  I1.Name = 'Name1'

Stat dan rencana eksekusi tanpa Indeks (dalam hal ini tabel menggunakan indeks berkerumun standar):

(5000 row(s) affected)
Table 'IndexTestTable'. Scan count 2, logical reads 5580, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 109 ms,  elapsed time = 294 ms.

masukkan deskripsi gambar di sini

Sekarang dengan Indeks diaktifkan:

(5000 row(s) affected)
Table 'IndexTestTable'. Scan count 2, logical reads 2819, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 94 ms,  elapsed time = 231 ms.

masukkan deskripsi gambar di sini

Sekarang kueri yang melambat karena indeks (kueri tidak berarti karena dibuat hanya untuk pengujian):

SELECT I1.Name,
       SUM(I1.val1),
       SUM(I1.val2),
       MIN(I2.Name),
       SUM(I2.val1),
       SUM(I2.val2)
FROM   IndexTestTable I1
       JOIN IndexTestTable I2
            ON  I1.Name = I2.Name
WHERE   
       I2.Name = 'Name1'
GROUP BY
       I1.Name

Dengan diaktifkan indeks berkerumun:

(1 row(s) affected)
Table 'IndexTestTable'. Scan count 4, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 155106, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 17207 ms,  elapsed time = 17337 ms.

masukkan deskripsi gambar di sini

Sekarang dengan Indeks dinonaktifkan:

(1 row(s) affected)
Table 'IndexTestTable'. Scan count 5, logical reads 8642, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 165212, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 17691 ms,  elapsed time = 9073 ms.

masukkan deskripsi gambar di sini

Pertanyaannya adalah:

  1. Meskipun indeks disarankan oleh SQL Server, mengapa indeks memperlambat segalanya karena perbedaan yang signifikan?
  2. Apa yang bergabung dengan Nested Loop yang menghabiskan sebagian besar waktu dan cara meningkatkan waktu pelaksanaannya?
  3. Adakah sesuatu yang saya lakukan salah atau telah terlewatkan?
  4. Dengan indeks default (hanya pada kunci utama) mengapa diperlukan waktu lebih sedikit, dan dengan indeks non-cluster hadir, untuk setiap baris dalam tabel bergabung, baris tabel bergabung harus ditemukan lebih cepat, karena bergabung adalah pada kolom Nama di mana indeks telah dibuat. Ini tercermin dalam rencana eksekusi permintaan dan biaya Pencarian Indeks lebih rendah ketika IndexA aktif, tetapi mengapa masih lebih lambat? Juga apa yang ada di Nested Loop kiri luar yang menyebabkan perlambatan?

Menggunakan SQL Server 2012

SpeedBirdNine
sumber

Jawaban:

23

Meskipun indeks disarankan oleh SQL Server, mengapa indeks memperlambat segalanya karena perbedaan yang signifikan?

Saran indeks dibuat oleh pengoptimal permintaan. Jika ia menemukan pilihan logis dari tabel yang tidak terlayani dengan baik oleh indeks yang ada, itu dapat menambahkan saran "indeks hilang" ke outputnya. Saran-saran ini oportunistik; mereka tidak didasarkan pada analisis penuh dari permintaan, dan tidak memperhitungkan pertimbangan yang lebih luas. Paling-paling, mereka merupakan indikasi bahwa pengindeksan yang lebih bermanfaat mungkin dilakukan, dan DBA yang terampil harus melihatnya.

Hal lain untuk dikatakan tentang saran indeks yang hilang adalah bahwa mereka didasarkan pada model penetapan biaya pengoptimal, dan pengoptimal memperkirakan dengan seberapa banyak indeks yang disarankan dapat mengurangi perkiraan biaya kueri. Kata-kata kunci di sini adalah "model" dan "perkiraan". Pengoptimal kueri tahu sedikit tentang konfigurasi perangkat keras Anda atau opsi konfigurasi sistem lainnya - modelnya sebagian besar didasarkan pada angka tetap yang terjadi untuk menghasilkan hasil rencana yang wajar bagi kebanyakan orang di sebagian besar sistem hampir sepanjang waktu. Selain masalah dengan angka biaya persis yang digunakan, hasilnya selalu perkiraan - dan estimasi bisa salah.

Apa yang bergabung dengan Nested Loop yang menghabiskan sebagian besar waktu dan cara meningkatkan waktu pelaksanaannya?

Ada sedikit yang harus dilakukan untuk meningkatkan kinerja operasi join silang itu sendiri; nested loop adalah satu-satunya implementasi fisik yang memungkinkan untuk cross join. Spool tabel di sisi bagian dalam gabungan adalah pengoptimalan untuk menghindari pemindaian ulang sisi bagian dalam untuk setiap baris luar. Apakah ini optimasi kinerja yang bermanfaat tergantung pada berbagai faktor, tetapi dalam pengujian saya, pertanyaan lebih baik tanpa itu. Sekali lagi, ini adalah konsekuensi dari penggunaan model biaya - CPU dan sistem memori saya cenderung memiliki karakteristik kinerja yang berbeda dari milik Anda. Tidak ada petunjuk permintaan khusus untuk menghindari spool tabel, tetapi ada flag jejak tidak berdokumen (8690) yang dapat Anda gunakan untuk menguji kinerja eksekusi dengan dan tanpa spool. Jika ini adalah masalah sistem produksi nyata, rencana tanpa gulungan dapat dipaksa menggunakan panduan rencana berdasarkan pada rencana yang dihasilkan dengan TF 8690 diaktifkan. Menggunakan flag jejak yang tidak berdokumen dalam produksi tidak disarankan karena instalasi menjadi tidak didukung secara teknis dan flag jejak dapat memiliki efek samping yang tidak diinginkan.

Adakah sesuatu yang saya lakukan salah atau telah terlewatkan?

Hal utama yang Anda lewatkan adalah bahwa meskipun rencana menggunakan indeks nonclustered memiliki perkiraan biaya yang lebih rendah sesuai dengan model pengoptimal, itu memiliki masalah waktu eksekusi yang signifikan. Jika Anda melihat distribusi baris di utas dalam paket menggunakan Clustered Index, Anda mungkin akan melihat distribusi yang cukup baik:

Rencana pemindaian

Dalam rencana menggunakan Nonclustered Index Seek, pekerjaan berakhir sepenuhnya oleh satu utas:

Carilah rencana

Ini adalah konsekuensi dari cara kerja didistribusikan di antara utas dengan operasi pemindaian / pencarian paralel. Ini tidak selalu merupakan kasus dimana pemindaian paralel akan mendistribusikan pekerjaan lebih baik daripada pencarian indeks - tetapi hal ini terjadi dalam kasus ini. Rencana yang lebih kompleks mungkin termasuk pertukaran partisi ulang untuk mendistribusikan kembali pekerjaan lintas utas. Paket ini tidak memiliki pertukaran seperti itu, jadi setelah baris diberikan ke utas, semua pekerjaan terkait dilakukan pada utas yang sama. Jika Anda melihat distribusi pekerjaan untuk operator lain dalam rencana eksekusi, Anda akan melihat bahwa semua pekerjaan dilakukan oleh utas yang sama seperti yang ditunjukkan untuk pencarian indeks.

Tidak ada petunjuk kueri untuk memengaruhi distribusi baris di antara utas, yang penting adalah mewaspadai kemungkinan dan untuk dapat membaca cukup detail dalam rencana eksekusi untuk menentukan kapan itu menyebabkan masalah.

Dengan indeks default (hanya pada kunci utama) mengapa diperlukan waktu lebih sedikit, dan dengan indeks non-cluster hadir, untuk setiap baris dalam tabel bergabung, baris tabel bergabung harus ditemukan lebih cepat, karena bergabung adalah pada kolom Nama di mana indeks telah dibuat. Ini tercermin dalam rencana eksekusi permintaan dan biaya Pencarian Indeks lebih rendah ketika IndexA aktif, tetapi mengapa masih lebih lambat? Juga apa yang ada di Nested Loop kiri luar yang menyebabkan perlambatan?

Seharusnya sekarang menjadi jelas bahwa rencana indeks nonclustered berpotensi lebih efisien, seperti yang Anda harapkan; itu hanya distribusi pekerjaan yang buruk di utas pada waktu pelaksanaan yang menyebabkan masalah kinerja.

Demi melengkapi contoh dan menggambarkan beberapa hal yang telah saya sebutkan, salah satu cara untuk mendapatkan distribusi kerja yang lebih baik adalah dengan menggunakan tabel sementara untuk mendorong eksekusi paralel:

SELECT
    val1,
    val2
INTO #Temp
FROM dbo.IndexTestTable AS ITT
WHERE Name = N'Name1';

SELECT 
    N'Name1',
    SUM(T.val1),
    SUM(T.val2),
    MIN(I2.Name),
    SUM(I2.val1),
    SUM(I2.val2)
FROM   #Temp AS T
CROSS JOIN IndexTestTable I2
WHERE
    I2.Name = 'Name1'
OPTION (FORCE ORDER, QUERYTRACEON 8690);

DROP TABLE #Temp;

Ini menghasilkan rencana yang menggunakan pencarian indeks yang lebih efisien, tidak menampilkan spool tabel, dan mendistribusikan pekerjaan di seluruh thread dengan baik:

Rencana optimal

Di sistem saya, paket ini dieksekusi lebih cepat secara signifikan daripada versi Pemindaian Indeks Berkelompok.

Jika Anda tertarik untuk mempelajari lebih lanjut tentang internal dari eksekusi query paralel, Anda mungkin ingin menonton rekaman sesi PASS Summit 2013 saya .

Paul White mengatakan GoFundMonica
sumber
0

Ini sebenarnya bukan pertanyaan tentang indeks, ini lebih merupakan permintaan yang ditulis dengan buruk. Anda hanya memiliki 100 nilai nama unik, ini menyisakan hitungan unik 5.000 per nama.

Jadi untuk setiap baris dalam tabel 1 Anda bergabung dengan 5000 dari tabel 2. Dapatkah Anda mengatakan 25020004 baris.

Coba ini, perhatikan ini hanya dengan 1 indeks, yang Anda daftarkan.

    DECLARE @Distincts INT
    SET @Distincts = (SELECT  TOP 1 COUNT(*) FROM IndexTestTable I1 WHERE I1.Name = 'Name1' GROUP BY I1.Name)
    SELECT I1.Name
    , @Distincts
    , SUM(I1.val1) * @Distincts
    , SUM(I1.val2) * @Distincts
    , MIN(I2.Name)
    , SUM(I2.val1)
    , SUM(I2.val2)
    FROM   IndexTestTable I1
    LEFT OUTER JOIN

    (
        SELECT I2.Name
        , SUM(I2.val1) val1
        , SUM(I2.val2) val2
        FROM IndexTestTable I2
        GROUP BY I2.Name
    ) I2 ON  I1.Name = I2.Name
    WHERE I1.Name = 'Name1'
    GROUP BY  I1.Name

Dan waktu:

    SQL Server parse and compile time: 
       CPU time = 0 ms, elapsed time = 8 ms.
    Table 'IndexTestTable'. Scan count 1, logical reads 31, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

     SQL Server Execution Times:
       CPU time = 0 ms,  elapsed time = 1 ms.

    (1 row(s) affected)
    Table 'IndexTestTable'. Scan count 2, logical reads 62, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

     SQL Server Execution Times:
       CPU time = 16 ms,  elapsed time = 10 ms.

masukkan deskripsi gambar di sini

Anda tidak bisa menyalahkan indeks SQL untuk kueri yang dibentuk dengan buruk

Adrian Sullivan
sumber
1
Terima kasih atas jawabannya, dan ya kueri dapat ditingkatkan, tetapi logika pertanyaan saya adalah bahwa dengan indeks default (hanya pada kunci primer) mengapa diperlukan waktu lebih sedikit, dan dengan indeks hadir non-cluster, untuk setiap baris di tabel bergabung, baris tabel bergabung harus ditemukan lebih cepat, yang tercermin dalam rencana eksekusi permintaan dan biaya Pencarian Indeks lebih rendah ketika IndexA aktif, tetapi mengapa masih lebih lambat? Juga apa yang ada di Nested Loop kiri luar yang menyebabkan perlambatan? Saya telah mengedit pertanyaan untuk menambahkan komentar ini, untuk membuat pertanyaan lebih jelas.