Apakah operator spool bersemangat untuk menghapus ini dari toko kolom berkerumun?


28

Saya sedang menguji menghapus data dari indeks columnstore berkerumun.

Saya perhatikan bahwa ada operator spool bersemangat besar dalam rencana eksekusi:

masukkan deskripsi gambar di sini

Ini dilengkapi dengan karakteristik berikut:

  • 60 juta baris dihapus
  • 1.9 GiB TempDB digunakan
  • 14 menit waktu eksekusi
  • Rencana seri
  • 1 rebind di spool
  • Perkiraan biaya untuk pemindaian: 364.821

Jika saya memperdaya penaksir agar meremehkan, saya mendapatkan rencana yang lebih cepat yang menghindari penggunaan TempDB:

masukkan deskripsi gambar di sini

Perkiraan biaya pemindaian: 56.901

(Ini adalah rencana perkiraan, tetapi angka dalam komentar sudah benar.)

Menariknya, spool menghilang lagi jika saya menyiram toko delta dengan menjalankan yang berikut:

ALTER INDEX IX_Clustered ON Fact.RecordedMetricsDetail REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);

Spool tampaknya hanya diperkenalkan ketika ada lebih dari beberapa ambang batas halaman di toko delta.

Untuk memeriksa ukuran toko delta, saya menjalankan kueri berikut untuk memeriksa halaman dalam-baris untuk tabel:

SELECT  
  SUM([in_row_used_page_count]) AS in_row_used_pages,
  SUM(in_row_data_page_count) AS in_row_data_pages
FROM sys.[dm_db_partition_stats] as pstats
JOIN sys.partitions AS p
ON pstats.partition_id = p.partition_id
WHERE p.[object_id] = OBJECT_ID('Fact.RecordedMetricsDetail');

Apakah ada manfaat yang masuk akal bagi spool iterator dalam rencana pertama? Saya harus menganggap itu dimaksudkan sebagai peningkatan kinerja dan bukan untuk perlindungan halloween karena kehadirannya tidak konsisten.

Saya menguji ini pada 2016 CTP 3.1, tapi saya melihat perilaku yang sama pada 2014 SP1 CU3.

Saya telah memposting skrip yang menghasilkan skema dan data dan memandu Anda menunjukkan masalah di sini .

Pertanyaannya sebagian besar karena penasaran tentang perilaku pengoptimal pada saat ini karena saya punya solusi untuk masalah yang mendorong pertanyaan (kumparan diisi TempDB besar). Saya sekarang menghapus dengan menggunakan beralih partisi sebagai gantinya.


2
Jika saya mencoba OPTION (QUERYRULEOFF EnforceHPandAccCard)gulungan hilang. Saya menganggap HP mungkin "Halloween Protection". Namun kemudian mencoba menggunakan rencana itu dengan sebuah USE PLANpetunjuk gagal (seperti halnya mencoba menggunakan rencana dari OPTIMIZE FOR solusi juga)
Martin Smith

Terima kasih @MartinSmith. Ada yang tahu apa yang AccCardakan terjadi? Kardinalitas kolom naik kardinalitas mungkin?
James L

1
@ JamesLupolt Tidak, saya tidak bisa menemukan sesuatu yang meyakinkan saya. Mungkin Acc itu Akumulasi atau Akses?
Martin Smith

Jawaban:


22

Apakah ada manfaat yang masuk akal bagi spool iterator dalam rencana pertama?

Ini tergantung pada apa yang Anda anggap "masuk akal", tetapi jawabannya sesuai dengan model biaya adalah ya. Tentu saja ini benar, karena pengoptimal selalu memilih paket termurah yang ditemukannya.

Pertanyaan sebenarnya adalah mengapa model biaya menganggap paket dengan spul jauh lebih murah daripada tanpa paket. Pertimbangkan taksiran rencana yang dibuat untuk tabel baru (dari skrip Anda) sebelum baris ditambahkan ke delta store:

DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE);

Perkiraan biaya untuk rencana ini adalah 771.734 unit :

Rencana asli

Biaya hampir semuanya terkait dengan Penghapusan Indeks Berkelompok, karena penghapusan diharapkan menghasilkan banyak I / O acak. Ini hanya logika umum yang berlaku untuk semua modifikasi data. Sebagai contoh, serangkaian modifikasi yang tidak teratur pada indeks b-tree diasumsikan menghasilkan I / O yang sebagian besar acak, dengan biaya I / O yang tinggi.

Paket pengubah data dapat menampilkan Urut untuk menyajikan baris dalam urutan yang akan mempromosikan akses sekuensial, untuk alasan biaya ini. Dampaknya diperburuk dalam kasus ini karena tabel dipartisi. Faktanya sangat dipartisi; skrip Anda menciptakan 15.000 di antaranya. Pembaruan acak untuk tabel yang sangat dipartisi sangat mahal terutama karena harga untuk beralih partisi (rowsets) mid-stream diberikan biaya tinggi juga.

Faktor utama terakhir yang perlu dipertimbangkan adalah bahwa permintaan pembaruan sederhana di atas (di mana 'pembaruan' berarti operasi perubahan data, termasuk penghapusan) memenuhi syarat untuk optimasi yang disebut "berbagi rowset", di mana rowset internal yang sama digunakan untuk pemindaian dan memperbarui tabel. Rencana pelaksanaan masih menunjukkan dua operator yang terpisah, namun demikian, hanya ada satu rowset yang digunakan.

Saya menyebutkan ini karena dapat menerapkan optimasi ini berarti optimizer mengambil jalur kode yang sama sekali tidak mempertimbangkan manfaat potensial dari penyortiran secara eksplisit untuk mengurangi biaya I / O acak. Di mana tabel adalah b-tree, ini masuk akal, karena strukturnya secara inheren dipesan, sehingga berbagi rowset memberikan semua manfaat potensial secara otomatis.

Konsekuensi penting adalah bahwa logika penetapan biaya untuk operator pembaruan tidak mempertimbangkan manfaat pemesanan ini (mempromosikan I / O berurutan atau optimasi lainnya) di mana objek yang mendasarinya adalah penyimpanan kolom. Ini karena modifikasi penyimpanan kolom tidak dilakukan di tempat; mereka menggunakan toko delta. Oleh karena itu, model biaya mencerminkan perbedaan antara pembaruan shared-rowset pada b-tree versus toko kolom.

Namun demikian, dalam kasus khusus dari kolomstore (sangat!) Yang dipartisi, mungkin masih ada manfaat untuk pemesanan yang diawetkan, dalam melakukan semua pembaruan ke satu partisi sebelum pindah ke yang berikutnya mungkin masih menguntungkan dari sudut pandang I / O. .

Logika biaya standar digunakan kembali untuk penyimpanan kolom di sini, sehingga paket yang mempertahankan pemesanan partisi (meskipun tidak dipesan di setiap partisi) biayanya lebih rendah. Kita dapat melihat ini pada kueri pengujian dengan menggunakan flag jejak 2332 tidak berdokumen untuk memerlukan input yang diurutkan ke operator pembaruan. Ini menyetel DMLRequestSortproperti menjadi true pada pembaruan, dan memaksa optimizer untuk menghasilkan rencana yang menyediakan semua baris untuk satu partisi sebelum pindah ke yang berikutnya:

DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332);

Perkiraan biaya untuk rencana ini sangat jauh lebih rendah, yaitu 52.5174 unit:

DMLRequestSort = paket sebenarnya

Pengurangan dalam biaya ini semua karena perkiraan biaya I / O yang lebih rendah pada saat pembaruan. Spool yang diperkenalkan tidak melakukan fungsi yang berguna, kecuali ia dapat menjamin output dalam urutan partisi, seperti yang diminta oleh pembaruan dengan DMLRequestSort = true(pemindaian serial indeks penyimpanan kolom tidak dapat memberikan jaminan ini). Biaya spool itu sendiri dianggap relatif rendah, terutama dibandingkan dengan pengurangan (mungkin tidak realistis) dalam biaya pada pembaruan.

Keputusan tentang apakah memerlukan input yang dipesan untuk operator pembaruan dibuat sangat awal dalam optimasi kueri. Heuristik yang digunakan dalam keputusan ini tidak pernah didokumentasikan, tetapi dapat ditentukan melalui coba-coba. Tampaknya ukuran setiap toko delta adalah masukan untuk keputusan ini. Setelah dibuat, pilihannya permanen untuk kompilasi permintaan. Tidak ada USE PLANpetunjuk yang akan berhasil: target paket telah memerintahkan input ke pembaruan, atau tidak.

Ada cara lain untuk mendapatkan rencana berbiaya rendah untuk kueri ini tanpa secara artifisial membatasi perkiraan kardinalitas. Perkiraan yang cukup rendah untuk menghindari Spool mungkin akan mengakibatkan DMLRequestSort salah, menghasilkan perkiraan biaya rencana yang sangat tinggi karena I / O acak yang diharapkan. Alternatifnya adalah menggunakan jejak flag 8649 (paket paralel) bersamaan dengan 2332 (DMLRequestSort = true):

DELETE Fact.RecordedMetricsDetail
WHERE MeasurementTime < DATEADD(day,-1,GETUTCDATE())
OPTION (RECOMPILE, QUERYTRACEON 2332, QUERYTRACEON 8649);

Ini menghasilkan rencana yang menggunakan pemindaian paralel mode per-partisi per-partisi dan pertukaran Pengumpulan (penggabungan) pelestarian pesanan:

Memerintahkan Hapus

Tergantung pada efektivitas run-time pemesanan partisi pada perangkat keras Anda, ini mungkin melakukan yang terbaik dari ketiganya. Yang mengatakan, modifikasi besar bukan ide bagus di kolom toko, jadi ide partisi-switching hampir pasti lebih baik. Jika Anda dapat mengatasi waktu kompilasi yang lama dan pilihan rencana yang aneh sering terlihat dengan objek yang dipartisi - terutama ketika jumlah partisi besar.

Menggabungkan banyak fitur yang relatif baru, terutama di dekat batasnya, adalah cara yang bagus untuk mendapatkan rencana eksekusi yang buruk. Kedalaman dukungan pengoptimal cenderung meningkat dari waktu ke waktu, tetapi menggunakan 15.000 partisi toko kolom kemungkinan akan selalu berarti Anda hidup di masa yang menarik.

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.