क्या लाखों पंक्तियों के साथ संकीर्ण तालिका पर क्वेरी प्रदर्शन बढ़ाना संभव है?


14

मेरे पास एक क्वेरी है जो वर्तमान में पूरा करने के लिए औसतन 2500ms ले रही है। मेरी तालिका बहुत संकीर्ण है, लेकिन 44 मिलियन पंक्तियाँ हैं। प्रदर्शन को बेहतर बनाने के लिए मेरे पास क्या विकल्प हैं, या यह जितना अच्छा है उतना अच्छा है?

पूछताछ

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31'; 

टेबल

CREATE TABLE [dbo].[Heartbeats](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [DeviceID] [int] NOT NULL,
    [IsPUp] [bit] NOT NULL,
    [IsWebUp] [bit] NOT NULL,
    [IsPingUp] [bit] NOT NULL,
    [DateEntered] [datetime] NOT NULL,
 CONSTRAINT [PK_Heartbeats] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

अनुक्रमणिका

CREATE NONCLUSTERED INDEX [CommonQueryIndex] ON [dbo].[Heartbeats] 
(
    [DateEntered] ASC,
    [DeviceID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

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

अपडेट करें

जब मैं बल इंडेक्स संकेत का उपयोग करने के लिए क्वेरी बदलता हूं, तो क्वेरी 50ms में निष्पादित होती है:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats] WITH(INDEX(CommonQueryIndex))
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' 

एक सही ढंग से चयनात्मक DeviceID क्लॉज जोड़ने से 50ms रेंज हिट होती है:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' AND DeviceID = 4;

यदि मैं ORDER BY [DateEntered], [DeviceID]मूल क्वेरी में जोड़ता हूं , तो मैं 50ms की सीमा में हूं:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' 
ORDER BY [DateEntered], [DeviceID];

ये सभी उस सूचकांक का उपयोग करते हैं जिसकी मैं उम्मीद कर रहा था (CommonQueryIndex) तो, मुझे लगता है कि मेरा सवाल अब है, क्या इस तरह से इस सूचकांक को प्रश्नों पर उपयोग करने के लिए मजबूर करने का एक तरीका है? या मेरी मेज का आकार ऑप्टिमाइज़र को बहुत अधिक फेंक रहा है और मुझे बस ORDER BYएक संकेत का उपयोग करना चाहिए ?


मुझे लगता है कि आप "डेट-एनीज्ड" पर एक और गैर-संकुल सूचकांक जोड़ सकते हैं जो प्रदर्शन को कुछ और हद तक बढ़ाएगा
प्रवीण

@ प्रवीण क्या यह मूल रूप से मेरे मौजूदा सूचकांक के समान होगा? क्या मुझे कुछ विशेष करने की आवश्यकता है क्योंकि एक ही क्षेत्र पर दो सूचकांक होंगे?
नैट

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

मैंने देखा कि आपके पास आपके NC इंडेक्स में डिवाइसआईडी है। क्या यह संभव है कि आपके क्लॉज़ में इसे शामिल किया जाए? और क्या यह परिणाम को दहलीज से नीचे लाएगा? <35k रिकॉर्ड (शीर्ष 1000 खंड के बिना)।
एडवर्ड डॉर्टलैंड

1
आखिरी सवाल, क्या आप हमेशा डेट के क्रम में सम्मिलित कर रहे हैं? या फिर ये आर्डर से बाहर हो सकते हैं क्योंकि डिवाइस एक दूसरे से एसिंक्स डाल सकते हैं। आप क्लस्टर किए गए अनुक्रमणिका को DateEntered स्तंभ में बदलने का प्रयास कर सकते हैं। आपके क्लस्टर किए गए अनुक्रमणिका के आपके अवकाश पृष्ठ अब 445 पृष्ठ हैं। यह दोगुना होगा, अगर आप एक इंट से डेटटाइम तक जाएंगे। लेकिन इस मामले में, यह बुरा नहीं हो सकता है।
एडवर्ड डॉर्टलैंड

जवाबों:


13

ऑप्टिमाइज़र आपके पहले इंडेक्स के लिए क्यों नहीं जाता है:

CREATE NONCLUSTERED INDEX [CommonQueryIndex] ON [dbo].[Heartbeats] 
(
    [DateEntered] ASC,
    [DeviceID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

[DateEntered] स्तंभ की चयनात्मकता का मामला है।

आपने हमें बताया कि आपकी तालिका में 44 मिलियन पंक्तियाँ हैं। पंक्ति का आकार है:

आईडी के लिए 4 बाइट्स, डिवाइस आईडी के लिए 4 बाइट्स, तारीख के लिए 8 बाइट्स और 4 बिट कॉलम के लिए 1 बाइट्स। यह है कि 17 बाइट्स + 7 बाइट्स ओवरहेड (टैग, नल बिटमैप, var कोल ऑफ़सेट, कर्नल काउंट) कुल 24 बाइट्स प्रति पंक्ति में।

जो कि 140k पेजों पर रौली अनुवाद करेगा। उन 44 मिलियन पंक्तियों को संग्रहीत करने के लिए।

अब आशावादी दो काम कर सकता है:

  1. यह टेबल को स्कैन कर सकता है (क्लस्टर इंडेक्स स्कैन)
  2. या यह आपके सूचकांक का उपयोग कर सकता है। आपके सूचकांक में प्रत्येक पंक्ति के लिए, तब क्लस्टर इंडेक्स में बुकमार्क लुकअप करने की आवश्यकता होगी।

अब एक निश्चित बिंदु पर यह और अधिक महंगा हो जाता है कि आपके गैर-संकुलित सूचकांक में पाए जाने वाले प्रत्येक सूचकांक प्रविष्टि के लिए संकुल सूचकांक में इन सभी एकल लुकअप को करना। उस सीमा के लिए आम तौर पर लुकअप की कुल संख्या 25% से अधिक होनी चाहिए, कुल तालिका पृष्ठ की संख्या का 33%।

तो इस मामले में: 140k / 25% = 35000 पंक्तियाँ 140k / 33% = 46666 पंक्तियाँ।

(@RBryryYoung, 35k कुल पंक्तियों का 0.08% है और 46666 0.10% है, इसलिए मुझे लगता है कि भ्रम कहाँ था)

इसलिए यदि आपका क्लॉज 35000 और 46666 पंक्तियों के बीच कहीं होगा, (यह शीर्ष खंड के नीचे है!) यह बहुत संभावना है कि आपके गैर-संकुल का उपयोग नहीं किया जाएगा और क्लस्टर अनुक्रमित स्कैन का उपयोग किया जाएगा।

इसे बदलने के केवल दो तरीके हैं:

  1. अपने चयन को और अधिक चयनात्मक बनाएं। (अगर संभव हो तो)
  2. * ड्रॉप करें और केवल कुछ कॉलम चुनें ताकि आप एक कवरिंग इंडेक्स का उपयोग कर सकें।

अब आप एक चयन सूचकांक का उपयोग करते हुए भी सुनिश्चित कर सकते हैं कि आप एक चयन * का उपयोग करें। होवर जो आपके आवेषण / अपडेट / डिलीट के लिए बस एक विशाल ओवरहेड बनाता है। अगर यह सबसे अच्छा समाधान है, तो हमें यह सुनिश्चित करने के लिए आपके कार्य भार (पढ़ने बनाम लिखना) के बारे में अधिक जानना होगा।

डेटाइम से स्मैलाडटाइम में बदलाव क्लस्टर्ड इंडेक्स पर आकार में 16% और आपके नॉन क्लस्टर्ड इंडेक्स पर साइज में 24% की कमी है।


स्कैन दहलीज आम तौर पर उस (10% या उससे भी कम) की तुलना में बहुत कम है, हालांकि चूंकि सीमा एक वर्ष से एक दिन पहले की है, इसलिए इसे उस सीमा को भी नहीं बनाना चाहिए। और एक गुच्छेदार सूचकांक स्कैन नहीं दिया गया है, क्योंकि एक आवरण सूचकांक जोड़ा गया था। चूंकि यह सूचकांक WHERE क्लॉज SARG-सक्षम बनाता है, इसलिए इसे प्राथमिकता दी जानी चाहिए।
RBarryYoung

@RarryYoung मैं यह समझाने की कोशिश कर रहा था कि [EnteredDate], [DeviceID] पर गैर-संकुलित सूचकांक का उपयोग क्यों नहीं किया गया। थ्रेशोल्ड के बारे में मुझे लगता है कि हम दोनों सहमत हैं, मैं केवल एक पृष्ठ के परिप्रेक्ष्य से बात कर रहा हूं। मैं इसे और अधिक स्पष्ट करने के लिए अपने उत्तर को बदल दूंगा।
बजे एडवर्ड डॉर्टलैंड

उत्तर को यह स्पष्ट करने के लिए बदल दिया कि मैं क्या उत्तर दे रहा था। मैं यह नहीं समझा सकता हूं कि @RBarryYoung के कवरिंग इंडेक्स का उपयोग क्यों नहीं किया गया है। मैंने इसे यहां एक लाख पंक्तियों में परीक्षण किया, और कवरिंग इंडेक्स का उपयोग करके इसे अनुकूलित किया।
एडवर्ड डॉर्टलैंड

बहुत व्यापक प्रतिक्रिया के लिए धन्यवाद, बहुत मायने रखता है। कार्यभार के संबंध में, तालिका में प्रति मिनट 5 मिनट की अवधि में 150-300 आवेषण होते हैं और रिपोर्टिंग उद्देश्यों के लिए कुछ प्रति दिन पढ़ते हैं।
नैट

कवरिंग इंडेक्स के लिए ओवरहेड हेड वास्तव में महत्वपूर्ण नहीं है क्योंकि यह एक संकीर्ण तालिका है और "कवरिंग" पहले से मौजूद इंडेक्स के लिए एक अतिरिक्त है जिसमें पहले से ही अधिकांश पंक्ति शामिल हैं।
RBarryYoung

8

क्या कोई विशेष कारण है कि आपका पीके संकुल है? कई लोग ऐसा करते हैं क्योंकि यह उस तरह से चूक करता है, या उन्हें लगता है कि पीके को क्लस्टर करना होगा। नहीं ऐसा नहीं है। क्लस्टर इंडेक्स आमतौर पर श्रेणी प्रश्नों (जैसे यह) या चाइल्ड टेबल की विदेशी कुंजी के लिए सर्वोत्तम होते हैं।

क्लस्टरिंग इंडेक्स का एक प्रभाव यह होता है कि यह सभी डेटा को एक साथ जोड़ देता है क्योंकि डेटा क्लस्टर ट्री के पत्ती नोड्स पर संग्रहीत होता है। इसलिए, यह मानते हुए कि आप किसी श्रेणी के 'बहुत विस्तृत' के लिए नहीं पूछ रहे हैं, ऑप्टिमाइज़र को यह पता चल जाएगा कि b ट्री के किस हिस्से में डेटा है और इसके लिए एक पंक्ति पहचानकर्ता ढूंढना नहीं पड़ेगा और फिर डेटा कहाँ पर होगा है (जैसे यह एक NC इंडेक्स के साथ काम करते समय होता है)। किसी श्रेणी का 'बहुत चौड़ा' क्या है? एक हास्यास्पद उदाहरण एक मेज से 11 महीने के डेटा के लिए पूछ रहा होगा जिसमें केवल एक वर्ष का रिकॉर्ड है। डेटा का एक दिन खींचना एक समस्या नहीं होनी चाहिए, यह मानते हुए कि आपके आंकड़े अद्यतित हैं। (हालांकि, यदि आप कल के डेटा की तलाश कर रहे हैं और आप तीन दिनों के लिए अपडेट नहीं किए गए हैं, तो ऑप्टिमाइज़र मुश्किल में पड़ सकता है।)

चूँकि आप "SELECT *" क्वेरी चला रहे हैं, इंजन को तालिका के सभी स्तंभों को वापस करने की आवश्यकता होगी (भले ही कोई व्यक्ति एक नया जोड़ता है कि आपके ऐप को उस क्षण की आवश्यकता नहीं है) इसलिए एक कवरिंग इंडेक्स या एक सूचकांक शामिल स्तंभों से बहुत मदद नहीं मिलेगी, अगर सभी पर। (यदि आप किसी इंडेक्स में टेबल से हर कॉलम को शामिल कर रहे हैं, तो आप कुछ गलत कर रहे हैं।) ऑप्टिमाइज़र शायद उन एनसी इंडेक्स की अनदेखी करेगा।

इसलिए क्या करना है?

मेरा सुझाव NC इंडेक्स को गिराना, क्लस्टर किए गए पीके को नॉनक्लस्टर्ड में बदलना और [DateEntered] पर क्लस्टर इंडेक्स बनाना होगा। सरल तब तक बेहतर है, जब तक कि यह अन्यथा सिद्ध न हो।


यह मानते हुए कि पंक्तियों को बढ़ते क्रम में डाला जाता है यह सबसे सरल उत्तर है - लेकिन गैर-रैखिक क्रम में डालने से विखंडन होगा।
कर्क ब्रॉडहर्स्ट

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

4

जब तक आपको वहाँ "" * "मिल गया है, तब केवल एक चीज जो मैं सोच सकता था कि इससे आपकी इंडेक्स परिभाषा को बदलने में बहुत अंतर होगा:

CREATE NONCLUSTERED INDEX [CommonQueryIndex] ON [dbo].[Heartbeats] 
(
    [DateEntered] ASC,
    [DeviceID] ASC
)INCLUDE (ID, IsWebUp, IsPingUp, IsPUp)
 WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

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


मैंने अभी इसे आज़माया है और मैं अभी भी उसी स्थान पर हूँ, 2500ms सर्वर की प्रतिक्रिया और 10ms ग्राहक प्रक्रिया समय की प्रतीक्षा कर रहे हैं।
नैट

क्वेरी योजना पोस्ट करें।
RBarryYoung

ऐसा लगता है कि यह Clustered Index का उपयोग कर रहा है। (सेलेक्ट कॉस्ट: 0% <- टॉप कॉस्ट: 20% <- क्लस्टर किए गए इंडेक्स स्कैन PK_Heartbeats कॉस्ट: 80%)
नैट

हाँ, यह सही नहीं है, कुछ आंकड़े फेंक रहे हैं / आशावादी बंद। नए इंडेक्स का उपयोग करने के लिए बाध्य करने के लिए एक संकेत जोड़ें।
RBarryYoung

@ मोम वर्नोन: हो सकता है, लेकिन यह क्वेरी योजना पर चिह्नित किया जाना चाहिए था।
RBarryYoung

3

मैं इसे थोड़ा अलग ढंग से देखूंगा।

  • हां, मुझे पता है कि यह एक पुराना धागा है, लेकिन मैं अंतर्विरोधी हूं।

मैं डेटाइम कालम को डंप करूंगा - इसे इंट में बदलें। लुकअप टेबल रखें या अपनी तिथि के लिए एक कन्वर्ट करें।

क्लस्टर किए गए इंडेक्स को डंप करें - इसे एक ढेर के रूप में छोड़ दें और नए INT कॉलम पर एक गैर-क्लस्टर इंडेक्स बनाएं जो दिनांक का प्रतिनिधित्व करता है। यानी आज 20121015 होगा। यह आदेश महत्वपूर्ण है। आप तालिका को कितनी बार लोड करते हैं, इसके आधार पर, उस सूचकांक को DESC क्रम में बनाते हुए देखें। मंट की लागत अधिक होगी और आप एक भरण कारक या विभाजन शुरू करना चाहेंगे। विभाजन से आपके रन समय को कम करने में मदद मिलेगी।

अंत में, यदि आप SQL 2012 का उपयोग कर सकते हैं, तो SEQUENCE का उपयोग करने का प्रयास करें - यह आवेषण के लिए पहचान () को बेहतर बनाएगा।


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

उस स्थिति में, डेटाइम कॉलम रखें, डेट के लिए इंट कॉलम जोड़ें (चूंकि आपकी सीमा डेट एलिमेंट पर आधारित है, न कि टाइम एलिमेंट पर)। आप टाइम डेटापाइप का उपयोग करने पर भी विचार कर सकते हैं और फिर, प्रभावी रूप से तारीख के अलावा समय को विभाजित कर सकते हैं। उस तरीके से, आपका डेटा फ़ुटप्रिंट छोटा है और आपके पास अभी भी कॉलम का टाइम एलिमेंट है।
जेरेमी लोवेल

1
मुझे यकीन नहीं है कि मैं इससे पहले क्यों चूक गया लेकिन क्लस्टर इंडेक्स और गैर-क्लस्टर इंडेक्स पर पंक्ति संपीड़न का उपयोग करता हूं। मैंने अभी आपकी तालिका के साथ एक त्वरित परीक्षण किया है और यहां मैंने जो पाया है: मैंने ऊपर निर्धारित तालिका में डेटा (5.8 मिलियन पंक्तियों) का एक सेट बनाया। मैंने क्लस्टर्ड और नॉनक्लेस्टेड इंडेक्स को संकुचित (पंक्ति) किया। तार्किक रीड, आपकी सटीक क्वेरी के आधार पर, 2,074 से घटकर 1,433 हो गया। यह एक महत्वपूर्ण कमी है और मुझे विश्वास है कि अकेले ही आपकी मदद करेगा - और यह बहुत कम जोखिम है।
जेरेमी लोवेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.