Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Pemindaian Urutan Alokasi

Saat rencana eksekusi menyertakan pemindaian struktur indeks b-tree, mesin penyimpanan mungkin dapat memilih antara dua strategi akses fisik saat rencana dijalankan:

  1. Ikuti struktur pohon-b indeks; atau,
  2. menemukan halaman menggunakan informasi alokasi halaman internal.

Jika pilihan tersedia, mesin penyimpanan membuat keputusan runtime pada setiap eksekusi. Kompilasi ulang rencana tidak diperlukan untuk mengubah pikirannya.

Strategi b-tree dimulai dari akar pohon, turun ke tepi ekstrim tingkat daun (tergantung pada apakah pemindaian maju atau mundur), kemudian mengikuti tautan halaman tingkat daun sampai ujung indeks tercapai. . Strategi alokasi menggunakan struktur Index Allocation Map (IAM) untuk menemukan halaman database yang dialokasikan ke indeks. Setiap halaman IAM memetakan alokasi ke interval 4 GB dalam satu file database fisik, jadi pemindaian rantai IAM yang terkait dengan indeks cenderung mengakses halaman indeks dalam urutan file fisik (setidaknya sejauh yang dapat diketahui SQL Server).

Perbedaan utama antara kedua strategi tersebut adalah:

  1. Pemindaian b-tree dapat mengirimkan baris ke prosesor kueri dalam urutan kunci indeks; pemindaian yang digerakkan oleh IAM tidak dapat;
  2. pemindaian b-tree mungkin tidak dapat mengeluarkan permintaan I/O baca-depan yang besar jika halaman indeks yang bersebelahan secara logis tidak juga bersebelahan secara fisik (misalnya sebagai akibat dari pemisahan halaman dalam indeks).

Pemindaian b-tree selalu tersedia untuk indeks. Kondisi yang sering dikutip agar pemindaian urutan alokasi tersedia adalah:

  1. Rencana kueri harus memungkinkan pemindaian indeks yang tidak berurutan;
  2. indeks harus berukuran minimal 64 halaman; dan,
  3. salah satu TABLOCK atau NOLOCK petunjuk harus ditentukan.

Kondisi pertama berarti bahwa pengoptimal kueri harus menandai pemindaian dengan Ordered:False Properti. Menandai pindaian Ordered:False berarti bahwa hasil yang benar dari rencana eksekusi tidak memerlukan scan untuk mengembalikan baris dalam urutan kunci indeks (meskipun mungkin melakukannya jika nyaman atau diperlukan).

Kondisi kedua (ukuran) hanya berlaku untuk SQL Server 2005 dan yang lebih baru. Ini mencerminkan fakta bahwa ada biaya awal tertentu untuk melakukan pemindaian yang digerakkan oleh IAM, jadi perlu ada jumlah halaman minimum untuk potensi penghematan untuk membayar investasi awal. “64 halaman” mengacu pada nilai data_pages untuk IN_ROW_DATA unit alokasi saja, seperti yang dilaporkan di sys.allocation_units.

Tentu saja, hanya ada hasil dari pemindaian urutan alokasi jika pertimbangan baca-depan yang mungkin lebih besar sebenarnya ikut bermain, tetapi SQL Server saat ini tidak mempertimbangkan faktor ini. Secara khusus, ini tidak memperhitungkan berapa banyak indeks yang saat ini ada di memori, juga tidak peduli seberapa terfragmentasi indeksnya.

Kondisi ketiga mungkin merupakan deskripsi yang paling tidak lengkap dalam daftar. Petunjuk sebenarnya tidak diperlukan , meskipun dapat digunakan untuk memenuhi persyaratan yang sebenarnya:Data harus dijamin tidak akan berubah selama pemindaian, atau (lebih kontroversial) kami harus menunjukkan bahwa kami tidak peduli tentang hasil yang berpotensi tidak akurat, dengan melakukan pemindaian pada tingkat isolasi read uncommited.

Bahkan dengan klarifikasi ini, daftar kondisi untuk pemindaian berdasarkan alokasi masih belum lengkap. Ada sejumlah peringatan dan pengecualian penting, yang akan segera kita bahas.

Demo

Kueri berikut menggunakan database sampel AdventureWorks:

CHECKPOINT;
DBCC DROPCLEANBUFFERS;
GO
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P;

Perhatikan bahwa tabel Person berisi 3.869 halaman. Rencana pasca-eksekusi (aktual) adalah sebagai berikut (ditampilkan di SQL Sentry Plan Explorer):

Dalam hal persyaratan pemindaian urutan alokasi yang kami miliki sejauh ini:

  • Paket memiliki Ordered:False yang diperlukan Properti; dan,
  • tabel memiliki lebih dari 64 halaman; tetapi,
  • kami tidak melakukan apa pun untuk memastikan data tidak dapat berubah selama pemindaian. Dengan asumsi sesi kami menggunakan default baca komitmen tingkat isolasi, pemindaian tidak dilakukan pada baca tanpa komitmen tingkat isolasi juga.

Akibatnya, kami berharap pemindaian ini dilakukan dengan memindai b-tree daripada didorong oleh IAM. Hasil kueri menunjukkan bahwa ini mungkin benar:

Baris dikembalikan dalam urutan kunci Indeks Cluster (oleh BusinessEntityID ). Saya harus menyatakan dengan jelas bahwa pemesanan hasil ini sama sekali tidak dijamin , dan tidak harus diandalkan. Hasil yang dipesan hanya dijamin oleh ORDER BY tingkat atas yang sesuai klausa.

Namun demikian, urutan keluaran yang diamati adalah bukti tidak langsung bahwa pemindaian dilakukan kali ini dengan mengikuti struktur pohon-b indeks berkerumun. Jika diperlukan lebih banyak bukti, kami dapat melampirkan debugger dan melihat jalur kode yang dijalankan SQL Server selama pemindaian:

Tumpukan panggilan dengan jelas menunjukkan pemindaian mengikuti b-tree.

Menambahkan petunjuk kunci tabel

Kami sekarang memodifikasi kueri untuk menyertakan petunjuk kunci tabel:

CHECKPOINT;
DBCC DROPCLEANBUFFERS;
 
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P
    WITH (TABLOCK);

Pada tingkat isolasi baca berkomitmen penguncian default, kunci tingkat tabel bersama mencegah kemungkinan modifikasi data secara bersamaan. Dengan ketiga prasyarat untuk pemindaian berbasis IAM terpenuhi, kami sekarang mengharapkan SQL Server untuk menggunakan pemindaian urutan alokasi. Rencana eksekusinya sama seperti sebelumnya, jadi saya tidak akan mengulanginya, tetapi hasil kuerinya pasti terlihat berbeda:

Hasilnya tampaknya masih diurutkan oleh BusinessEntityID , tetapi titik awalnya (10866) berbeda. Memang, jika kami menggulir ke bawah hasil, kami segera menemukan bagian yang lebih jelas dari urutan utama:

Pengurutan sebagian disebabkan oleh pemindaian urutan alokasi yang memproses seluruh halaman indeks sekaligus. Hasil dalam satu halaman kebetulan dikembalikan dipesan oleh kunci indeks, tetapi urutan halaman yang dipindai sekarang berbeda. Sekali lagi, saya harus menekankan bahwa hasilnya mungkin terlihat berbeda untuk Anda:tidak ada jaminan urutan keluaran, bahkan dalam satu halaman, tanpa ORDER BY tingkat atas pada kueri asli.

Sebagai perbandingan dengan tumpukan panggilan yang ditampilkan sebelumnya, ini adalah jejak tumpukan yang diperoleh saat SQL Server memproses kueri dengan TABLOCK petunjuk:

Melangkah sedikit lebih jauh melalui eksekusi:

Jelas, SQL Server melakukan pemindaian yang diurutkan alokasi ketika kunci tabel ditentukan. Sayang sekali tidak ada indikasi dalam rencana pasca-eksekusi tentang jenis pemindaian yang digunakan saat runtime. Sebagai pengingat, jenis pemindaian dipilih oleh mesin penyimpanan, dan dapat berubah di antara eksekusi tanpa kompilasi ulang rencana.

Cara lain untuk memenuhi kondisi ketiga

Saya katakan sebelumnya bahwa untuk mendapatkan pemindaian berbasis IAM, kita perlu memastikan data tidak dapat berubah di bawah pemindaian saat sedang berlangsung, atau kita perlu menjalankan kueri pada tingkat isolasi baca yang tidak terikat. Kami telah melihat bahwa petunjuk kunci tabel untuk mengunci isolasi baca yang dilakukan sudah cukup untuk memenuhi persyaratan pertama, dan mudah untuk menunjukkan bahwa menggunakan NOLOCK/READUNCOMMITTED petunjuk juga memungkinkan pemindaian urutan alokasi dengan kueri demo.

Sebenarnya ada banyak cara untuk memenuhi syarat ketiga, di antaranya:

  • Mengubah indeks untuk hanya mengizinkan kunci tabel;
  • membuat database menjadi read-only (sehingga data dijamin tidak akan berubah); atau,
  • mengubah sesi tingkat isolasi ke READ UNCOMMITTED .

Namun, ada variasi yang jauh lebih menarik pada tema ini yang berarti kita perlu mengubah tiga kondisi yang disebutkan sebelumnya…

Tingkat isolasi versi baris

Aktifkan isolasi snapshot berkomitmen baca (RCSI) pada database AdventureWorks, dan jalankan pengujian dengan TABLOCK petunjuk lagi (saat membaca isolasi berkomitmen):

ALTER DATABASE AdventureWorks2012
SET READ_COMMITTED_SNAPSHOT ON
    WITH ROLLBACK IMMEDIATE;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
CHECKPOINT;
DBCC DROPCLEANBUFFERS;
GO
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P
    WITH (TABLOCK);
GO
ALTER DATABASE AdventureWorks2012
SET READ_COMMITTED_SNAPSHOT OFF
    WITH ROLLBACK IMMEDIATE;

Dengan RCSI aktif, diurutkan berdasarkan indeks scan digunakan dengan TABLOCK , bukan pemindaian urutan alokasi yang kita lihat sebelumnya. Alasannya adalah TABLOCK petunjuk menentukan kunci bersama tingkat tabel, tetapi dengan RCSI diaktifkan, tidak ada kunci bersama diambil. Tanpa kunci tabel bersama, kami belum memenuhi persyaratan untuk mencegah modifikasi bersamaan pada data saat pemindaian sedang berlangsung, sehingga pemindaian berdasarkan alokasi tidak dapat digunakan.

Namun, mencapai pemindaian yang diurutkan berdasarkan alokasi saat RCSI diaktifkan. Salah satu caranya adalah dengan menggunakan TABLOCKX petunjuk (untuk level tabel eksklusif lock) bukannya TABLOCK . Kami juga dapat mempertahankan TABLOCK petunjuk dan tambahkan satu lagi seperti READCOMMITTEDLOCK , atau REPEATABLE READ atau SERIALIZABLE … dan seterusnya. Semua ini bekerja dengan mencegah kemungkinan modifikasi bersamaan dengan mengambil kunci tabel bersama, dengan biaya kehilangan manfaat RCSI . Kami juga masih dapat mencapai pemindaian urutan alokasi menggunakan NOLOCK atau READUNCOMMITTED petunjuk, tentu saja.

Situasi di bawah isolasi snapshot (SI) sangat mirip dengan RCSI, dan tidak dieksplorasi secara detail karena alasan ruang.

TABLESAMPLE selalu* melakukan pemindaian urutan alokasi

TABLESAMPLE klausa adalah pengecualian yang menarik untuk banyak hal yang telah kita diskusikan sejauh ini.

Menentukan TABLESAMPLE klausa selalu* menghasilkan pemindaian urutan alokasi, bahkan di bawah RCSI atau SI, dan bahkan tanpa petunjuk. Untuk memperjelasnya, pemindaian urutan alokasi yang dihasilkan dari penggunaan TABLESAMPLE mempertahankan semantik RCSI/SI – pemindaian menggunakan versi baris dan pembacaan tidak menghalangi penulisan (dan sebaliknya).

Kejutan kedua adalah TABLESAMPLE selalu* melakukan pemindaian berbasis IAM meskipun tabel memiliki kurang dari 64 halaman . Ini masuk akal karena dokumentasi setidaknya mengisyaratkan bahwa SYSTEM metode pengambilan sampel menggunakan struktur IAM (jadi tidak ada pilihan selain melakukan pemindaian urutan alokasi):

SISTEM Adalah metode pengambilan sampel yang bergantung pada implementasi yang ditentukan oleh standar ISO. Di SQL Server, ini adalah satu-satunya metode pengambilan sampel yang tersedia dan diterapkan secara default. SISTEM menerapkan metode pengambilan sampel berbasis halaman di mana kumpulan halaman acak dari tabel dipilih untuk sampel, dan semua baris pada halaman tersebut dikembalikan sebagai subkumpulan sampel.

* Pengecualian terjadi jika ROWS atau PERCENT spesifikasi di TABLESAMPLE klausa berhasil berarti 100% dari tabel. Menentukan lebih banyak ROWS dari yang ditunjukkan metadata saat ini dalam tabel juga tidak akan berfungsi. Menggunakan TABLESAMPLE SYSTEM (100 PERCENT) atau yang setara akan tidak memaksa pemindaian urutan alokasi.

CHECKPOINT;
DBCC DROPCLEANBUFFERS;
GO
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P
    TABLESAMPLE SYSTEM (50 ROWS)
    REPEATABLE (12345678)
    --WITH (TABLOCK);

Hasil:

Efek TOP dan SET ROWCOUNT

Singkatnya, tak satu pun dari ini berpengaruh pada keputusan untuk menggunakan pemindaian urutan alokasi atau tidak. Ini mungkin tampak mengejutkan jika "jelas" bahwa kurang dari 64 halaman akan dipindai.

Misalnya, kueri berikut menggunakan pemindaian berbasis IAM untuk mengembalikan 5 baris dari pemindaian:

SELECT TOP (5)
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P WITH (TABLOCK)
 
SET ROWCOUNT 5;
 
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P WITH (TABLOCK)
 
SET ROWCOUNT 0;

Hasilnya sama untuk keduanya:

Artinya TOP dan SET ROWCOUNT kueri mungkin mengeluarkan biaya tambahan untuk menyiapkan pemindaian urutan alokasi, meskipun kurang dari 64 halaman yang dipindai. Dalam mitigasi, kueri TOP yang lebih kompleks dengan predikat selektif yang didorong ke dalam pemindaian masih dapat mengambil manfaat dari pemindaian urutan alokasi. Jika pemindaian harus memproses 10.000 halaman untuk menemukan 5 baris pertama yang cocok, pemindaian urutan alokasi masih bisa menjadi kemenangan.

Mencegah semua* pemindaian urutan alokasi di seluruh instance

Ini bukan sesuatu yang mungkin Anda lakukan dengan sengaja, tetapi ada pengaturan server yang akan mencegah pemindaian urutan alokasi untuk semua* kueri pengguna di semua database.

Tampaknya tidak mungkin, setelan yang dimaksud adalah opsi konfigurasi server ambang kursor, yang memiliki deskripsi berikut di Buku Daring:

Opsi ambang kursor menentukan jumlah baris dalam kumpulan kursor di mana kumpulan tombol kursor dihasilkan secara asinkron. Saat kursor menghasilkan keyset untuk kumpulan hasil, pengoptimal kueri memperkirakan jumlah baris yang akan dikembalikan untuk kumpulan hasil tersebut. Jika pengoptimal kueri memperkirakan bahwa jumlah baris yang dikembalikan lebih besar dari ambang batas ini, kursor dibuat secara asinkron, memungkinkan pengguna untuk mengambil baris dari kursor sementara kursor terus diisi. Jika tidak, kursor dibuat secara sinkron, dan kueri menunggu hingga semua baris dikembalikan.

Jika cursor threshold opsi disetel ke apa pun selain -1 (default), tidak ada pemindaian urutan alokasi yang akan terjadi untuk kueri pengguna di database mana pun pada instance SQL Server.

Dengan kata lain, jika populasi kursor asinkron diaktifkan, tidak ada pemindaian berbasis IAM untuk Anda.

* Pengecualiannya adalah (non-100%) TABLESAMPLE pertanyaan. Kueri internal yang dihasilkan oleh sistem untuk pembuatan statistik dan pembaruan statistik juga terus menggunakan pemindaian berdasarkan alokasi.

CHECKPOINT;
DBCC DROPCLEANBUFFERS;
GO
 
-- WARNING! Disables allocation-order scans instance-wide
EXECUTE sys.sp_configure 
    @configname = 'cursor threshold',
    @configvalue = 5000;
 
RECONFIGURE WITH OVERRIDE;
GO
 
-- Would normally result in an allocation-order scan
SELECT
    P.BusinessEntityID,
    P.PersonType
FROM Person.Person AS P
    WITH (READUNCOMMITTED);
GO
 
-- Reset to default allocation-order scans
EXECUTE sys.sp_configure 
    @configname = 'cursor threshold',
    @configvalue = -1;
 
RECONFIGURE WITH OVERRIDE;

Hasil (tidak ada pemindaian urutan alokasi):

Orang hanya bisa menebak bahwa populasi kursor asinkron tidak bekerja dengan baik dengan pemindaian urutan alokasi karena alasan tertentu. Sama sekali tidak terduga bahwa pembatasan ini akan memengaruhi semua kueri pengguna non-kursor juga meskipun. Mungkin terlalu sulit bagi SQL Server untuk mendeteksi jika kueri berjalan sebagai bagian dari kursor API yang dikeluarkan secara eksternal? Siapa tahu.

Alangkah baiknya jika efek samping ini didokumentasikan secara resmi di suatu tempat, meskipun sulit untuk mengetahui secara pasti ke mana harus pergi di Books Online. Saya bertanya-tanya berapa banyak sistem produksi di luar sana yang tidak menggunakan pemindaian urutan alokasi karena hal ini? Mungkin tidak banyak, tetapi Anda tidak pernah tahu.

Untuk menyelesaikannya, berikut adalah ringkasannya. Pemindaian berdasarkan alokasi tersedia jika:

  1. Opsi server cursor threshold diatur ke -1 (default); dan,
  2. operator pemindaian paket kueri memiliki Ordered:False Properti; dan,
  3. total data_pages dari IN_ROW_DATA unit alokasi paling sedikit 64; dan,
  4. salah satu:
    1. SQL Server memiliki jaminan yang dapat diterima bahwa modifikasi bersamaan tidak mungkin dilakukan; atau,
    2. pemindaian berjalan pada tingkat isolasi baca tanpa komitmen.

Terlepas dari semua hal di atas, pindai dengan TABLESAMPLE klausa selalu menggunakan pemindaian berdasarkan alokasi (dengan satu pengecualian teknis yang disebutkan dalam teks utama).


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Kasus penggunaan untuk sp_prepare / sp_prepexec

  2. Seberapa mahalkah Konversi Implisit sisi kolom?

  3. Pemodelan Database untuk Pencatatan Penjualan. Bagian 1

  4. Cara Membuat Tampilan di SQL

  5. Tidak ada bentuk caching basis data untuk mengurangi kueri basis data duplikat.