Mengapa lebih (dan beragam jumlah) bacaan logis dengan baca di depan (prefetch)?

8

Setelah membuat database tpch di SQL Server saya, saya mencoba permintaan di bawah ini:

    set statistics io on
    DBCC DROPCLEANBUFFERS;        
    select top 100 * from dbo.lineitem order by l_partkey;

Tableitem tabel memiliki indeks non-clustered di l_partkey. Saya mengeluarkan pertanyaan di atas untuk beberapa kali dan menemukan bahwa pembacaan logis bervariasi setiap kali:

    Table 'lineitem'. Scan count 1, logical reads 1019, physical reads 4, read-ahead reads 1760, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'lineitem'. Scan count 1, logical reads 1007, physical reads 4, read-ahead reads 1720, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'lineitem'. Scan count 1, logical reads 1030, physical reads 4, read-ahead reads 1792, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Dari pos di sini: Hitungan baca logis bervariasi , saya tahu itu bisa disebabkan oleh perilaku baca depan.

TETAPI mengapa membaca ke depan dapat menyebabkan lebih banyak membaca logis? Bagaimana cara mengubah perilaku SQL Server? Seperti SQL Server dapat membaca lebih banyak halaman indeks karena itu masih dalam cache?

Lagi pula, saya menonaktifkan baca sebelumnya dan mengeluarkan pertanyaan di atas lagi. Sekarang ia melaporkan jumlah pembacaan logis yang sama setiap kali. TETAPI bacaan logisnya jauh lebih kecil !!

    Table 'lineitem'. Scan count 1, logical reads 404, physical reads 160, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Jadi pertanyaan saya adalah, mengapa fitur baca depan dapat menyebabkan lebih banyak dan beragam jumlah pembacaan logis?

Karena penasaran, saya mencoba kueri lain tanpa "pesanan oleh":

    select top 100 * from dbo.lineitem

Inilah hasilnya tanpa membaca dulu:

    Table 'lineitem'. Scan count 1, logical reads 5, physical reads 3, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Inilah hasilnya dengan membaca sebelumnya:

    Table 'lineitem'. Scan count 1, logical reads 15, physical reads 2, read-ahead reads 3416, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Yang dengan baca depan masih memiliki lebih banyak membaca logis. Jadi kenapa?

pengguna3610199
sumber

Jawaban:

9

Rencana kueri untuk ORDER BY l_partkeycontoh hampir pasti membaca indeks nonclustered secara berurutan (dengan baca-depan), diikuti oleh Pencarian Kunci untuk mengambil kolom yang tidak tertutup.

Operator Nested Loops Bergabung di atas Pencarian mungkin menggunakan prefetching tambahan ( WithOrderedPrefetch) untuk Pencarian. Lihat artikel berikut yang saya tulis untuk detail lengkap:

Juga, seperti yang saya sebutkan sebagai jawaban atas tanya jawab yang ditautkan, jumlah read-ahead reads tergantung pada karakteristik subsistem waktu dan penyimpanan. Jenis pertimbangan yang sama berlaku untuk prefetch Lookup di Nested Loops Join.

Perhatikan bahwa masalah SQL Server baca-depan untuk halaman yang mungkin diperlukan oleh pemindaian indeks, tetapi ini tidak dibatasi oleh TOPspesifikasi dalam kueri. Ini TOPadalah elemen prosesor kueri, sedangkan bacaan di depan dikendalikan oleh Storage Engine.

Kegiatannya cukup terpisah: baca-depan (dan prefetching) mengeluarkan I / O asinkron untuk halaman yang mungkin dibutuhkan oleh Pemindaian (atau Pencarian).

Bergantung pada urutan di mana I / O benar-benar menyelesaikan dan membuat baris tersedia untuk prosesor kueri (antara lain), jumlah halaman yang benar-benar disentuh (pembacaan logis) atau pembacaan fisik dapat bervariasi. Perhatikan khususnya bahwa prefetch Lookup yang tertunda juga berkontribusi pada pembacaan logis ketika memeriksa untuk melihat apakah halaman yang diperlukan untuk Lookup sudah ada dalam memori atau tidak.

Jadi, itu semua bermuara pada waktu terperinci dari operasi yang tumpang tindih: Prosesor permintaan akan mulai menutup pipa eksekusi permintaan segera setelah jumlah baris yang diperlukan (100) telah terlihat di Top iterator. Cukup banyak I / O asinkron (baca-depan atau prefetch) yang akan dikeluarkan atau diselesaikan pada saat itu pada dasarnya tidak deterministik.

Anda dapat menonaktifkan prefetching Nested Loops Join dengan trace flag 8744 untuk menjelajahi ini lebih lanjut. Ini akan menghapus WithOrderedPrefetchproperti dari Nested Loops Join. Saya biasanya menggunakan OPTION (QUERYTRACEON 8744)query itu sendiri. Dalam hal apa pun, Anda perlu memastikan bahwa Anda tidak menggunakan kembali paket cache yang memiliki prefetch. Bersihkan cache rencana setiap kali atau paksa kompilasi ulang kueri dengan OPTION (RECOMPILE).

Bacaan logis adalah ukuran sederhana dari jumlah halaman cache yang disentuh atas nama permintaan. Jika baca-depan (dan / atau pengambilan awal) diaktifkan, lebih banyak (dan berbeda!) Halaman data dan indeks dapat disentuh untuk menerbitkan baca-depan itu atau sebagai bagian dari kegiatan pengambilan sebelumnya.

Paul White 9
sumber