Database
 sql >> डेटाबेस >  >> RDS >> Database

हेकाटन को मूल रूप से संकलित संग्रहीत प्रक्रियाओं को कैसे कॉल न करें

नोट:यह पोस्ट मूल रूप से केवल हमारी ईबुक, SQL सर्वर के लिए उच्च प्रदर्शन तकनीक, खंड 2 में प्रकाशित हुई थी। आप हमारी ई-बुक्स के बारे में यहां जान सकते हैं। यह भी ध्यान दें कि SQL सर्वर 2016 में इन-मेमोरी OLTP में नियोजित एन्हांसमेंट के साथ इनमें से कुछ चीजें बदल सकती हैं।

ट्रांजैक्ट-एसक्यूएल कोड के संबंध में कुछ आदतें और सर्वोत्तम प्रथाएं हैं जो समय के साथ विकसित होती हैं। विशेष रूप से संग्रहीत प्रक्रियाओं के साथ, हम सही डेटा प्रकार के पैरामीटर मानों को पारित करने का प्रयास करते हैं, और हमारे पैरामीटर को स्पष्ट रूप से नाम देते हैं न कि केवल क्रमिक स्थिति पर निर्भर करते हैं। कभी-कभी, हालांकि, हम इसके बारे में आलसी हो सकते हैं:हम एक यूनिकोड स्ट्रिंग को N के साथ उपसर्ग करना भूल सकते हैं , या पैरामीटर नाम निर्दिष्ट करने के बजाय केवल स्थिरांक या चरों को क्रम में सूचीबद्ध करें। या दोनों।

SQL सर्वर 2014 में, यदि आप इन-मेमोरी OLTP ("हेकाटन") और मूल रूप से संकलित प्रक्रियाओं का उपयोग कर रहे हैं, तो आप इन चीजों पर अपनी सोच को थोड़ा समायोजित करना चाह सकते हैं। मैं कोडप्लेक्स पर SQL Server 2014 RTM इन-मेमोरी OLTP सैंपल के विरुद्ध कुछ कोड के साथ प्रदर्शित करूंगा, जो AdventureWorks2012 नमूना डेटाबेस का विस्तार करता है। (यदि आप इसे शुरू से आगे बढ़ाने के लिए सेट अप करने जा रहे हैं, तो कृपया पिछली पोस्ट में मेरी टिप्पणियों पर एक त्वरित नज़र डालें।)

आइए संग्रहित प्रक्रिया के लिए हस्ताक्षर पर एक नज़र डालें 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

मैं उत्सुक था कि क्या यह मायने रखता है कि क्या मापदंडों का नाम दिया गया था, या यदि मूल रूप से संकलित प्रक्रियाओं ने निहित रूपांतरणों को पारंपरिक संग्रहीत प्रक्रियाओं की तुलना में किसी भी बेहतर संग्रहीत प्रक्रियाओं के तर्क के रूप में संभाला। सबसे पहले मैंने एक कॉपी बनाई Sales.usp_InsertSpecialOffer_inmem एक पारंपरिक संग्रहित प्रक्रिया के रूप में - इसमें केवल ATOMIC . को हटाना शामिल था NOT NULL . को ब्लॉक करना और हटाना इनपुट पैरामीटर से घोषणाएं:

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

स्थानांतरण मानदंड को कम करने के लिए, प्रक्रिया अभी भी तालिका के इन-मेमोरी संस्करण, Sales.SpecialOffer_inmem में सम्मिलित होती है।

तब मैं इन मानदंडों के साथ संग्रहीत कार्यविधि की दोनों प्रतियों के लिए 100,000 कॉल करना चाहता था:

स्पष्ट रूप से नामित पैरामीटर पैरामीटर नामित नहीं हैं
सही डेटा प्रकार के सभी पैरामीटर x x
गलत डेटा प्रकार के कुछ पैरामीटर x x


निम्न बैच का उपयोग करके, संग्रहीत कार्यविधि के पारंपरिक संस्करण के लिए कॉपी किया गया (बस _inmem को हटा रहा है) चार EXEC . से कॉल):

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

मैंने प्रत्येक परीक्षण को 10 बार चलाया, और यहां औसत अवधि मिलीसेकंड में दी गई:

पारंपरिक संग्रहित प्रक्रिया
पैरामीटर औसत अवधि
(मिलीसेकंड)
नामांकित, उचित प्रकार 72,132
नाम नहीं, उचित प्रकार 72,846
नामांकित, अनुचित प्रकार 76,154
नाम नहीं, अनुचित प्रकार 76,902
मूल रूप से संकलित संग्रहित प्रक्रिया
पैरामीटर औसत अवधि
(मिलीसेकंड)
नामांकित, उचित प्रकार 63,202
नाम नहीं, उचित प्रकार 61,297
नामांकित, अनुचित प्रकार 64,560
नाम नहीं, अनुचित प्रकार 64,288

विभिन्न कॉल विधियों की औसत अवधि, मिलीसेकंड में

पारंपरिक संग्रहीत कार्यविधि के साथ, यह स्पष्ट है कि गलत डेटा प्रकारों का उपयोग करने से प्रदर्शन (लगभग 4 सेकंड का अंतर) पर काफी प्रभाव पड़ता है, जबकि मापदंडों का नाम न देने का नाटकीय प्रभाव बहुत कम था (लगभग 700ms जोड़ना)। मैंने हमेशा सर्वोत्तम प्रथाओं का पालन करने और सही डेटा प्रकारों का उपयोग करने के साथ-साथ सभी मापदंडों को नाम देने की कोशिश की है, और यह छोटा परीक्षण इस बात की पुष्टि करता है कि ऐसा करना फायदेमंद हो सकता है।

मूल रूप से संकलित संग्रहीत कार्यविधि के साथ, गलत डेटा प्रकारों का उपयोग करने के कारण प्रदर्शन में पारंपरिक संग्रहीत कार्यविधि के समान ही गिरावट आई। इस बार, हालांकि, मापदंडों के नामकरण से बहुत मदद नहीं मिली; वास्तव में, इसका नकारात्मक प्रभाव पड़ा, जिससे कुल अवधि में लगभग दो सेकंड जुड़ गए। निष्पक्ष होने के लिए, यह काफी कम समय में बड़ी संख्या में कॉल है, लेकिन यदि आप इस सुविधा से पूर्णत:सबसे अधिक ब्लीडिंग-एज प्रदर्शन को निचोड़ने का प्रयास कर रहे हैं, तो हर नैनोसेकंड मायने रखता है।

समस्या का पता लगाना

आप कैसे जान सकते हैं कि आपकी मूल रूप से संकलित संग्रहीत प्रक्रियाओं को इन "धीमी" विधियों में से किसी एक के साथ बुलाया जा रहा है या नहीं? उसके लिए एक XEvent है! इवेंट को natively_compiled_proc_slow_parameter_passing कहा जाता है , और ऐसा लगता है कि इस समय पुस्तकें ऑनलाइन में प्रलेखित नहीं है। इस घटना की निगरानी के लिए आप निम्नलिखित विस्तारित ईवेंट सत्र बना सकते हैं:

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;

एक बार सत्र चलने के बाद, आप उपरोक्त चार कॉलों में से कोई भी व्यक्तिगत रूप से आज़मा सकते हैं, और फिर आप इस क्वेरी को चला सकते हैं:

;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;

आपने जो चलाया उसके आधार पर, आपको इसके समान परिणाम देखने चाहिए:

<थ>डीबी <थ>कारण <थ>बैच
टाइमस्टैम्प ऑब्जेक्ट_आईडी
2014-07-01 16:23:14 AdventureWorks2012 2087678485 नाम_पैरामीटर
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;
2014-07-01 16:23:22 AdventureWorks2012 2087678485 पैरामीटर_रूपांतरण
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;

विस्तारित घटनाओं से नमूना परिणाम

उम्मीद है कि बैच कॉलम अपराधी की पहचान करने के लिए पर्याप्त है, लेकिन अगर आपके पास बड़े बैच हैं जिनमें मूल रूप से संकलित प्रक्रियाओं के लिए कई कॉल हैं और आपको उन वस्तुओं को ट्रैक करने की आवश्यकता है जो विशेष रूप से इस समस्या को ट्रिगर कर रहे हैं, तो आप बस उन्हें object_id उनके संबंधित डेटाबेस में।

अब, मैं सत्र के सक्रिय रहने के दौरान पाठ में सभी 400,000 कॉल चलाने या अत्यधिक समवर्ती, उत्पादन परिवेश में इस सत्र को चालू करने की अनुशंसा नहीं करता - यदि आप इसे बहुत अधिक कर रहे हैं, तो यह कुछ महत्वपूर्ण ओवरहेड का कारण बन सकता है। आप अपने विकास या मंचन के माहौल में इस तरह की गतिविधि की जाँच करने से बहुत बेहतर हैं, जब तक कि आप इसे एक पूर्ण व्यावसायिक चक्र को कवर करने वाले उचित कार्यभार के अधीन कर सकते हैं।

निष्कर्ष

मैं निश्चित रूप से इस तथ्य से हैरान था कि नामकरण पैरामीटर - लंबे समय से एक सर्वोत्तम अभ्यास माना जाता है - को मूल रूप से संकलित संग्रहीत प्रक्रियाओं के साथ सबसे खराब अभ्यास में बदल दिया गया है। और यह Microsoft द्वारा एक संभावित समस्या के लिए पर्याप्त रूप से जाना जाता है कि उन्होंने इसे ट्रैक करने के लिए विशेष रूप से डिज़ाइन किया गया एक विस्तारित ईवेंट बनाया है। यदि आप इन-मेमोरी ओएलटीपी का उपयोग कर रहे हैं, तो यह एक ऐसी चीज है जिसे आपको अपने रडार पर रखना चाहिए क्योंकि आप सहायक संग्रहीत प्रक्रियाओं को विकसित करते हैं। मुझे पता है कि मुझे निश्चित रूप से नामित मापदंडों का उपयोग करने से अपनी मांसपेशियों की मेमोरी को अन-ट्रेन करना होगा।


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PowerShell को Salesforce.com से कनेक्ट करना

  2. बच्चों की पार्टी डेटा मॉडल

  3. रिलेशनल बनाम नॉन-रिलेशनल डेटा बेस - भाग 3

  4. सब कुछ जो आपको SQL में LIKE ऑपरेटर के बारे में जानना आवश्यक है

  5. उपयोगकर्ताओं, थ्रेड्स और पोस्ट को प्रबंधित करने के लिए बुनियादी डेटा संरचना की मॉडलिंग