Seri lima bagian ini membahas secara mendalam cara memulai rencana paralel mode baris SQL Server. Bagian pertama ini mencakup peran tugas induk (koordinator) dalam mempersiapkan rencana untuk eksekusi paralel. Ini termasuk menginisialisasi setiap operator, dan menambahkan profiler tersembunyi untuk mengumpulkan data kinerja waktu proses seperti jumlah baris aktual dan waktu yang telah berlalu.
Penyiapan
Untuk memberikan dasar yang konkret untuk analisis, kami akan mengikuti bagaimana kueri paralel tertentu memulai eksekusi. Saya menggunakan Stack Overflow 2013 public publik basis data (detail unduhan). Bentuk denah yang diinginkan juga dapat diperoleh dengan kumpulan data Stack Overflow 2010 yang lebih kecil jika itu lebih nyaman. Itu dapat diunduh di tautan yang sama. Saya menambahkan satu indeks nonclustered:
CREATE NONCLUSTERED INDEX PP ON dbo.Posts ( PostTypeId ASC, CreationDate ASC );
Lingkungan pengujian saya adalah SQL Server 2019 CU9 pada laptop dengan 8 core dan memori 16 GB yang dialokasikan untuk instans. Tingkat kompatibilitas 150 digunakan secara eksklusif. Saya menyebutkan detail itu untuk membantu Anda mereproduksi rencana target jika Anda mau. Dasar-dasar eksekusi paralel mode baris tidak berubah sejak SQL Server 2005, jadi diskusi berikut dapat diterapkan secara luas.
Kueri tes mengembalikan jumlah total pertanyaan dan jawaban, yang dikelompokkan berdasarkan bulan dan tahun:
WITH MonthlyPosts AS ( SELECT P.PostTypeId, CA.TheYear, CA.TheMonth, Latest = MAX(P.CreationDate) FROM dbo.Posts AS P CROSS APPLY ( VALUES ( YEAR(P.CreationDate), MONTH(P.CreationDate) ) ) AS CA (TheYear, TheMonth) GROUP BY P.PostTypeId, CA.TheYear, CA.TheMonth ) SELECT rn = ROW_NUMBER() OVER ( ORDER BY Q.TheYear, Q.TheMonth), Q.TheYear, Q.TheMonth, LatestQuestion = Q.Latest, LatestAnswer = A.Latest FROM MonthlyPosts AS Q JOIN MonthlyPosts AS A ON A.TheYear = Q.TheYear AND A.TheMonth = Q.TheMonth WHERE Q.PostTypeId = 1 AND A.PostTypeId = 2 ORDER BY Q.TheYear, Q.TheMonth OPTION ( USE HINT ('DISALLOW_BATCH_MODE'), USE HINT ('FORCE_DEFAULT_CARDINALITY_ESTIMATION'), ORDER GROUP, MAXDOP 2 );
Saya telah menggunakan petunjuk untuk mendapatkan rencana mode baris bentuk tertentu. Eksekusi terbatas pada DOP 2 untuk membuat beberapa detail yang ditampilkan nanti lebih ringkas.
Perkiraan rencana eksekusi adalah (klik untuk memperbesar):
Latar Belakang
Pengoptimal kueri menghasilkan satu paket terkompilasi untuk satu batch. Setiap pernyataan dalam batch ditandai untuk eksekusi serial atau paralel, tergantung pada kelayakan dan perkiraan biaya.
Paket paralel berisi pertukaran (operator paralelisme). Pertukaran mungkin muncul di aliran distribusi , aliran partisi ulang , atau kumpulkan aliran membentuk. Masing-masing jenis pertukaran ini menggunakan komponen dasar yang sama, hanya dihubungkan secara berbeda, dengan jumlah input dan output yang berbeda. Untuk latar belakang lebih lanjut tentang eksekusi paralel mode baris, lihat Rencana Eksekusi Paralel – Cabang dan Utas.
Penurunan versi DOP
Tingkat paralelisme (DOP) untuk rencana paralel dapat diturunkan pada saat runtime jika perlu. Kueri paralel mungkin mulai meminta DOP 8, tetapi secara bertahap diturunkan ke DOP 4, DOP 2, dan akhirnya DOP 1 karena kurangnya sumber daya sistem pada saat itu. Jika Anda ingin melihatnya beraksi, lihat video singkat ini oleh Erik Darling.
Menjalankan paket paralel pada satu utas juga dapat terjadi saat paket paralel yang di-cache digunakan kembali oleh sesi yang dibatasi ke DOP 1 oleh pengaturan lingkungan (mis. topeng afinitas atau gubernur sumber daya). Lihat Mitos:SQL Server Men-cache Paket Serial dengan setiap Paket Paralel untuk detailnya.
Apa pun penyebabnya, penurunan versi DOP dari paket paralel yang di-cache tidak menghasilkan rencana serial baru sedang dikompilasi. SQL Server menggunakan kembali paket paralel yang ada dengan menonaktifkan pertukaran. Hasilnya adalah rencana 'paralel' yang dijalankan pada satu utas. Pertukaran masih muncul dalam rencana, tetapi dilewati saat runtime.
SQL Server tidak dapat mempromosikan paket serial ke eksekusi paralel dengan menambahkan pertukaran saat runtime. Itu akan membutuhkan kompilasi baru.
Inisialisasi Paket Paralel
Paket paralel memiliki semua pertukaran yang diperlukan untuk memanfaatkan utas pekerja tambahan, tetapi ada pekerjaan penyiapan tambahan diperlukan saat runtime sebelum eksekusi paralel dapat dimulai. Salah satu contoh yang jelas adalah bahwa utas pekerja tambahan harus dialokasikan untuk tugas-tugas tertentu dalam rencana, tetapi ada lebih dari itu.
Saya akan mulai pada titik di mana paket paralel telah diambil dari cache paket. Pada titik ini, hanya utas asli yang memproses permintaan saat ini yang ada. Utas ini terkadang disebut "utas koordinator" dalam rencana paralel, tetapi saya lebih suka istilah "tugas induk ” atau “pekerja orang tua”. Jika tidak, tidak ada yang istimewa dari utas ini; ini adalah utas yang sama yang digunakan koneksi untuk memproses permintaan klien dan menjalankan rencana serial hingga selesai.
Untuk menekankan bahwa hanya ada satu utas sekarang, saya ingin Anda memvisualisasikan rencananya pada saat ini seperti ini:
Saya akan menggunakan screenshot dari Sentry One Plan Explorer hampir secara eksklusif di posting ini, tetapi untuk tampilan pertama ini saja, saya juga akan menampilkan versi SSMS:
Dalam kedua representasi, perbedaan utama adalah kurangnya ikon paralelisme pada setiap operator, meskipun pertukaran masih ada. Hanya tugas induk yang ada sekarang, berjalan di utas koneksi asli. Tidak ada utas pekerja tambahan telah dipesan, dibuat, atau diberi tugas. Pertahankan representasi rencana di atas di depan pikiran saat kita melanjutkan.
Membuat paket yang dapat dieksekusi
Rencana pada saat ini pada dasarnya hanyalah sebuah templat yang dapat digunakan sebagai dasar untuk eksekusi di masa depan. Untuk menyiapkannya untuk menjalankan tertentu, SQL Server perlu mengisi nilai runtime seperti pengguna saat ini, konteks transaksi, nilai parameter, id untuk objek apa pun yang dibuat saat runtime (mis. tabel dan variabel sementara), dan seterusnya.
Untuk paket paralel, SQL Server perlu melakukan sedikit pekerjaan persiapan tambahan untuk mendapatkan mesin internal ke titik di mana eksekusi dapat dimulai. Utas pekerja tugas induk bertanggung jawab untuk melakukan hampir semua pekerjaan ini (dan tentu saja semua pekerjaan yang akan kita bahas di bagian 1).
Proses mengubah template rencana untuk menjalankan tertentu dikenal sebagai membuat rencana yang dapat dieksekusi . Kadang-kadang sulit untuk menjaga terminologi tetap lurus, karena istilah sering kelebihan beban dan salah diterapkan (bahkan oleh Microsoft), tetapi saya akan melakukan yang terbaik untuk menjadi sekonsisten mungkin.
Konteks eksekusi
Anda dapat memikirkan konteks eksekusi sebagai templat rencana yang diisi dengan semua informasi runtime spesifik yang dibutuhkan oleh utas tertentu. Rencana yang dapat dieksekusi untuk serial pernyataan terdiri dari konteks eksekusi tunggal, di mana satu utas menjalankan seluruh rencana.
Sebuah paralel rencana yang dapat dieksekusi berisi kumpulan konteks eksekusi :Satu untuk tugas induk, dan satu per utas di setiap cabang paralel. Setiap utas pekerja paralel tambahan menjalankan bagiannya dari keseluruhan rencana dalam konteks eksekusinya sendiri. Misalnya, rencana paralel dengan tiga cabang yang berjalan pada DOP 8 memiliki (1 + (3 * 8)) =25 konteks eksekusi. Konteks eksekusi serial di-cache untuk digunakan kembali, tetapi konteks eksekusi paralel tambahan tidak.
Tugas induk selalu ada sebelum tugas paralel tambahan, sehingga ditugaskan eksekusi konteks nol . Konteks eksekusi yang digunakan oleh pekerja paralel akan dibuat nanti, setelah konteks induk sepenuhnya diinisialisasi. Konteks tambahan dikloning dari konteks induk, lalu disesuaikan untuk tugas khusus mereka (ini dibahas di bagian 2).
Ada sejumlah aktivitas yang terlibat dalam memulai eksekusi konteks nol. Tidak praktis untuk mencoba membuat daftar semuanya, tetapi akan berguna untuk mencakup beberapa yang utama yang berlaku untuk kueri pengujian kami. Masih akan ada terlalu banyak untuk satu daftar, jadi saya akan membaginya menjadi beberapa bagian (agak arbitrer):
1. Inisialisasi konteks induk
Saat kami mengirimkan pernyataan untuk dieksekusi, konteks tugas induk (konteks eksekusi nol) diinisialisasi dengan:
- Referensi ke transaksi dasar (eksplisit, implisit, atau komit otomatis). Pekerja paralel akan menjalankan sub-transaksi, tetapi semuanya tercakup dalam transaksi dasar.
- Daftar pernyataan parameter dan nilainya saat ini.
- Sebuah objek memori utama (PMO) digunakan untuk mengelola pemberian dan alokasi memori.
- Sebuah peta tertaut operator (node kueri) dalam paket yang dapat dieksekusi.
- Pabrik untuk benda besar apa pun yang diperlukan (gumpalan) menangani.
- Kunci kelas untuk melacak beberapa kunci yang ditahan selama satu periode selama eksekusi. Tidak semua paket memerlukan kelas kunci karena operator streaming penuh biasanya mengunci dan membuka kunci baris individual secara berurutan.
- Perkiraan hibah memori untuk kueri.
- Memori mode baris memberikan umpan balik struktur untuk setiap operator (SQL Server 2019).
Banyak dari hal-hal ini akan digunakan atau direferensikan oleh tugas paralel nanti, jadi mereka harus ada di lingkup induk terlebih dahulu.
2. Metadata konteks induk
Tugas utama selanjutnya yang dilakukan adalah:
- Memeriksa perkiraan biaya kueri berada dalam batas yang ditetapkan oleh opsi konfigurasi batas biaya gubernur kueri.
- Memperbarui penggunaan indeks catatan – diekspos melalui
sys.dm_db_index_usage_stats
. - Membuat nilai ekspresi yang di-cache (konstanta runtime).
- Membuat daftar pembuat profil operator digunakan untuk mengumpulkan metrik waktu proses seperti jumlah baris dan pengaturan waktu, jika ini telah diminta untuk eksekusi saat ini. Profiler itu sendiri belum dibuat, hanya daftarnya.
- Mengambil cuplikan menunggu untuk fitur tunggu sesi diekspos melalui
sys.dm_exec_session_wait_stats
.
3. DOP dan hibah memori
Konteks tugas induk sekarang:
- Menghitung derajat paralelisme runtime (DOP ). Ini dipengaruhi oleh jumlah pekerja gratis (lihat “DOP downgrade” sebelumnya), di mana mereka dapat ditempatkan di antara node, dan sejumlah flag trace.
- Menyimpan jumlah utas yang diperlukan. Langkah ini adalah penghitungan murni – utas itu sendiri mungkin tidak ada pada saat ini. SQL Server melacak jumlah maksimum utas yang diizinkan untuk dimiliki. Memesan rangkaian pesan dikurangi dari angka tersebut. Ketika utas selesai, jumlah maksimum ditingkatkan lagi.
- Menetapkan waktu tunggu pemberian memori .
- Menghitung pemberian memori, termasuk memori yang diperlukan untuk buffer pertukaran.
- Memperoleh hibah memori melalui semaphore sumber daya yang sesuai .
- Membuat objek pengelola untuk menangani subproses parallel paralel . Tugas induk adalah proses tingkat atas; tugas tambahan juga dikenal sebagai subproses .
Sementara utas 'dicadangkan' pada saat ini, SQL Server mungkin masih menemukan THREADPOOL
menunggu nanti ketika mencoba menggunakan salah satu utas yang 'dipesan'. Reservasi menjamin SQL Server akan tetap berada di sekitar jumlah utas maksimum yang dikonfigurasi setiap saat, tetapi utas fisik mungkin tidak segera tersedia dari kumpulan utas . Ketika itu terjadi, utas baru perlu dimulai oleh sistem operasi, yang dapat memakan waktu cukup lama. Untuk lebih lanjut tentang itu, lihat THREADPOOL Menunggu yang Tidak Biasa oleh Josh Darnell.
4. Penyiapan pemindaian kueri
Paket mode baris dijalankan dalam berulang mode, mulai dari akarnya. Rencana yang kami miliki saat ini belum mampu melakukan mode eksekusi ini. Sebagian besar masih berupa template, meskipun sudah berisi cukup banyak informasi khusus eksekusi.
SQL Server perlu mengubah struktur saat ini menjadi pohon iterator , masing-masing dengan metode seperti Open
, GetRow
, dan Close
. Metode iterator terhubung ke anak-anak mereka melalui pointer fungsi. Misalnya, memanggil GetRow
pada root secara rekursif memanggil GetRow
pada operator anak sampai level daun tercapai dan baris mulai 'menggelembung' pohon. Untuk penyegaran tentang detailnya, lihat Iterator, Paket Kueri, dan Mengapa Mereka Berjalan Mundur.
Akhir Bagian 1
Kami telah membuat kemajuan yang baik dalam menyiapkan konteks eksekusi untuk tugas induk. Di bagian 2, kita akan mengikuti saat SQL Server membuat pohon pemindaian kueri yang diperlukan untuk eksekusi berulang.