वार्चर (अधिकतम) के कारण टेम्पर्ड बीजों को छाँटना


10

32GB वाले सर्वर पर हम SQL Server 2014 SP2 को 25GB की अधिकतम मेमोरी के साथ चला रहे हैं, हमारे पास दो टेबल हैं, यहाँ आपको दोनों तालिकाओं की सरलीकृत संरचना मिलती है:

CREATE TABLE [dbo].[Settings](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [resourceId] [int] NULL,
    [typeID] [int] NULL,
    [remark] [varchar](max) NULL,
    CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Resources](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [resourceUID] [int] NULL,
 CONSTRAINT [PK_Resources] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO

निम्नलिखित गैर-संकुल अनुक्रमणिका के साथ:

CREATE NONCLUSTERED INDEX [IX_UID] ON [dbo].[Resources]
(
    [resourceUID] ASC
)

CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Settings]
(
    [resourceId] ASC,
    [typeID] ASC
)

डेटाबेस compatibility level120 के साथ कॉन्फ़िगर किया गया है ।

जब मैं इस क्वेरी को चलाता हूं तो स्पिल टू tempdb। इस प्रकार मैं क्वेरी निष्पादित करता हूं:

exec sp_executesql N'
select r.id,remark
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38

यदि [remark]क्षेत्र का चयन नहीं करते हैं तो कोई फैल नहीं होता है। मेरी पहली प्रतिक्रिया यह थी कि नेस्टेड-लूप ऑपरेटर पर अनुमानित पंक्तियों की कम संख्या के कारण स्पिल हुआ।

इसलिए मैं सेटिंग्स तालिका में 5 डेटाटाइम और 5 पूर्णांक कॉलम जोड़ता हूं और उन्हें अपने चयन विवरण में जोड़ता हूं। जब मैं क्वेरी निष्पादित करता हूं तो कोई भी स्पिल नहीं हो रहा है।

[remark]चयन होने पर केवल स्पिल क्यों हो रहे हैं ? यह शायद इस तथ्य के साथ कुछ करना है कि यह एक है varchar(max)। स्पिलिंग से बचने के लिए मैं क्या कर सकता हूं tempdb?

OPTION (RECOMPILE)क्वेरी में जोड़ने से कोई फर्क नहीं पड़ता।


हो सकता है आप कोशिश कर सकते हैं select r.id, LEFT(remark, 512)(या जो भी समझदार सबस्ट्रिंग लंबाई हो सकती है)।
मस्तकियो

@ फॉरेस्ट: मैं समस्या का अनुकरण करने के लिए आवश्यक डेटा को पुन: उत्पन्न करने की कोशिश कर रहा हूं। पहली नजर में यह नेस्टेड लूप के कम अनुमान के साथ करना है। मेरे डमी डेटा में पंक्तियों की अनुमानित संख्या बहुत अधिक है और कोई
स्पिलिंग

जवाबों:


10

यहां कई संभावित वर्कअराउंड होने जा रहे हैं।

आप स्मृति अनुदान को मैन्युअल रूप से समायोजित कर सकते हैं , हालांकि मैं शायद उस मार्ग पर नहीं जाऊंगा।

अधिकतम लंबाई वाले कॉलम को हथियाने से पहले, आप सॉर्ट लोअर को पुश करने के लिए CTE और TOP का भी उपयोग कर सकते हैं। यह नीचे की तरह कुछ दिखेगा।

WITH CTE AS (
SELECT TOP 1000000000 r.ID, s.ID AS ID2, s.typeID
FROM Resources r
inner join Settings s on resourceid=r.id
where resourceUID=@UID
ORDER BY s.typeID
)
SELECT c.ID, ca.remark
FROM CTE c
CROSS APPLY (SELECT remark FROM dbo.Settings s WHERE s.id = c.ID2) ca(remark)
ORDER BY c.typeID

प्रूफ-ऑफ-कॉन्सेप्ट डीबीएफ़लड यहां । नमूना डेटा अभी भी सराहना की जाएगी!

यदि आप पॉल व्हाइट द्वारा एक उत्कृष्ट विश्लेषण पढ़ना चाहते हैं, तो यहां पढ़ें।


7

जब केवल [टिप्पणी] का चयन किया जाता है तो स्पिल क्यों हो रहे हैं?

स्पिल तब हो रहा है जब आप उस कॉलम को शामिल करते हैं क्योंकि आपको बड़े स्ट्रिंग डेटा को सॉर्ट करने के लिए एक बड़ा मेमोरी मेमोरी नहीं मिलता है।

आपको एक बड़ा पर्याप्त मेमोरी अनुदान नहीं मिलता है क्योंकि पंक्तियों की वास्तविक संख्या पंक्तियों की अनुमानित संख्या (1,302 वास्तविक बनाम 126 अनुमानित) से 10x अधिक है।

अनुमान बंद क्यों है? SQL सर्वर को क्यों लगता है कि dbo.Settings में केवल resourceid38 में से एक ही पंक्ति है ?

यह एक आँकड़ों का मुद्दा हो सकता है, जिसे आप चलाकर DBCC SHOW_STATISTICS('dbo.Settings', 'IX_Test')देख सकते हैं और उस हिस्टोग्राम कदम के लिए मायने रखते हैं। लेकिन निष्पादन योजना यह संकेत देती है कि आँकड़े पूर्ण और अद्यतित हैं क्योंकि वे हो सकते हैं।

चूंकि आँकड़े मदद नहीं कर रहे हैं, आपका सबसे अच्छा दांव शायद एक क्वेरी फिर से लिखना है - जिसे फॉरेस्ट ने अपने जवाब में कवर किया है


3

मेरे लिए यह प्रतीत होता है कि whereक्वेरी में खंड समस्या दे रहा है, और कम अनुमानों का कारण है, भले ही OPTION(RECOMPILE)इसका उपयोग किया जाए।

मैंने कुछ परीक्षण डेटा बनाए, और अंत में दो समाधानों के साथ आया, IDक्षेत्र को resourcesया तो एक चर (यदि यह हमेशा अद्वितीय है) या एक अस्थायी तालिका में संग्रहीत किया जाता है, अगर हमारे पास एक से अधिक हो सकते हैं ID

बेस टेस्ट रिकॉर्ड

SET NOCOUNT ON
DECLARE @i int= 1;
WHILE @i <= 10000
BEGIN
INSERT INTO [dbo].[Settings]([resourceId],[typeID],remark)
VALUES(@i,@i,'KEPT THESE VALUES OUT BECAUSE IT WOULD CLUTTER THE EXAMPLES, VALUES OVER 8000 Chars entered here'); -- 23254 character length on each value
INSERT INTO  [dbo].[Resources](resourceUID)
VALUES(@i);
SET @i += 1;
END

ओपी (1300 रिकॉर्ड) के समान अनुमानित परिणाम पाने के लिए 'सीक' मान डालें।

INSERT INTO  [dbo].[Settings]([resourceId],[typeID],remark)
VALUES(38,38,'KEPT THESE VALUES OUT BECAUSE IT WOULD CLUTTER THE EXAMPLES, VALUES OVER 8000 Chars entered here')
GO 1300

ओपी से मिलान करने के लिए कंप्रेशर और अपडेट आँकड़े बदलें

ALTER DATABASE StackOverflow SET COMPATIBILITY_LEVEL = 120;
UPDATE STATISTICS settings WITH FULLSCAN;
UPDATE STATISTICS resources WITH FULLSCAN;

मूल प्रश्न

exec sp_executesql N'
select r.id
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38

मेरे अनुमान और भी खराब हैं , एक अनुमानित पंक्ति के साथ, जबकि 1300 वापस आ गए हैं। और जैसे ओपी ने कहा, इससे कोई फर्क नहीं पड़ता कि मैं जोड़ूंOPTION(RECOMPILE)

ध्यान देने वाली एक महत्वपूर्ण बात यह है कि जब हम अनुमान से मुक्त हो जाते हैं, तो अनुमान 100% सही होते हैं, जो कि अपेक्षित है क्योंकि हम दोनों तालिकाओं में सभी डेटा का उपयोग कर रहे हैं।

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

exec sp_executesql N'
select r.id,remark
FROM Resources r with(index([IX_UID]))
inner join Settings WITH(INDEX([IX_Test])) 
on resourceid=r.id
ORDER BY typeID',
N'@UID int',
@UID=38

जैसा कि अपेक्षित था, अच्छे अनुमान।

इसलिए, हम बेहतर अनुमान प्राप्त करने के लिए क्या बदल सकते हैं लेकिन फिर भी अपने मूल्यों की तलाश कर सकते हैं?

अगर @ यूआईडी अद्वितीय है, जैसा कि ओपी ने दिया था, तो हम एक वेरिएबल में idसे लौटाए गए सिंगल को लगा सकते थे resources, फिर उस वेरिएबल पर एक ऑप्शन (RECOMPILE) की तलाश करें।

DECLARE @UID int =38 , @RID int;
SELECT @RID=r.id from 
Resources r where resourceUID = @UID;

SELECT @uid, remark 
from Settings 
where resourceId = @uid 
Order by typeID
OPTION(RECOMPILE);

जो 100% सटीक अनुमान देता है

लेकिन क्या होगा अगर संसाधनों में कई रिसोर्सयूआईडी हों?

कुछ परीक्षण डेटा जोड़ें

INSERT INTO Resources(ResourceUID)
VALUES (38);
go 50

यह एक अस्थायी तालिका के साथ हल किया जा सकता है

CREATE TABLE #RID (id int)
DECLARE @UID int =38 
INSERT INTO #RID
SELECT r.id 
from 
Resources r where resourceUID = @UID

SELECT @uid, remark 
from Settings  s
INNER JOIN #RID r
ON r.id =s.resourceId
Order by typeID
OPTION(RECOMPILE)

DROP TABLE #RID

सटीक अनुमानों के साथ फिर से ।

यह मेरे अपने डेटासेट, YMMV के साथ किया गया था।


Sp_executesql के साथ लिखा गया

एक चर के साथ

exec sp_executesql N'
DECLARE  @RID int;
    SELECT @RID=r.id from 
    Resources r where resourceUID = @UID;

    SELECT @uid, remark 
    from Settings 
    where resourceId = @uid 
    Order by typeID
    OPTION(RECOMPILE);',
N'@UID int',
@UID=38

एक अस्थायी तालिका के साथ

exec sp_executesql N'

CREATE TABLE #RID (id int)

INSERT INTO #RID
SELECT r.id 
from 
Resources r where resourceUID = @UID

SELECT @uid, remark 
from Settings  s
INNER JOIN #RID r
ON r.id =s.resourceId
Order by typeID
OPTION(RECOMPILE)

DROP TABLE #RID',
N'@UID int',
@UID=38

अभी भी मेरे परीक्षण पर 100% सही अनुमान है

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