न्यूनतम कार्डिनैलिटी का अनुमान न्यूनतम लॉगिंग से INSERT को अयोग्य घोषित करता है?


11

दूसरा INSERTकथन ~ 5x पहले की तुलना में धीमा क्यों है ?

उत्पन्न लॉग डेटा की मात्रा से, मुझे लगता है कि दूसरा न्यूनतम लॉगिंग के लिए योग्य नहीं है। हालाँकि, डेटा लोड हो रहा है प्रदर्शन गाइड में प्रलेखन इंगित करता है कि दोनों आवेषण न्यूनतम लॉग इन करने में सक्षम होना चाहिए। इसलिए यदि न्यूनतम लॉगिंग महत्वपूर्ण प्रदर्शन अंतर है, तो ऐसा क्यों है कि दूसरी क्वेरी न्यूनतम लॉगिंग के लिए योग्य नहीं है? स्थिति को सुधारने के लिए क्या किया जा सकता है?


# 1 क्वेरी: INSERT का उपयोग करके 5MM पंक्तियों को सम्मिलित करना ... (TABLOCK) के साथ

निम्नलिखित क्वेरी पर विचार करें, जो 5MM पंक्तियों को एक ढेर में सम्मिलित करता है। जैसा कि रिपोर्ट किया गया है, यह क्वेरी लेन-देन और लॉग डेटा 1 secondउत्पन्न करती 64MBहै sys.dm_tran_database_transactions

CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
FROM dbo.fiveMillionNumbers
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO


# 2 क्वेरी: समान डेटा सम्मिलित करता है, लेकिन SQL पंक्तियों के # को कम करके आंका जाता है

अब इस समान क्वेरी पर विचार करें, जो बिल्कुल उसी डेटा पर संचालित होती है, लेकिन एक तालिका (या SELECTमेरे वास्तविक उत्पादन मामले में कई जोड़ के साथ जटिल विवरण) से आरेखित होती है जहां कार्डिनिटी का अनुमान बहुत कम है। यह क्वेरी लेन-देन लॉग डेटा में निष्पादित 5.5 secondsऔर उत्पन्न होती 461MBहै।

CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that produces 5MM rows but SQL estimates just 1000 rows
FROM dbo.fiveMillionNumbersBadEstimate
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO


पूरी स्क्रिप्ट

परीक्षण डेटा उत्पन्न करने और इनमें से किसी भी परिदृश्य को निष्पादित करने के लिए स्क्रिप्ट के पूरे सेट के लिए इस पास्टबिन को देखें । ध्यान दें कि आपको SIMPLE पुनर्प्राप्ति मॉडल में एक डेटाबेस का उपयोग करना चाहिए ।


व्यावसायिक संदर्भ

हम अर्ध-अक्सर डेटा की लाखों पंक्तियों के चारों ओर घूम रहे हैं, और इन ऑपरेशनों को यथासंभव प्रभावी होना आवश्यक है, निष्पादन समय और डिस्क I / O लोड दोनों के संदर्भ में। हम शुरू में इस धारणा के तहत थे कि एक ढेर तालिका बनाना और उपयोग INSERT...WITH (TABLOCK)करना ऐसा करने का एक अच्छा तरीका था, लेकिन अब यह देखते हुए कम आत्मविश्वास हो गया है कि हमने वास्तविक उत्पादन परिदृश्य में ऊपर प्रदर्शित स्थिति का अवलोकन किया है (अधिक जटिल प्रश्नों के साथ, न कि सरलीकृत संस्करण यहाँ)।

जवाबों:


7

ऐसा क्यों है कि दूसरी क्वेरी न्यूनतम लॉगिंग के लिए योग्य नहीं है?

दूसरी क्वेरी के लिए न्यूनतम लॉगिंग उपलब्ध है , लेकिन इंजन रनटाइम पर इसका उपयोग नहीं करने का विकल्प चुनता है।

नीचे एक न्यूनतम सीमा है INSERT...SELECTजिसके लिए वह बल्क लोड ऑप्टिमाइज़ेशन का उपयोग नहीं करना चुनता है। एक थोक पंक्तियों के संचालन को स्थापित करने में एक लागत शामिल है, और केवल कुछ पंक्तियों को सम्मिलित करने से कुशल अंतरिक्ष उपयोग नहीं होगा।

स्थिति को सुधारने के लिए क्या किया जा सकता है?

कई अन्य तरीकों (जैसे SELECT INTO) में से एक का उपयोग करें जिसमें यह दहलीज नहीं है। वैकल्पिक रूप से, आप स्रोत क्वेरी को किसी तरह से पंक्तियों / पृष्ठों की अनुमानित संख्या को बढ़ाने के लिए किसी तरह से फिर से लिखने में सक्षम हो सकते हैं INSERT...SELECT

अधिक उपयोगी जानकारी के लिए ज्योफ का स्व-उत्तर भी देखें ।


संभवतः दिलचस्प सामान्य ज्ञान: SET STATISTICS IO लक्ष्य तालिका के लिए तार्किक रीड तभी रिपोर्ट करता है जब बल्क लोडिंग अनुकूलन का उपयोग नहीं किया जाता है


5

मैं अपने स्वयं के परीक्षण रिग के साथ समस्या को फिर से बनाने में सक्षम था:

USE test;

CREATE TABLE dbo.SourceGood
(
    SourceGoodID INT NOT NULL
        CONSTRAINT PK_SourceGood
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , SomeData VARCHAR(384) NOT NULL
);

CREATE TABLE dbo.SourceBad
(
    SourceBadID INT NOT NULL
        CONSTRAINT PK_SourceBad
        PRIMARY KEY CLUSTERED
        IDENTITY(-2147483647,1)
    , SomeData VARCHAR(384) NOT NULL
);

CREATE TABLE dbo.InsertTest
(
    SourceBadID INT NOT NULL
        CONSTRAINT PK_InsertTest
        PRIMARY KEY CLUSTERED
    , SomeData VARCHAR(384) NOT NULL
);
GO

INSERT INTO dbo.SourceGood WITH (TABLOCK) (SomeData) 
SELECT TOP(5000000) o.name + o1.name + o2.name
FROM syscolumns o
    , syscolumns o1
    , syscolumns o2;
GO

ALTER DATABASE test SET AUTO_UPDATE_STATISTICS OFF;
GO

INSERT INTO dbo.SourceBad WITH (TABLOCK) (SomeData)
SELECT TOP(5000000) o.name + o1.name + o2.name
FROM syscolumns o
    , syscolumns o1
    , syscolumns o2;
GO

ALTER DATABASE test SET AUTO_UPDATE_STATISTICS ON;
GO

BEGIN TRANSACTION;

INSERT INTO dbo.InsertTest WITH (TABLOCK)
SELECT *
FROM dbo.SourceGood;

SELECT * FROM sys.dm_tran_database_transactions;

/*
database_transaction_log_record_count
472 
database_transaction_log_bytes_used
692136
*/

COMMIT TRANSACTION;


BEGIN TRANSACTION;

INSERT INTO dbo.InsertTest WITH (TABLOCK)
SELECT *
FROM dbo.SourceBad;

SELECT * FROM sys.dm_tran_database_transactions;

/*
database_transaction_log_record_count   
5000003 
database_transaction_log_bytes_used
642699256
*/

COMMIT TRANSACTION;

यह सवाल है, न्यूनतम-लॉग-ऑपरेशन चलाने से पहले स्रोत तालिकाओं पर आंकड़े अपडेट करके समस्या को "ठीक" क्यों नहीं किया जाता है?

TRUNCATE TABLE dbo.InsertTest;
UPDATE STATISTICS dbo.SourceBad;

BEGIN TRANSACTION;

INSERT INTO dbo.InsertTest WITH (TABLOCK)
SELECT *
FROM dbo.SourceBad;

SELECT * FROM sys.dm_tran_database_transactions;

/*
database_transaction_log_record_count
472
database_transaction_log_bytes_used
692136
*/

COMMIT TRANSACTION;

2
वास्तविक कोड में, SELECTकई जोड़ों के साथ एक जटिल कथन होता है जो परिणाम के लिए सेट उत्पन्न करता है INSERT। ये अंतिम तालिका सम्मिलित करने वाले ऑपरेटर के लिए खराब कार्डिनैलिटी अनुमानों का उत्पादन करते हैं (जिसे मैंने खराब UPDATE STATISTICSकॉल के माध्यम से रिप्रो स्क्रिप्ट में सिम्युलेटेड किया है ), और इसलिए यह UPDATE STATISTICSसमस्या को ठीक करने के लिए आदेश जारी करने के रूप में काफी सरल नहीं है। मैं इस बात से पूरी तरह सहमत हूँ कि क्वेरी को सरल बनाना ताकि कार्डिनैलिटी एस्टिमेटर को समझना आसान हो, एक अच्छा दृष्टिकोण हो सकता है, लेकिन यह दिए गए जटिल व्यावसायिक तर्क को लागू करने के लिए एक बढ़िया नहीं है।
ज्योफ पैटरसन

मेरे पास इस पर परीक्षण करने के लिए SQL Server 2014 का उदाहरण नहीं है, हालांकि नए कार्डिनलिटी अनुमानक को सक्षम करने के लिए SQL Server 2014 नई कार्डिनैलिटी एस्टीमेटर समस्याओं और सर्विस पैक 1 में ट्रेस ध्वज 4199 को सक्षम करने के बारे में अन्य बातों की पहचान करना । क्या आपने कोशिश की है?
मैक्स वर्नोन

अच्छा विचार है, लेकिन यह मदद नहीं की। मैंने सिर्फ TF 4199 की कोशिश की, TF 610 (कम से कम लॉगिंग की स्थिति), और दोनों एक साथ (अरे, क्यों नहीं?), लेकिन 2 टेस्ट क्वेरी के लिए कोई बदलाव नहीं।
ज्योफ पैटरसन

4

पंक्तियों की अनुमानित संख्या को बढ़ावा देने के लिए किसी तरह से स्रोत क्वेरी को फिर से लिखें

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

-- Create a dummy table that SQL Server thinks has a million rows
CREATE TABLE dbo.emptyTableWithMillionRowEstimate (
    n INT PRIMARY KEY
)
GO
UPDATE STATISTICS dbo.emptyTableWithMillionRowEstimate
WITH ROWCOUNT = 1000000
GO

-- Concatenate this table into the final rowset:
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
FROM dbo.fiveMillionNumbersBadEstimate
-- Add in dummy rowset to ensure row estimate is high enough for bulk load optimization
UNION ALL
SELECT NULL FROM dbo.emptyTableWithMillionRowEstimate
OPTION (MAXDOP 1)

अंतिम takeaways

  1. SELECT...INTOयदि न्यूनतम लॉगिंग की आवश्यकता है, तो एक बार सम्मिलित ऑपरेशन के लिए उपयोग करें । जैसा कि पॉल बताते हैं, यह पंक्ति अनुमान की परवाह किए बिना न्यूनतम लॉगिंग सुनिश्चित करेगा
  2. जहाँ भी संभव हो, क्वेरी को सरल तरीके से लिखें जो क्वेरी ऑप्टिमाइज़र प्रभावी रूप से बता सकता है। उदाहरण के लिए, एक मध्यवर्ती तालिका पर आँकड़ों को बनाने की अनुमति देने के लिए, कई टुकड़ों में एक क्वेरी को तोड़ना संभव हो सकता है।
  3. यदि आपके पास SQL ​​Server 2014 तक पहुंच है, तो इसे अपने प्रश्न पर आज़माएं; मेरे वास्तविक उत्पादन के मामले में, मैंने अभी इसकी कोशिश की और नए कार्डिनैलिटी एस्टीमेटर ने बहुत अधिक (और बेहतर) अनुमान लगाया; तब क्वेरी न्यूनतम लॉग की गई थी। लेकिन यह मददगार नहीं हो सकता है अगर आपको एसक्यूएल 2012 और उससे पहले का समर्थन करने की आवश्यकता हो।
  4. यदि आप हताश हैं, तो इस तरह के घिनौने उपाय लागू हो सकते हैं!

एक संबंधित लेख

पॉल व्हाइट की मई 2019 ब्लॉग पोस्ट INSERT के साथ न्यूनतम लॉगिंग ... हीप टेबल्स में चयन इस जानकारी को कुछ और विस्तार से कवर करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.