Mengapa perlu waktu lebih lama untuk membuat indeks setelah ukuran kolom bertambah?

18

Vendor kami mengubah lebar kolom di hampir setiap kolom di seluruh basis data. Basis data sekitar 7TB, 9000+ tabel. Kami mencoba membuat indeks pada tabel yang memiliki 5,5 miliar baris. Sebelum peningkatan vendor, kami dapat membuat indeks dalam 2 jam. Sekarang butuh berhari-hari. Apa yang telah mereka lakukan adalah menambah ukuran varchar (xx) menjadi varchar (256). Jadi kebanyakan kolom dulunya adalah varchar (18) atau varchar (75), dll.

Pokoknya kunci utama terdiri dari 6 kolom dengan lebar gabungan adalah 126 karakter. Sekarang setelah upgrade, kunci utama adalah 1283 karakter yang melanggar batas SQL Server 900 karakter. Lebar seluruh kolom tabel berubah dari jumlah total varchar gabungan 1049 menjadi total jumlah varchar gabungan 4009.

Tidak ada peningkatan dalam data, tabel tidak memakan lebih banyak "ruang" daripada sebelum semua lebar kolom meningkat, tetapi kinerja untuk membuat sesuatu yang sederhana seperti indeks sekarang mengambil jumlah waktu yang tidak masuk akal.

Adakah yang bisa menjelaskan mengapa perlu waktu lebih lama untuk membuat dan mengindeks ketika satu-satunya hal yang dilakukan adalah meningkatkan ukuran kolom?

Indeks yang kami coba buat tidak berstatus karena pk adalah indeks berkerumun. Setelah beberapa upaya membuat indeks, kami menyerah. Saya pikir itu berjalan 4 atau 5 hari tanpa selesai.

Saya mencoba ini di lingkungan non-produksi dengan mengambil snapshot sistem file dan membawa database pada server yang lebih tenang.

Daniel Miller
sumber

Jawaban:

12

Remus telah membantu menunjukkan bahwa panjang maksimal VARCHARkolom berdampak pada perkiraan ukuran baris dan oleh karena itu pemberian memori yang disediakan SQL Server.

Saya mencoba melakukan sedikit penelitian lebih lanjut untuk memperluas bagian "dari ini pada hal kaskade" jawabannya. Saya tidak memiliki penjelasan yang lengkap atau singkat, tetapi inilah yang saya temukan.

Skrip repro

Saya membuat skrip lengkap yang menghasilkan set data palsu yang pembuatan indeksnya kira-kira 10 kali lebih lama pada mesin saya untuk VARCHAR(256)versi. Data yang digunakan adalah persis sama, tapi tabel pertama menggunakan panjang max sebenarnya 18, 75, 9, 15, 123, dan 5, sementara semua kolom menggunakan panjang maksimum 256pada tabel kedua.


Mengunci tabel asli

Di sini kita melihat bahwa kueri asli selesai dalam sekitar 20 detik dan pembacaan logis sama dengan ukuran tabel ~1.5GB(195K halaman, 8K per halaman).

-- CPU time = 37674 ms,  elapsed time = 19206 ms.
-- Table 'testVarchar'. Scan count 9, logical reads 194490, physical reads 0
CREATE CLUSTERED INDEX IX_testVarchar
ON dbo.testVarchar (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO


Mengunci tabel VARCHAR (256)

Untuk VARCHAR(256)tabel, kita melihat bahwa waktu yang berlalu telah meningkat secara dramatis.

Menariknya, baik waktu CPU maupun bacaan logis tidak bertambah. Ini masuk akal mengingat bahwa tabel memiliki data yang sama persis, tetapi tidak menjelaskan mengapa waktu yang berlalu jauh lebih lambat.

-- CPU time = 33212 ms,  elapsed time = 263134 ms.
-- Table 'testVarchar256'. Scan count 9, logical reads 194491
CREATE CLUSTERED INDEX IX_testVarchar256
ON dbo.testVarchar256 (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO


I / O dan tunggu statistik: asli

Jika kita menangkap sedikit lebih detail (menggunakan p_perfMon, prosedur yang saya tulis ), kita dapat melihat bahwa sebagian besar I / O dilakukan pada LOGfile. Kami melihat jumlah I / O yang relatif sederhana pada file aktual ROWS(file data utama), dan tipe tunggu utamanya adalah LATCH_EX, yang menunjukkan pertentangan halaman dalam memori.

Kita juga dapat melihat bahwa disk pemintalan saya berada di antara "buruk" dan "sangat buruk", menurut Paul Randal :)

masukkan deskripsi gambar di sini


I / O dan tunggu statistik: VARCHAR (256)

Untuk VARCHAR(256)versi, statistik I / O dan tunggu terlihat sangat berbeda! Di sini kita melihat peningkatan besar dalam I / O pada file data ( ROWS), dan waktu kios sekarang membuat Paul Randal hanya mengatakan "WOW!".

Tidak mengherankan bahwa tipe menunggu # 1 sekarang IO_COMPLETION. Tetapi mengapa begitu banyak I / O dihasilkan?

masukkan deskripsi gambar di sini


Paket permintaan aktual: VARCHAR (256)

Dari rencana kueri, kita dapat melihat bahwa Sortoperator memiliki tumpahan rekursif (sedalam 5 level!) Dalam VARCHAR(256)versi kueri. (Tidak ada tumpahan sama sekali dalam versi aslinya.)

masukkan deskripsi gambar di sini


Kemajuan permintaan langsung: VARCHAR (256)

Kita bisa menggunakan sys.dm_exec_query_profiles untuk melihat kemajuan permintaan langsung dalam SQL 2014+ . Dalam versi asli, keseluruhan Table Scandan Sortdiproses tanpa tumpahan ( spill_page_counttetap ada di 0seluruh).

VARCHAR(256)Namun dalam versi, kita dapat melihat bahwa tumpahan halaman dengan cepat menumpuk untuk Sortoperator. Ini adalah snapshot dari kemajuan permintaan tepat sebelum permintaan selesai. Data di sini dikumpulkan di semua utas.

masukkan deskripsi gambar di sini

Jika saya menggali masing-masing utas, saya melihat bahwa 2 utas menyelesaikan pengurutan dalam waktu sekitar 5 detik (keseluruhan @ 20 detik, setelah 15 detik dihabiskan untuk pemindaian tabel). Jika semua utas berkembang pada tingkat ini, VARCHAR(256)pembuatan indeks akan selesai dalam waktu yang hampir sama dengan tabel aslinya.

Namun, 6 utas yang tersisa berkembang dengan kecepatan yang jauh lebih lambat. Ini mungkin karena cara memori dialokasikan dan cara benang ditahan oleh I / O saat mereka menumpahkan data. Saya tidak tahu pasti.

masukkan deskripsi gambar di sini


Apa yang bisa kau lakukan?

Ada beberapa hal yang mungkin ingin Anda coba:

  • Bekerja dengan vendor untuk memutar kembali ke versi sebelumnya. Jika itu tidak memungkinkan, biarkan vendor yang Anda tidak senang dengan perubahan ini sehingga mereka dapat mempertimbangkan untuk mengembalikannya di rilis mendatang.
  • Saat menambahkan indeks Anda, pertimbangkan untuk menggunakan di OPTION (MAXDOP X)mana Xangka yang lebih rendah dari pengaturan tingkat server Anda saat ini. Ketika saya menggunakan OPTION (MAXDOP 2)set data spesifik ini di komputer saya, VARCHAR(256)versi selesai 25 seconds(dibandingkan dengan 3-4 menit dengan 8 utas!). Mungkin saja perilaku tumpah diperburuk oleh paralelisme yang lebih tinggi.
  • Jika ada investasi perangkat keras tambahan, buat profil I / O (kemungkinan kemacetan) pada sistem Anda dan pertimbangkan untuk menggunakan SSD untuk mengurangi latensi I / O yang ditimbulkan oleh tumpahan.


Bacaan lebih lanjut

Paul White memiliki posting blog yang bagus di internal jenis SQL Server yang mungkin menarik. Itu memang berbicara sedikit tentang tumpah, kemiringan utas, dan alokasi memori untuk jenis paralel.

Geoff Patterson
sumber
11

Tabel sortir antara akan diperkirakan secara berbeda antara kedua kasus. Ini akan menyebabkan permintaan hibah memori yang berbeda ( VARCHAR(256)akan lebih besar) dan kemungkinan hibah yang sebenarnya jauh lebih kecil, persen bijaksana, dibandingkan dengan permintaan 'ideal'. Saya kira ini mengarah ke tumpahan saat menyortir.

Menguji skrip dari Geoff (hanya pada baris 100rb) Saya dapat melihat dengan jelas perbedaan dalam mengurutkan ukuran baris yang diperkirakan (141B vs 789B). Dari ini pada hal kaskade.

Remus Rusanu
sumber
8
Saya yakin Paul akan membuktikan jawaban yang lebih menyeluruh dan lengkap, termasuk tumpukan panggilan, yang akan digunakan sebagai bahan pembelajaran oleh tim dev. Lagi ...
Remus Rusanu