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

Bagaimana tidak memanggil prosedur tersimpan yang dikompilasi secara asli Hekaton

Catatan:Posting ini awalnya diterbitkan hanya di eBook kami, High Performance Techniques for SQL Server, Volume 2. Anda dapat mengetahui tentang eBook kami di sini. Perhatikan juga bahwa beberapa hal ini dapat berubah dengan peningkatan yang direncanakan untuk OLTP Dalam Memori di SQL Server 2016.

Ada beberapa kebiasaan dan praktik terbaik yang banyak dari kita kembangkan dari waktu ke waktu terkait dengan kode Transact-SQL. Dengan prosedur tersimpan khususnya, kami berusaha untuk melewatkan nilai parameter dari tipe data yang benar, dan memberi nama parameter kami secara eksplisit daripada hanya mengandalkan posisi ordinal. Namun, kadang-kadang, kita mungkin malas tentang ini:kita mungkin lupa memberi awalan string Unicode dengan N , atau cukup daftarkan konstanta atau variabel secara berurutan alih-alih menentukan nama parameter. Atau keduanya.

Di SQL Server 2014, jika Anda menggunakan In-Memory OLTP ("Hekaton") dan prosedur yang dikompilasi secara asli, Anda mungkin ingin sedikit menyesuaikan pemikiran Anda tentang hal-hal ini. Saya akan mendemonstrasikan dengan beberapa kode terhadap Sampel OLTP Dalam Memori SQL Server 2014 RTM di CodePlex, yang memperluas database sampel AdventureWorks2012. (Jika Anda akan mengatur ini dari awal untuk mengikuti, silakan lihat sekilas pengamatan saya di posting sebelumnya.)

Mari kita lihat tanda tangan untuk prosedur tersimpan Sales.usp_InsertSpecialOffer_inmem :

CREATE PROCEDURE [Sales].[usp_InsertSpecialOffer_inmem] 
	@Description    NVARCHAR(255)  NOT NULL, 
	@DiscountPct    SMALLMONEY     NOT NULL = 0,
	@Type           NVARCHAR(50)   NOT NULL,
	@Category       NVARCHAR(50)   NOT NULL,
	@StartDate      DATETIME2      NOT NULL,
	@EndDate        DATETIME2      NOT NULL,
	@MinQty         INT            NOT NULL = 0,
	@MaxQty         INT                     = NULL,
	@SpecialOfferID INT OUTPUT
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS
BEGIN ATOMIC 
WITH (TRANSACTION ISOLATION LEVEL=SNAPSHOT, LANGUAGE=N'us_english')
 
	DECLARE @msg nvarchar(256)
 
        -- validation removed for brevity
 
	INSERT Sales.SpecialOffer_inmem (Description, 
		DiscountPct,
		Type,
		Category,
		StartDate,
		EndDate,
		MinQty,
		MaxQty) 
	VALUES (@Description, 
		@DiscountPct,
		@Type,
		@Category,
		@StartDate,
		@EndDate,
		@MinQty,
		@MaxQty)
 
	SET @SpecialOfferID = SCOPE_IDENTITY()
END
GO

Saya ingin tahu apakah penting apakah parameter diberi nama, atau jika prosedur yang dikompilasi secara asli menangani konversi implisit sebagai argumen untuk prosedur tersimpan lebih baik daripada prosedur tersimpan tradisional. Pertama saya membuat salinan Sales.usp_InsertSpecialOffer_inmem sebagai prosedur tersimpan tradisional – ini hanya melibatkan penghapusan ATOMIC memblokir dan menghapus NOT NULL deklarasi dari parameter input:

CREATE PROCEDURE [Sales].[usp_InsertSpecialOffer] 
	@Description    NVARCHAR(255), 
	@DiscountPct    SMALLMONEY     = 0,
	@Type           NVARCHAR(50),
	@Category       NVARCHAR(50),
	@StartDate      DATETIME2,
	@EndDate        DATETIME2,
	@MinQty         INT            = 0,
	@MaxQty         INT            = NULL,
	@SpecialOfferID INT OUTPUT
AS
BEGIN
	DECLARE @msg nvarchar(256)
 
        -- validation removed for brevity
 
	INSERT Sales.SpecialOffer_inmem (Description, 
		DiscountPct,
		Type,
		Category,
		StartDate,
		EndDate,
		MinQty,
		MaxQty) 
	VALUES (@Description, 
		@DiscountPct,
		@Type,
		@Category,
		@StartDate,
		@EndDate,
		@MinQty,
		@MaxQty)
 
	SET @SpecialOfferID = SCOPE_IDENTITY()
END
GO

Untuk meminimalkan pergeseran kriteria, prosedur masih menyisipkan ke dalam tabel versi In-Memory, Sales.SpecialOffer_inmem.

Kemudian saya ingin mengatur waktu 100.000 panggilan ke kedua salinan prosedur tersimpan dengan kriteria ini:

Parameter diberi nama secara eksplisit Parameter tidak diberi nama Semua parameter tipe data yang benar x x Beberapa parameter tipe data yang salah x x


Menggunakan kumpulan berikut, disalin untuk versi tradisional prosedur tersimpan (cukup hapus _inmem dari empat EXEC panggilan):

SET NOCOUNT ON;
 
CREATE TABLE #x
(
  i INT IDENTITY(1,1),
  d VARCHAR(32), 
  s DATETIME2(7) NOT NULL DEFAULT SYSDATETIME(), 
  e DATETIME2(7)
);
GO
 
INSERT #x(d) VALUES('Named, proper types');
GO
 
/* this uses named parameters, and uses correct data types */
 
DECLARE 
	@p1 NVARCHAR(255) = N'Product 1',
	@p2 SMALLMONEY    = 10,
	@p3 NVARCHAR(50)  = N'Volume Discount',
	@p4 NVARCHAR(50)  = N'Reseller',
	@p5 DATETIME2     = '20140615',
	@p6 DATETIME2     = '20140620',
	@p7 INT           = 10, 
	@p8 INT           = 20, 
	@p9 INT;
 
EXEC Sales.usp_InsertSpecialOffer_inmem 
	@Description    = @p1,
	@DiscountPct    = @p2,
	@Type           = @p3,
	@Category       = @p4,
	@StartDate      = @p5,
	@EndDate        = @p6,
	@MinQty         = @p7,
	@MaxQty         = @p8,
	@SpecialOfferID = @p9 OUTPUT;
 
GO 100000
 
UPDATE #x SET e = SYSDATETIME() WHERE i = 1;
GO
 
DELETE Sales.SpecialOffer_inmem WHERE Description = N'Product 1';
GO
 
INSERT #x(d) VALUES('Not named, proper types');
GO
 
/* this does not use named parameters, but uses correct data types */
 
DECLARE 
	@p1 NVARCHAR(255) = N'Product 1',
	@p2 SMALLMONEY    = 10,
	@p3 NVARCHAR(50)  = N'Volume Discount',
	@p4 NVARCHAR(50)  = N'Reseller',
	@p5 DATETIME2     = '20140615',
	@p6 DATETIME2     = '20140620',
	@p7 INT           = 10, 
	@p8 INT           = 20, 
	@p9 INT;
 
EXEC Sales.usp_InsertSpecialOffer_inmem 
	@p1, @p2, @p3, @p4, @p5, 
	@p6, @p7, @p8, @p9 OUTPUT;
 
GO 100000
 
UPDATE #x SET e = SYSDATETIME() WHERE i = 2;
GO
 
DELETE Sales.SpecialOffer_inmem WHERE Description = N'Product 1';
GO
 
INSERT #x(d) VALUES('Named, improper types');
GO
 
/* this uses named parameters, but incorrect data types */
 
DECLARE 
	@p1 VARCHAR(255)  = 'Product 1',
	@p2 DECIMAL(10,2) = 10,
	@p3 VARCHAR(255)  = 'Volume Discount',
	@p4 VARCHAR(32)   = 'Reseller',
	@p5 DATETIME      = '20140615',
	@p6 CHAR(8)       = '20140620',
	@p7 TINYINT       = 10, 
	@p8 DECIMAL(10,2) = 20, 
	@p9 BIGINT;
 
EXEC Sales.usp_InsertSpecialOffer_inmem 
	@Description    = @p1,
	@DiscountPct    = @p2,
	@Type           = @p3,
	@Category       = @p4,
	@StartDate      = @p5,
	@EndDate        = @p6,
	@MinQty         = '10',
	@MaxQty         = @p8,
	@SpecialOfferID = @p9 OUTPUT;
 
GO 100000
 
UPDATE #x SET e = SYSDATETIME() WHERE i = 3;
GO
 
DELETE Sales.SpecialOffer_inmem WHERE Description = N'Product 1';
GO
 
INSERT #x(d) VALUES('Not named, improper types');
GO
 
/* this does not use named parameters, and uses incorrect data types */
 
DECLARE 
	@p1 VARCHAR(255)  = 'Product 1',
	@p2 DECIMAL(10,2) = 10,
	@p3 VARCHAR(255)  = 'Volume Discount',
	@p4 VARCHAR(32)   = 'Reseller',
	@p5 DATETIME      = '20140615',
	@p6 CHAR(8)       = '20140620',
	@p7 TINYINT       = 10, 
	@p8 DECIMAL(10,2) = 20, 
	@p9 BIGINT;
 
EXEC Sales.usp_InsertSpecialOffer_inmem 
	@p1, @p2, @p3, @p4, @p5, 
	@p6, '10', @p8, @p9 OUTPUT;
 
GO 100000
 
UPDATE #x SET e = SYSDATETIME() WHERE i = 4;
GO
DELETE Sales.SpecialOffer_inmem WHERE Description = N'Product 1';
GO
 
SELECT d, duration_ms = DATEDIFF(MILLISECOND, s, e) FROM #x;
GO
DROP TABLE #x;
GO

Saya menjalankan setiap tes 10 kali, dan berikut adalah durasi rata-rata, dalam milidetik:

Prosedur Tersimpan Tradisional
Parameter Durasi Rata-rata
(milidetik)
Bernama, jenis yang tepat 72.132
Tidak disebutkan namanya, jenis yang tepat 72.846
Bernama, jenis yang tidak tepat 76,154
Tidak disebutkan namanya, jenis yang tidak tepat 76.902
Prosedur Terkompilasi Secara Asli
Parameter Durasi Rata-rata
(milidetik)
Bernama, jenis yang tepat 63,202
Tidak disebutkan namanya, jenis yang tepat 61,297
Bernama, jenis yang tidak tepat 64.560
Tidak disebutkan namanya, jenis yang tidak tepat 64,288

Durasi rata-rata, dalam milidetik, dari berbagai metode panggilan

Dengan prosedur tersimpan tradisional, jelas bahwa menggunakan tipe data yang salah memiliki dampak besar pada kinerja (perbedaan sekitar 4 detik), sementara tidak menyebutkan parameter memiliki efek yang jauh lebih dramatis (menambahkan sekitar 700 ms). Saya selalu mencoba mengikuti praktik terbaik dan menggunakan tipe data yang tepat serta memberi nama semua parameter, dan pengujian kecil ini tampaknya mengonfirmasi bahwa hal itu dapat bermanfaat.

Dengan prosedur tersimpan yang dikompilasi secara asli, menggunakan tipe data yang salah masih menyebabkan penurunan kinerja yang sama seperti prosedur tersimpan tradisional. Namun kali ini, memberi nama parameter tidak banyak membantu; sebenarnya, itu memiliki dampak negatif, menambahkan hampir dua detik ke durasi keseluruhan. Agar adil, ini adalah jumlah panggilan yang besar dalam waktu yang cukup singkat, tetapi jika Anda mencoba memeras kinerja paling mutakhir yang dapat Anda lakukan dari fitur ini, setiap nanodetik sangat berarti.

Menemukan Masalah

Bagaimana Anda bisa tahu jika prosedur tersimpan yang dikompilasi secara asli dipanggil dengan salah satu dari metode "lambat" ini? Ada XEvent untuk itu! Acara ini disebut natively_compiled_proc_slow_parameter_passing , dan tampaknya tidak didokumentasikan di Buku Daring saat ini. Anda dapat membuat sesi Acara yang Diperpanjang berikut untuk memantau acara ini:

CREATE EVENT SESSION [XTP_Parameter_Events] ON SERVER 
ADD EVENT sqlserver.natively_compiled_proc_slow_parameter_passing
(
    ACTION(sqlserver.sql_text)
) 
ADD TARGET package0.event_file(SET filename=N'C:\temp\XTPParams.xel');
GO
ALTER EVENT SESSION [XTP_Parameter_Events] ON SERVER STATE = START;

Setelah sesi berjalan, Anda dapat mencoba salah satu dari empat panggilan di atas satu per satu, lalu Anda dapat menjalankan kueri ini:

;WITH x([timestamp], db, [object_id], reason, batch)
AS
(
  SELECT 
    xe.d.value(N'(event/@timestamp)[1]',N'datetime2(0)'),
    DB_NAME(xe.d.value(N'(event/data[@name="database_id"]/value)[1]',N'int')),
    xe.d.value(N'(event/data[@name="object_id"]/value)[1]',N'int'),
    xe.d.value(N'(event/data[@name="reason"]/text)[1]',N'sysname'),
    xe.d.value(N'(event/action[@name="sql_text"]/value)[1]',N'nvarchar(max)')
  FROM 
    sys.fn_xe_file_target_read_file(N'C:\temp\XTPParams*.xel',NULL,NULL,NULL) AS ft
    CROSS APPLY (SELECT CONVERT(XML, ft.event_data)) AS xe(d)
)
SELECT [timestamp], db, [object_id], reason, batch FROM x;

Bergantung pada apa yang Anda jalankan, Anda akan melihat hasil yang mirip dengan ini:

stempel waktu db object_id alasan batch
01-07-2014 16:23:14 AdventureWorks2012 2087678485 named_parameters
DECLARE 
	@p1 NVARCHAR(255) = N'Product 1',
	@p2 SMALLMONEY    = 10,
	@p3 NVARCHAR(50)  = N'Volume Discount',
	@p4 NVARCHAR(50)  = N'Reseller',
	@p5 DATETIME2     = '20140615',
	@p6 DATETIME2     = '20140620',
	@p7 INT           = 10, 
	@p8 INT           = 20, 
	@p9 INT;

EXEC Sales.usp_InsertSpecialOffer_inmem 
	@Description    = @p1,
	@DiscountPct    = @p2,
	@Type           = @p3,
	@Category       = @p4,
	@StartDate      = @p5,
	@EndDate        = @p6,
	@MinQty         = @p7,
	@MaxQty         = @p8,
	@SpecialOfferID = @p9 OUTPUT;
01-07-2014 16:23:22 AdventureWorks2012 2087678485 konversi_parameter
DECLARE 
	@p1 VARCHAR(255)  = 'Product 1',
	@p2 DECIMAL(10,2) = 10,
	@p3 VARCHAR(255)  = 'Volume Discount',
	@p4 VARCHAR(32)   = 'Reseller',
	@p5 DATETIME      = '20140615',
	@p6 CHAR(8)       = '20140620',
	@p7 TINYINT       = 10, 
	@p8 DECIMAL(10,2) = 20, 
	@p9 BIGINT;

EXEC Sales.usp_InsertSpecialOffer_inmem 
	@p1, @p2, @p3, @p4, @p5, 
	@p6, '10', @p8, @p9 OUTPUT;

Contoh hasil dari Acara yang Diperpanjang

Semoga batch kolom sudah cukup untuk mengidentifikasi pelakunya, tetapi jika Anda memiliki kumpulan besar yang berisi banyak panggilan ke prosedur yang dikompilasi secara asli dan Anda perlu melacak objek yang secara khusus memicu masalah ini, Anda cukup mencarinya dengan object_id di database masing-masing.

Sekarang, saya tidak menyarankan menjalankan semua 400.000 panggilan dalam teks saat sesi aktif, atau mengaktifkan sesi ini di lingkungan produksi yang sangat bersamaan – jika Anda sering melakukan ini, ini dapat menyebabkan beberapa overhead yang signifikan. Jauh lebih baik Anda memeriksa aktivitas semacam ini di lingkungan pengembangan atau pementasan Anda, selama Anda dapat menerapkannya pada beban kerja yang tepat yang mencakup siklus bisnis penuh.

Kesimpulan

Saya benar-benar terkejut dengan fakta bahwa penamaan parameter – yang telah lama dianggap sebagai praktik terbaik – telah berubah menjadi praktik terburuk dengan prosedur tersimpan yang dikompilasi secara native. Dan diketahui oleh Microsoft sebagai masalah potensial yang cukup besar sehingga mereka membuat Acara yang Diperpanjang yang dirancang khusus untuk melacaknya. Jika Anda menggunakan OLTP Dalam Memori, ini adalah satu hal yang harus Anda perhatikan saat Anda mengembangkan prosedur tersimpan yang mendukung. Saya tahu saya pasti harus melepaskan pelatihan memori otot saya dari menggunakan parameter bernama.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Menemukan Nilai Berbeda dengan Cepat

  2. Buat Daftar Keren Sendiri, atau GitHub sebagai Notebook

  3. Anonimkan detail paket Anda secara asli di Plan Explorer

  4. Cara Membuat Objek ResultSet yang Dapat Digulir dan Dapat Diperbarui di JDBC

  5. Migrasi Data