Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

SQL Server Internal:Rencana Caching Pt. I – Menggunakan Kembali Paket

SQL Server telah ada selama lebih dari 30 tahun, dan saya telah bekerja dengan SQL Server hampir selama itu. Saya telah melihat banyak perubahan selama bertahun-tahun (dan beberapa dekade!) Dan versi produk luar biasa ini. Dalam posting ini saya akan berbagi dengan Anda bagaimana saya melihat beberapa fitur atau aspek SQL Server, terkadang bersama dengan sedikit perspektif historis.

Lihat blog terbaru Kalen tentang Operator Bermasalah di sini.

Rencana untuk diagnostik server SQL bisa mahal untuk dibuat, karena pengoptimal kueri harus dapat menemukan rencana yang bagus untuk setiap kueri legal yang dikirimkan. Pengoptimal mengevaluasi beberapa urutan bergabung, beberapa indeks. dan berbagai jenis algoritme penggabungan dan pengelompokan, bergantung pada kueri Anda dan tabel yang terlibat. Jika kueri yang sama dijalankan kembali, SQL Server dapat menghemat banyak sumber daya dengan menggunakan kembali paket yang sudah ada. Tetapi tidak selalu mungkin untuk menggunakan kembali rencana yang sudah ada, dan itu tidak selalu merupakan hal yang baik untuk dilakukan. Dalam dua artikel berikutnya, kita akan melihat kapan sebuah rencana digunakan kembali dan kapan dikompilasi ulang.

Pertama, kita akan melihat berbagai jenis paket dan tampilan metadata yang paling sering saya gunakan untuk melihat apa yang ada di cache paket saya. Saya telah menulis pandangan saya sendiri yang memberikan informasi yang menurut saya paling sering berguna. SQL Server menyimpan enam jenis paket kueri yang berbeda, tetapi hanya dua yang biasanya digunakan untuk penyetelan cache paket. Ini adalah RENCANA YANG DIKOMPILASI dan RENCANA YANG DIKOMPILASI. Tampilan saya menyaring semua kecuali dua jenis objek cache ini. RENCANA KOMPILASI hadir dalam tiga jenis:AD HOC, PREPARED, dan PROC. Saya akan mengomentari ketiga jenis tersebut.

Bahkan jika kita hanya melihat RENCANA KOMPILASI, masih banyak paket dalam cache yang biasanya perlu diabaikan, karena dibuat oleh SQL Server itu sendiri. Ini termasuk rencana mencari filestream atau indeks pencarian teks lengkap atau kueri internal yang bekerja dengan OLTP dalam memori. Jadi, tampilan saya menambahkan filter untuk mencoba dan menghilangkan sebagian besar rencana yang tidak saya minati. Anda dapat mengunduh skrip untuk membuat tampilan ini, yang disebut sp_cacheobjects , dari sini.

Bahkan dengan semua filter yang digunakan tampilan saya, masih ada beberapa kueri internal SQL Server sendiri dalam cache; Saya biasanya sering menghapus cache paket saat melakukan pengujian di area ini. Cara termudah untuk menghapus SEMUA paket dari cache adalah dengan perintah:DBCC FREEPROCCACHE.

Rencana Kompilasi Adhoc

Jenis rencana yang paling sederhana adalah Adhoc. Ini digunakan untuk kueri dasar yang tidak sesuai dengan kategori lain. Jika Anda telah mengunduh skrip saya dan membuat tampilan sp_cacheobjects saya, Anda dapat menjalankan yang berikut ini. Versi database AdventureWorks apa pun harus berfungsi. Skrip ini membuat salinan tabel dan membuat beberapa indeks unik di atasnya. Itu juga memijat jumlah SubTotal untuk menghapus angka desimal apa pun.

USE AdventureWorks2016;
GO
DROP TABLE IF EXISTS newsales;
GO
-- Make a copy of the Sales.SalesOrderHeader table
SELECT * INTO dbo.newsales
FROM Sales.SalesOrderHeader;
GO
UPDATE dbo.newsales
SET SubTotal = cast(cast(SubTotal as int) as money);
GO
CREATE UNIQUE index newsales_ident
    ON newsales(SalesOrderID);
GO
CREATE INDEX IX_Sales_SubTotal ON newsales(SubTotal);
GO
-- Adhoc query plan reuse
DBCC FREEPROCCACHE;
GO
-- adhoc query
SELECT * FROM dbo.newsales
WHERE SubTotal = 4;
GO
SELECT * FROM sp_cacheobjects;
GO

Dalam output saya, Anda melihat dua paket Adhoc. Salah satunya adalah untuk pernyataan SELECT dari newssales tabel, dan yang lainnya untuk SELECT dari sp_cacheobjects . saya melihat. Karena paket di-cache, jika kueri yang sama PERSIS dijalankan lagi, paket yang sama dapat digunakan kembali dan Anda akan melihat jumlah penggunaan peningkatan nilai. Namun, ada tangkapan. Agar rencana Adhoc dapat digunakan kembali, string SQL harus benar-benar sama persis. Jika Anda mengubah karakter apa pun di SQL, kueri tidak dikenali sebagai kueri yang sama, dan rencana baru dibuat. Jika saya bahkan menambahkan spasi, menyertakan komentar, atau jeda baris baru, itu bukan string yang sama. Jika saya mengubah huruf besar/kecil, itu berarti ada nilai kode ASCII yang berbeda, jadi stringnya tidak sama.

Anda dapat mencobanya sendiri dengan menjalankan berbagai variasi pernyataan SELECT pertama saya dari penjualan berita meja. Anda akan melihat baris yang berbeda dalam cache untuk masing-masing baris. Setelah saya menjalankan beberapa variasi-mengubah nomor yang saya cari, mengubah kasing, menambahkan komentar, dan baris baru, saya melihat yang berikut di cache. SELECT dari tampilan saya sedang digunakan kembali, tetapi yang lainnya memiliki penggunaan nilai 1.

Satu persyaratan tambahan untuk penggunaan kembali rencana kueri Adhoc adalah bahwa sesi yang menjalankan kueri harus memiliki opsi SET yang sama yang berlaku . Ada kolom lain dalam output yang dapat Anda lihat di sebelah kanan teks kueri, yang disebut SETOPTS. Ini adalah string bit dengan bit untuk setiap opsi SET yang relevan. Jika Anda mengubah salah satu opsi, misalnya, SET ANSI_NULLS OFF, bit string akan berubah dan paket yang sama dengan bit string asli tidak dapat digunakan kembali.

Rencana Kompilasi yang Disiapkan

Jenis kedua dari rencana kompilasi yang di-cache adalah rencana yang DIPERSIAPKAN. Jika kueri Anda memenuhi serangkaian persyaratan tertentu. Ini sebenarnya dapat diparameterisasi secara otomatis. Itu muncul di metadata sebagai SIAP dan string SQL menunjukkan penanda parameter. Berikut ini contohnya:

Paket PREPARED menunjukkan penanda parameter sebagai @1 dan tidak menyertakan nilai sebenarnya. Perhatikan ada baris untuk kueri ADHOC dengan nilai aktual 5555, tetapi itu sebenarnya hanya 'kulit' dari kueri sebenarnya. Itu tidak men-cache seluruh paket tetapi hanya kueri dan beberapa detail pengenal, untuk membantu pemroses kueri menemukan paket berparameter dalam cache. Perhatikan ukurannya (halaman yang digunakan ) jauh lebih kecil dari paket yang DIPERSIAPKAN.

Mode parameterisasi default, yang disebut parameterisasi SEDERHANA, sangat ketat tentang rencana apa yang dapat diparameterisasi. Ini benar-benar hanya kueri paling sederhana yang dapat diparameterisasi secara default. Kueri yang berisi JOIN, GROUP BY, OR, dan banyak konstruksi kueri yang relatif umum lainnya mencegah kueri diparameterisasi. Selain tidak memiliki konstruksi ini, hal terpenting untuk parameterisasi SEDERHANA adalah kuerinya AMAN. Ini berarti bahwa hanya ada satu rencana yang mungkin tidak peduli nilai apa yang dilewatkan untuk parameter apa pun. (Tentu saja, kueri tanpa parameter apa pun juga bisa AMAN.) Kueri saya mencari kecocokan persis di kolom SalesOrderID , yang memiliki indeks unik di atasnya. Jadi indeks nonclustered yang ada dapat digunakan untuk menemukan baris yang cocok. Berapapun nilai yang saya gunakan, 55555 atau yang lainnya, tidak akan pernah ada lebih dari satu baris yang berarti rencananya akan tetap bagus.

Dalam contoh paket kueri Adhoc saya, saya mencari nilai yang cocok untuk SubTotal . Beberapa SubTotal nilai muncul beberapa kali atau tidak sama sekali, jadi indeks nonclustered akan bagus. Tetapi nilai lain dapat muncul berkali-kali, sehingga indeks TIDAK akan berguna. Dengan demikian, paket kueri tidak AMAN dan kueri tidak dapat diparameterisasi. Itulah mengapa kami melihat rencana Adhoc untuk contoh pertama saya.

JIKA Anda memiliki pertanyaan dengan GABUNG atau konstruksi yang tidak diizinkan lainnya, Anda dapat memberi tahu SQL Server untuk lebih agresif dalam parameterisasi dengan mengubah opsi basis data:

ALTER DATABASE AdventureWorks2016 SET parameterization FORCED;
GO

Menyetel database Anda ke parameterisasi FORCED berarti SQL Server akan membuat parameter lebih banyak kueri, termasuk yang dengan JOIN, GROUP BY, OR, dll. Tetapi juga berarti SQL Server dapat membuat parameter kueri yang tidak AMAN. Itu mungkin muncul dengan rencana yang bagus ketika hanya beberapa baris yang dikembalikan, dan kemudian menggunakan kembali rencana itu ketika banyak baris dikembalikan. Ini bisa berakhir dengan kinerja yang sangat suboptimal.

Salah satu opsi terakhir untuk rencana yang disiapkan adalah ketika Anda secara eksplisit menyiapkan rencana. Perilaku ini biasanya dipanggil melalui aplikasi dengan SQLPrepare dan SQLExecute Lebah. Anda menentukan apa yang dimaksud dengan kueri dengan penandaan parameter, Anda menentukan tipe data dan Anda menentukan nilai spesifik yang akan digunakan. Kueri yang sama kemudian dapat dijalankan lagi dengan nilai spesifik yang berbeda dan rencana yang ada akan digunakan. Meskipun menggunakan rencana yang disiapkan secara eksplisit dapat dilakukan untuk kasus-kasus di mana SQL Server tidak membuat parameter dan Anda menginginkannya, itu tidak mencegah SQL Server menggunakan rencana yang TIDAK baik untuk parameter berikutnya. Anda perlu menguji kueri Anda dengan banyak nilai input yang berbeda, dan memastikan Anda mendapatkan performa yang diharapkan jika dan saat paket digunakan kembali.

Metadata (mis. sp_cacheobjects saya) view) hanya menunjukkan PREPARED untuk ketiga jenis paket:autoparameterisasi FORCED dan SIMPLE dan parameterisasi EXPLICIT.

Rencana Kompilasi Proc

objtype final terakhir nilai untuk Rencana yang Dikompilasi adalah untuk prosedur tersimpan, yang ditampilkan sebagai Proc. Jika memungkinkan, prosedur tersimpan adalah pilihan terbaik untuk kode yang dapat digunakan kembali, karena kemudahan pengelolaannya dari server itu sendiri, tetapi itu tidak berarti mereka dijamin selalu memberikan kinerja terbaik. Sama seperti menggunakan opsi parameterisasi FORCED (dan juga parameterisasi eksplisit), prosedur tersimpan menggunakan 'parameter sniffing'. Ini berarti nilai parameter pertama yang diteruskan menentukan rencana. Jika eksekusi berikutnya berjalan dengan baik dengan rencana yang sama, maka parameter sniffing tidak menjadi masalah dan sebenarnya bisa bermanfaat karena menghemat biaya kompilasi ulang dan optimasi ulang. Namun, jika eksekusi berikutnya dengan nilai yang berbeda tidak menggunakan rencana awal, maka kami memiliki masalah. Saya akan menunjukkan contoh parameter sniffing yang menyebabkan masalah

Saya akan membuat prosedur tersimpan berdasarkan penjualan berita tabel yang kita gunakan sebelumnya. Prosedur akan memiliki satu kueri, yang memfilter berdasarkan SalesOrderID kolom, di mana kami membangun indeks nonclustered. Kueri akan didasarkan pada ketidaksetaraan, jadi untuk beberapa nilai kueri dapat mengembalikan hanya beberapa baris, dan menggunakan indeks, dan untuk nilai lainnya, kueri dapat mengembalikan BANYAK baris. Dengan kata lain, kueri tidak AMAN.

USE AdventureWorks2016;
GO
DROP PROC IF EXISTS get_sales_range;
GO
CREATE PROC get_sales_range
   @num int
AS
    SELECT * FROM dbo.newsales
    WHERE SalesOrderID < @num;
GO

Saya akan menggunakan opsi SET STATISTICS IO ON untuk melihat berapa banyak pekerjaan yang dilakukan saat prosedur dijalankan. Pertama, saya akan menjalankannya dengan parameter yang hanya mengembalikan beberapa baris:

SET STATISTICS IO ON
GO
EXEC get_sales_range 43700;
GO

Nilai STATISTICS IO melaporkan bahwa dibutuhkan 43 pembacaan logis untuk mengembalikan 41 baris. Ini normal untuk indeks nonclustered. Sekarang kita jalankan prosedur itu lagi dengan nilai yang jauh lebih besar.

EXEC get_sales_range 66666;
GO
SELECT * FROM sp_cacheobjects;
GO
This time, we see that SQL Server used a whole lot more reads:

Bahkan, tabel memindai penjualan koran tabel hanya membutuhkan 843 pembacaan, jadi ini adalah kinerja yang jauh lebih buruk daripada pemindaian tabel. objek sp_cache view menunjukkan kepada kita bahwa rencana PROC telah digunakan kembali untuk eksekusi kedua ini. Ini adalah contoh ketika parameter sniffing BUKAN hal yang baik.

Jadi, apa yang bisa kita lakukan ketika parameter sniffing menjadi masalah? Di posting berikutnya, saya akan memberi tahu Anda ketika SQL Server datang dengan rencana baru, dan tidak menggunakan kembali yang lama. Kami akan melihat bagaimana Anda dapat memaksa (atau mendorong) kompilasi ulang, dan juga, kami akan melihat kapan SQL Server mengkompilasi ulang kueri Anda secara otomatis.

Spotlight Cloud dapat merevolusi pemantauan kinerja dan diagnostik server SQL Anda. Mulai uji coba gratis Anda menggunakan tautan di bawah ini:


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tips untuk Memindahkan Database SQL Server dari Satu Server ke Server Lain - Tutorial SQL oleh Rajan Singh

  2. Bagaimana cara mengubah gambar ke array byte menggunakan javascript hanya untuk menyimpan gambar di server sql?

  3. Konversi 'datetimeoffset' menjadi 'waktu' di SQL Server (Contoh T-SQL)

  4. Cara menonaktifkan semua Batasan Periksa di Database SQL Server - Tutorial SQL Server / TSQL Bagian 87

  5. Cara memasukkan Daftar C# ke database menggunakan Dapper.NET