Saya memiliki tabel berikut dengan 7,5 juta catatan:
CREATE TABLE [dbo].[TestTable](
[Id] [int] IDENTITY(1,1) NOT NULL,
[TestCol] [nvarchar](50) NOT NULL,
[TestCol2] [nvarchar](50) NOT NULL,
[TestCol3] [nvarchar](50) NOT NULL,
[Anonymised] [tinyint] NOT NULL,
[Date] [datetime] NOT NULL,
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Saya perhatikan bahwa ketika ada indeks non-cluster di bidang tanggal:
CREATE NONCLUSTERED INDEX IX_TestTable_Date ON [dbo].[TestTable] ([Date])
-dan saya menjalankan kueri berikut:
UPDATE TestTable
SET TestCol='*GDPR*', TestCol2='*GDPR*', TestCol3='*GDPR*', Anonymised=1
WHERE [Date] <= '25 August 2016'
- data yang dikembalikan oleh operasi akses indeks diurutkan agar sesuai dengan urutan utama PK / CX, mengurangi kinerja.
Saya terkejut menemukan bahwa menghapus indeks dari bidang tanggal sebenarnya meningkatkan kinerja kueri sekitar 30% karena tidak lagi melakukan pengurutan:
Teori saya, dan ini mungkin jelas bagi yang lebih berpengalaman di antara Anda, adalah telah menemukan bahwa kolom tanggal secara tersirat dipesan persis sama dengan kunci utama / indeks berkerumun.
Jadi pertanyaan saya adalah: Apakah mungkin untuk mengambil keuntungan dari fakta ini untuk meningkatkan kinerja permintaan saya?
sumber
[Date]
tetapi dalamDESC
urutan? Penasaran saja karena predikatnya adalah<=
. Juga, jika indeks aktifDate
(dalamACS
urutan default ) membantu pertanyaan lain, maka mungkin Anda dapat mencoba menambahkan petunjuk tabel ke UPDATE untuk memaksanya menggunakan PK? Atau, mungkin pilah ini menjadi dua bagian: membuat tabel temp, mengisi dengan[Id]
berdasarkan[Date] <= '25 August 2016'
, dan kemudian menghapusWHERE
dari UPDATE dan menambahkanFROM dbo.TestTable tt INNER JOIN #tmp ids ON ids.[Id] = tt.[Id]
. Bagaimanapun, ini adalah PEMBARUAN, dan perlu menemukan baris aktual, indeks atau tidak.Jawaban:
Saya membuat data uji yang sebagian besar mereproduksi masalah Anda:
Statistik untuk kueri yang menggunakan indeks nonclustered:
Statistik untuk kueri yang menggunakan indeks berkerumun:
Mendapatkan pertanyaan Anda:
Iya. Anda dapat menggunakan indeks nonclustered yang sudah Anda miliki untuk secara efisien menemukan nilai maksimum
id
yang perlu diperbarui. Jika Anda menyimpannya ke variabel dan menyaringnya, Anda akan mendapatkan paket permintaan pembaruan yang melakukan pemindaian indeks berkerumun (tanpa pengurutan) yang berhenti lebih awal dan karenanya mengurangi IO. Inilah satu implementasi:Jalankan statistik untuk kueri baru:
Serta rencana kueri:
Dengan semua itu, keinginan Anda untuk membuat kueri lebih cepat menunjukkan kepada saya bahwa Anda berencana untuk menjalankan kueri lebih dari satu kali. Saat ini permintaan Anda memiliki filter ujung terbuka pada
date
kolom. Apakah benar-benar perlu menganonimkan baris lebih dari satu kali? Bisakah Anda menghindari memperbarui atau memindai baris yang sudah dianonimkan? Tentunya akan lebih cepat untuk memperbarui berbagai tanggal dengan tanggal di kedua sisi itu. Anda juga bisa menambahkanAnonymised
kolom ke indeks Anda, tetapi indeks itu perlu diperbarui selamaUPDATE
permintaan Anda . Singkatnya, hindari memproses data yang sama berulang kali jika Anda bisa.Kueri asli yang Anda miliki dengan pengurutan lebih lambat karena pekerjaan yang dilakukan di
Clustered Index Update
operator. Jumlah waktu yang dihabiskan untuk pencarian indeks dan jenisnya hanya 407 ms. Anda dapat melihat ini dalam rencana aktual. Paket dijalankan dalam mode baris sehingga waktu yang dihabiskan untuk pengurutan adalah waktu dari operator itu bersama dengan setiap operator anak:Itu membuat operator sortir sekitar 1600 ms waktu. SQL Server perlu membaca halaman dari indeks berkerumun untuk melakukan pembaruan. Anda dapat melihat bahwa
Clustered Index Update
operator melakukan 1205921 pembacaan logis. Anda dapat membaca lebih lanjut tentang pengurutan optimasi untuk DML dan dioptimalkan pengambilan di posting blog ini oleh Paul White .Paket kueri lain yang Anda miliki (tanpa pengurutan) membutuhkan 683 ms untuk pemindaian indeks berkelompok dan sekitar 550 ms untuk
Clustered Index Update
operator. Operator pembaruan tidak melakukan IO apa pun untuk kueri ini.Jawaban sederhana mengapa rencana dengan pengurutan lebih lambat adalah bahwa SQL Server membaca lebih logis pada indeks berkerumun untuk rencana itu dibandingkan dengan rencana pemindaian indeks berkerumun. Bahkan jika semua data yang dibutuhkan ada di memori, masih ada overhead dan biaya untuk melakukan pembacaan logis. Jawaban yang lebih baik jauh lebih sulit didapat, sejauh yang saya tahu rencana tidak akan memberi Anda rincian lebih lanjut. Dimungkinkan untuk menggunakan PerfView atau alat lain berdasarkan pelacakan ETW untuk membandingkan tumpukan panggilan antara permintaan:
Di sebelah kiri adalah permintaan yang melakukan pemindaian indeks berkerumun dan di sebelah kanan adalah permintaan yang melakukan pengurutan. Saya menandai tumpukan panggilan dengan warna biru atau merah yang hanya muncul dalam satu permintaan. Tidak mengherankan, tumpukan panggilan yang berbeda dengan jumlah besar siklus CPU sampel untuk permintaan pengurutan tampaknya berkaitan dengan pembacaan logis yang diperlukan untuk melakukan pembaruan pada indeks berkerumun. Selain itu, ada perbedaan dalam jumlah siklus sampel antara permintaan untuk operasi yang sama. Untuk sampel, kueri dengan pengurutan menghabiskan 31 siklus memperoleh kait sedangkan permintaan dengan pemindaian hanya menghabiskan 9 siklus memperoleh kait.
Saya menduga bahwa SQL Server memilih rencana yang lebih lambat karena keterbatasan biaya rencana operator paket. Mungkin bagian dari perbedaan waktu berjalan adalah karena perangkat keras atau edisi SQL Server Anda. Dalam kasus apa pun, SQL Server tidak dapat mengetahui bahwa kolom tanggal secara implisit dipesan persis sama dengan indeks berkerumun. Data dikembalikan dari pemindaian indeks berkerumun dalam urutan kunci berkerumun, sehingga tidak perlu melakukan pengurutan dalam upaya untuk mengoptimalkan IO ketika melakukan pembaruan indeks berkerumun.
sumber