DATALENGTHs का Sys.allocation_units से तालिका आकार का मिलान नहीं


11

मैं इस धारणा के तहत था कि अगर मैं DATALENGTH()एक तालिका में सभी रिकॉर्ड के लिए सभी क्षेत्रों का योग कर रहा हूं, तो मुझे तालिका का कुल आकार मिलेगा। क्या मैं गलत हूं?

SELECT 
SUM(DATALENGTH(Field1)) + 
SUM(DATALENGTH(Field2)) + 
SUM(DATALENGTH(Field3)) TotalSizeInBytes
FROM SomeTable
WHERE X, Y, and Z are true

मैंने नीचे इस क्वेरी का उपयोग किया (जो मुझे टेबल साइज प्राप्त करने के लिए ऑनलाइन से मिला है, केवल अनुक्रमित अनुक्रमित हैं ताकि यह मेरे डेटाबेस में किसी विशेष टेबल के आकार को प्राप्त करने के लिए NC इंडेक्स शामिल न करें)। बिलिंग उद्देश्यों के लिए (हम अपने विभागों को उनके द्वारा उपयोग की जाने वाली जगह की राशि से चार्ज करते हैं) मुझे यह पता लगाने की आवश्यकता है कि इस तालिका में प्रत्येक विभाग ने कितना स्थान उपयोग किया है। मेरे पास एक क्वेरी है जो तालिका के भीतर प्रत्येक समूह की पहचान करती है। मुझे केवल यह पता लगाने की आवश्यकता है कि प्रत्येक समूह कितना स्थान ले रहा है।

VARCHAR(MAX)तालिका में फ़ील्ड के कारण प्रति पंक्ति स्थान बेतहाशा स्विंग हो सकता है , इसलिए मैं एक विभाग के लिए पंक्तियों का अनुपात * औसत आकार नहीं ले सकता। जब मैं DATALENGTH()ऊपर वर्णित दृष्टिकोण का उपयोग करता हूं तो मुझे केवल नीचे दिए गए क्वेरी में उपयोग किए गए कुल स्थान का 85% मिलता है। विचार?

SELECT 
s.Name AS SchemaName,
t.NAME AS TableName,
p.rows AS RowCounts,
(SUM(a.total_pages) * 8)/1024 AS TotalSpaceMB, 
(SUM(a.used_pages) * 8)/1024 AS UsedSpaceMB, 
((SUM(a.total_pages) - SUM(a.used_pages)) * 8)/1024 AS UnusedSpaceMB
FROM 
    sys.tables t with (nolock)
INNER JOIN 
    sys.schemas s with (nolock) ON s.schema_id = t.schema_id
INNER JOIN      
    sys.indexes i with (nolock) ON t.OBJECT_ID = i.object_id
INNER JOIN 
    sys.partitions p with (nolock) ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN 
    sys.allocation_units a with (nolock) ON p.partition_id = a.container_id
WHERE 
    t.is_ms_shipped = 0
    AND i.OBJECT_ID > 255 
    AND i.type_desc = 'Clustered'
GROUP BY 
    t.Name, s.Name, p.Rows
ORDER BY 
    TotalSpaceMB desc

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

मुझे वह सुझाव पसंद है और आमतौर पर मैं ऐसा करूंगा। लेकिन ईमानदार होने के लिए मैं एक उदाहरण के रूप में "प्रत्येक विभाग" का उपयोग यह समझाने के लिए करता हूं कि मुझे इसकी आवश्यकता क्यों है, लेकिन ईमानदार होने के लिए, यह वास्तव में ऐसा नहीं है। गोपनीयता कारणों के कारण मैं सटीक कारण नहीं बता पा रहा हूं कि मुझे इस डेटा की आवश्यकता क्यों है, लेकिन यह विभिन्न विभागों के अनुरूप है।

इस तालिका में गैर-अनुक्रमित अनुक्रमितों के बारे में: यदि मुझे NC इंडेक्सों के आकार मिल सकते हैं, तो यह बहुत अच्छा होगा। हालाँकि, NC इंडेक्स में <1% क्लस्टर किए गए इंडेक्स का आकार होता है, इसलिए हम ठीक नहीं हैं। हालाँकि, हम एनसी इंडेक्स को किसी भी तरह कैसे शामिल करेंगे? मुझे क्लस्टर इंडेक्स के लिए एक सटीक आकार भी नहीं मिल सकता है :)


तो संक्षेप में, आपके दो सवाल हैं: (1) पंक्ति की लंबाई का योग मेटाडेटा के संपूर्ण तालिका के आकार के लेखांकन से मेल नहीं खाता है? नीचे दिए गए उत्तर से पता चलता है कि कम से कम भाग में (और जो प्रति सेकंड और प्रति फीचर में उतार-चढ़ाव कर सकता है, उदाहरण के लिए संपीड़न, कॉलमस्टोर आदि)। और अधिक महत्वपूर्ण बात: (2) आप प्रति विभाग उपयोग की जाने वाली वास्तविक जगह का सही निर्धारण कैसे कर सकते हैं? मुझे नहीं पता कि आप ऐसा सटीक रूप से कर सकते हैं - क्योंकि कुछ डेटा के लिए जवाब में हिसाब है, यह बताने का कोई तरीका नहीं है कि यह किस विभाग का है।
हारून बर्ट्रेंड

मुझे नहीं लगता कि समस्या यह है कि आपके पास संकुल अनुक्रमणिका के लिए एक सटीक आकार नहीं है - मेटाडेटा निश्चित रूप से आपको सटीकता के साथ बताता है कि आपका सूचकांक कितना स्थान लेता है। मेटाडेटा आपको क्या बताने के लिए डिज़ाइन नहीं किया गया है - कम से कम आपकी वर्तमान डिज़ाइन / संरचना - प्रत्येक विभाग के साथ कितना डेटा जुड़ा हुआ है।
हारून बर्ट्रेंड

जवाबों:


19

                          Please note that the following info is not intended to be a comprehensive
description of how data pages are laid out, such that one can calculate
the number of bytes used per any set of rows, as that is very complicated.

डेटा केवल 8k डेटा पृष्ठ पर स्थान लेने वाली चीज़ नहीं है:

  • आरक्षित स्थान है। आपको केवल 8192 बाइट्स में से 8060 का उपयोग करने की अनुमति है (यह 132 बाइट्स हैं जो आपकी पहली जगह में कभी नहीं थे):

    • पृष्ठ शीर्ष लेख: यह ठीक 96 बाइट्स है।
    • स्लॉट सरणी: यह प्रति पंक्ति 2 बाइट्स है और प्रत्येक पंक्ति को पृष्ठ पर शुरू होने की भरपाई का संकेत देता है। इस सरणी का आकार शेष 36 बाइट्स (132 - 96 = 36) तक सीमित नहीं है, अन्यथा आप केवल एक डेटा पेज पर अधिकतम 18 पंक्तियों को लगाने के लिए प्रभावी रूप से सीमित होंगे। इसका मतलब है कि प्रत्येक पंक्ति 2 बाइट्स से बड़ी है जितना आपको लगता है कि यह है। यह मान "रिकॉर्ड आकार" में शामिल नहीं है DBCC PAGE, जैसा कि रिपोर्ट किया गया है , यही वजह है कि इसे नीचे प्रति-पंक्ति जानकारी में शामिल किए जाने के बजाय यहां अलग रखा गया है।
    • प्रति-पंक्ति मेटा-डेटा (सहित, लेकिन सीमित नहीं):
      • आकार तालिका की परिभाषा (स्तंभों की संख्या, चर-लंबाई या निश्चित लंबाई, आदि) के आधार पर भिन्न होता है। @ पॉल व्हाईट और @ आरोन की टिप्पणियों से ली गई जानकारी जो इस उत्तर और परीक्षण से संबंधित चर्चा में पाई जा सकती है ।
      • पंक्ति-शीर्षलेख: 4 बाइट्स, उनमें से 2 रिकॉर्ड प्रकार को दर्शाते हैं, और अन्य दो NULL बिटमैप के लिए एक ऑफसेट हैं
      • कॉलम की संख्या: 2 बाइट्स
      • पूर्ण बिटमैप: वर्तमान में कौन से कॉलम हैं NULL। 8 कॉलम के प्रत्येक सेट पर 1 बाइट। और सभी स्तंभों के लिए, यहां तक ​​कि NOT NULLलोगों के लिए भी । इसलिए, न्यूनतम 1 बाइट।
      • चर-लंबाई स्तंभ ऑफ़सेट सरणी: 4 बाइट न्यूनतम। 2 बाइट्स चर-लंबाई स्तंभों की संख्या को रखने के लिए, और फिर प्रत्येक बाइट-लेंथ कॉलम के प्रति 2 बाइट्स जहां से शुरू होती है, उस पर होल्ड करने के लिए।
      • संस्करण जानकारी: 14 बाइट्स (यह तब मौजूद होगा जब आपका डेटाबेस ALLOW_SNAPSHOT_ISOLATION ONया तो सेट हो गया हो या READ_COMMITTED_SNAPSHOT ON)।
    • कृपया इस पर अधिक विवरण के लिए निम्न प्रश्न और उत्तर देखें: स्लॉट अर्रे और कुल पृष्ठ का आकार
    • कृपया पॉल रान्डेल से निम्नलिखित ब्लॉग पोस्ट देखें जिसमें डेटा पेज कैसे रखे गए हैं, इस बारे में कई दिलचस्प विवरण हैं: DBCC पेज के साथ पोकिंग (भाग 1?)
  • डेटा के लिए LOB संकेत जो पंक्ति में संग्रहीत नहीं है। ताकि DATALENGTH+ के लिए खाता होगा । लेकिन ये एक मानक आकार के नहीं हैं। कृपया इस जटिल विषय पर जानकारी के लिए निम्नलिखित ब्लॉग पोस्ट देखें: (MAX) प्रकार जैसे वरचर, वरीनेन्सी, आदि के लिए LOB सूचक का आकार क्या है? । उस लिंक किए गए पोस्ट और कुछ अतिरिक्त परीक्षण के बीच , मैंने (डिफ़ॉल्ट) नियम निम्नानुसार होने चाहिए:

    • विरासत / पदावनत LOB प्रकार है कि कोई भी (SQL सर्वर 2005 के रूप में अब और का उपयोग करना चाहिए TEXT, NTEXTऔर IMAGE):
      • डिफ़ॉल्ट रूप से, हमेशा अपने डेटा को LOB पृष्ठों पर संग्रहीत करते हैं और हमेशा LOB संग्रहण के लिए 16-बाइट पॉइंटर का उपयोग करते हैं।
      • यदि विकल्प सेट करने के लिए if sp_tableoption का उपयोग किया गया था text in row, तो:
        • यदि मान को संग्रहीत करने के लिए पृष्ठ पर जगह है, और मान अधिकतम-पंक्ति आकार (256 के डिफ़ॉल्ट के साथ 24 - 7000 बाइट्स की विन्यास योग्य सीमा) से अधिक नहीं है, तो इसे इन-पंक्ति में संग्रहीत किया जाएगा;
        • वरना यह 16-बाइट पॉइंटर होगा।
    • नए LOB प्रकार एसक्यूएल सर्वर 2005 में शुरू के लिए ( VARCHAR(MAX), NVARCHAR(MAX), और VARBINARY(MAX)):
      • डिफ़ॉल्ट रूप से:
        • यदि मूल्य 8000 बाइट्स से अधिक नहीं है, और पृष्ठ पर कमरा है, तो इसे इन-रो में संग्रहीत किया जाएगा।
        • इनलाइन रूट - 8001 से 40,000 (वास्तव में 42,000) बाइट्स, अंतरिक्ष अनुमति के बीच डेटा के लिए, 1 से 5 पॉइंटर्स (24 - 72 बाइट्स) होंगे जो कि सीधे LOB पेज (ओं) को इंगित करते हैं। प्रारंभिक 8k LOB पेज के लिए 24 बाइट्स, और प्रत्येक अतिरिक्त 8k पेज पर 12 बाइट्स चार से अधिक 8k पेजों के लिए।
        • TEXT_TREE - 42,000 बाइट्स के डेटा के लिए, या यदि 1 से 5 पॉइंटर्स-इन-रो फिट नहीं हो सकते हैं, तो LOB पेज (यानी "text_tree" पॉइंटर्स की सूची के शुरुआती पृष्ठ पर सिर्फ 24 बाइट पॉइंटर होगा। " पृष्ठ)।
      • यदि विकल्प सेट करने के लिए sp_tableoption का उपयोग किया गया था large value types out of row, तो हमेशा LOB स्टोरेज के लिए 16-बाइट पॉइंटर का उपयोग करें।
    • मैंने "डिफ़ॉल्ट" नियमों को कहा क्योंकि मैंने कुछ विशेषताओं जैसे डेटा संपीड़न, स्तंभ-स्तरीय एन्क्रिप्शन, पारदर्शी डेटा एन्क्रिप्शन, हमेशा एन्क्रिप्ट किए गए, आदि के प्रभाव में इन-पंक्ति मानों का परीक्षण नहीं किया था ।
  • LOB अतिप्रवाह पृष्ठ: यदि कोई मान 10k है, तो इसके लिए 1 पूर्ण 8k पृष्ठ अतिप्रवाह की आवश्यकता होगी, और फिर 2 पृष्ठ का भाग। यदि कोई अन्य डेटा शेष स्थान नहीं ले सकता है (या मुझे अनुमति नहीं है, तो मैं उस नियम के बारे में अनिश्चित हूं), तो आपके पास उस 2 LOB ओवरफ्लो डेटापेज पर "बर्बाद" अंतरिक्ष के 6kb लगभग है।

  • अप्रयुक्त स्थान: एक 8k डेटा पेज सिर्फ इतना है: 8192 बाइट्स। यह आकार में भिन्न नहीं होता है। इस पर रखा गया डेटा और मेटा-डेटा, हालांकि, सभी 8192 बाइट्स में हमेशा फिट नहीं होता है। और पंक्तियों को कई डेटा पृष्ठों पर विभाजित नहीं किया जा सकता है। इसलिए यदि आपके पास 100 बाइट्स शेष हैं, लेकिन कोई पंक्ति (या कोई पंक्ति जो उस स्थान में फिट नहीं होगी, कई कारकों पर निर्भर करता है) वहां फिट हो सकती है, डेटा पेज अभी भी 8192 बाइट्स ले रहा है, और आपकी दूसरी क्वेरी केवल संख्या की गिनती कर रही है डेटा पृष्ठ। आप इस मूल्य को दो स्थानों पर पा सकते हैं (बस ध्यान रखें कि इस मूल्य का कुछ हिस्सा उस आरक्षित स्थान की कुछ राशि है):

    • DBCC PAGE( db_name, file_id, page_id ) WITH TABLERESULTS;के लिए देखो ParentObject= "पृष्ठ हैडर:" और Field= "m_freeCnt"। Valueक्षेत्र अप्रयुक्त बाइट्स की संख्या है।
    • SELECT buff.free_space_in_bytes FROM sys.dm_os_buffer_descriptors buff WHERE buff.[database_id] = DB_ID(N'db_name') AND buff.[page_id] = page_id;यह "m_freeCnt" द्वारा रिपोर्ट की गई समान मान है। यह DBCC की तुलना में अधिक आसान है क्योंकि इसमें कई पृष्ठ मिल सकते हैं, लेकिन इसके लिए यह भी आवश्यक है कि पृष्ठों को पहले स्थान पर बफर पूल में पढ़ा गया हो।
  • FILLFACTOR<100 द्वारा आरक्षित स्थान। नए बनाए गए पृष्ठ FILLFACTORसेटिंग का सम्मान नहीं करते हैं, लेकिन REBUILD करने से प्रत्येक डेटा पृष्ठ पर वह स्थान आरक्षित हो जाएगा। आरक्षित स्थान के पीछे विचार यह है कि इसका उपयोग गैर-अनुक्रमिक आवेषण और / या अपडेट द्वारा किया जाएगा जो पहले से ही पृष्ठ पर पंक्तियों के आकार का विस्तार करते हैं, चर लंबाई के स्तंभों को थोड़ा और डेटा के साथ अद्यतन किए जाने के कारण (लेकिन पर्याप्त नहीं है पेज-विभाजन)। लेकिन आप आसानी से डेटा पृष्ठों पर स्थान आरक्षित कर सकते हैं जो स्वाभाविक रूप से नई पंक्तियों को कभी नहीं प्राप्त करेंगे और मौजूदा पंक्तियों को कभी भी अपडेट नहीं किया जाएगा, या कम से कम इस तरह से अपडेट नहीं किया जाएगा जो पंक्ति के आकार को बढ़ाएगा।

  • पृष्ठ-विभाजन (विखंडन): पंक्ति को किसी स्थान पर जोड़ने की आवश्यकता है जिसमें पंक्ति के लिए कोई स्थान नहीं है, जिससे पृष्ठ विभाजन हो जाएगा। इस स्थिति में, मौजूदा डेटा का लगभग 50% एक नए पृष्ठ पर ले जाया जाता है और नई पंक्ति को 2 पृष्ठों में से एक में जोड़ा जाता है। लेकिन आपके पास अब थोड़ी अधिक खाली जगह है जिसका हिसाब नहीं है DATALENGTH

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

  • भूत पेज? निश्चित नहीं है कि क्या यह उचित शब्द है, लेकिन कभी-कभी डेटा पेज तब तक हटाए नहीं जाते हैं जब तक कि क्लस्टर सूचकांक का REBUILD नहीं किया जाता है। यह भी अधिक पृष्ठों के लिए खाते की तुलना में DATALENGTHजोड़ देगा। यह आमतौर पर नहीं होना चाहिए, लेकिन मैं कई साल पहले एक बार इसमें चला गया था।

  • स्‍पार्स स्‍तंभ: स्‍पार्स स्‍तंभ उन तालिकाओं में स्थान (अधिकतर निश्चित लंबाई के डेटाटाइप्स) के लिए बचाते हैं जहां NULLएक या अधिक कॉलम के लिए बड़ी% पंक्तियाँ होती हैं । SPARSEविकल्प बनाता है NULLअप 0 बाइट्स (सामान्य फिक्स्ड लंबाई राशि के बजाय, एक के लिए इस तरह के रूप 4 बाइट मान प्रकार INT), लेकिन गैर-शून्य तय-लंबाई प्रकार के लिए एक अतिरिक्त 4 बाइट और के लिए एक चर राशि प्रत्येक ले महत्व देता है चर-लंबाई प्रकार। यहाँ समस्या यह है कि DATALENGTHस्पार्स कॉलम में गैर-पूर्ण मानों के लिए अतिरिक्त 4 बाइट्स शामिल नहीं हैं, इसलिए उन 4 बाइट्स को वापस जोड़ने की आवश्यकता है। आप यह देखने के लिए देख सकते हैं कि क्या कोई SPARSEकॉलम हैं:

    SELECT OBJECT_SCHEMA_NAME(sc.[object_id]) AS [SchemaName],
           OBJECT_NAME(sc.[object_id]) AS [TableName],
           sc.name AS [ColumnName]
    FROM   sys.columns sc
    WHERE  sc.is_sparse = 1;

    और फिर प्रत्येक SPARSEकॉलम के लिए, मूल क्वेरी का उपयोग करने के लिए अपडेट करें:

    SUM(DATALENGTH(FieldN) + 4)

    कृपया ध्यान दें कि मानक 4 बाइट्स में जोड़ने के लिए ऊपर की गणना थोड़ी सरल है क्योंकि यह केवल निश्चित लंबाई के प्रकारों के लिए काम करता है। और, प्रति पंक्ति अतिरिक्त मेटा-डेटा है (जो मैं अभी तक बता सकता हूं) जो डेटा के लिए उपलब्ध स्थान को कम कर देता है, बस कम से कम एक स्पार्स कॉलम होने से। अधिक जानकारी के लिए, कृपया उपयोग विरल कॉलम के लिए MSDN पृष्ठ देखें ।

  • सूचकांक और अन्य (जैसे IAM, PFS, GAM, SGAM, आदि) पृष्ठ: ये उपयोगकर्ता डेटा के संदर्भ में "डेटा" पृष्ठ नहीं हैं। ये तालिका के कुल आकार को बढ़ाएँगे। यदि SQL Server 2012 या नए का उपयोग कर रहे हैं, तो आप sys.dm_db_database_page_allocationsपृष्ठ प्रकार (SQL सर्वर के पुराने संस्करण उपयोग कर सकते हैं DBCC IND(0, N'dbo.table_name', 0);) को देखने के लिए डायनेमिक मैनेजमेंट फ़ंक्शन (DMF) का उपयोग कर सकते हैं :

    SELECT *
    FROM   sys.dm_db_database_page_allocations(
                   DB_ID(),
                   OBJECT_ID(N'dbo.table_name'),
                   1,
                   NULL,
                   N'DETAILED'
                  )
    WHERE  page_type = 1; -- DATA_PAGE

    न तो और DBCC INDन ही sys.dm_db_database_page_allocations(उस WHERE क्लॉज के साथ) कोई भी इंडेक्स पेज रिपोर्ट करेगा, और केवल DBCC INDकम से कम एक IAM पेज रिपोर्ट करेगा।

  • DATA_COMPRESSION: यदि आपने क्लस्टर इंडेक्स या हीप पर सक्षम ROWया PAGEकंप्रेशन को सक्षम किया है, तो आप अभी तक उल्लेखित अधिकांश बातों के बारे में भूल सकते हैं। 96 बाइट पेज हैडर, 2 बाइट्स-प्रति-पंक्ति स्लॉट ऐरे, और 14 बाइट्स-प्रति-पंक्ति संस्करण जानकारी अभी भी हैं, लेकिन डेटा का भौतिक प्रतिनिधित्व अत्यधिक जटिल हो जाता है (पहले से उल्लिखित पहले की तुलना में बहुत अधिक) उपयोग नहीं किया जा रहा है)। उदाहरण के लिए, रो संपीड़न के साथ, SQL सर्वर प्रत्येक पंक्ति में, प्रत्येक कॉलम को फिट करने के लिए सबसे छोटे संभव कंटेनर का उपयोग करने का प्रयास करता है। इसलिए यदि आपके पास एक BIGINTकॉलम है जो अन्यथा होगा (यह मानते हुए SPARSEभी सक्षम नहीं है) हमेशा 8 बाइट्स लें, यदि मूल्य -128 और 127 (यानी 8-बिट पूर्णांक पर हस्ताक्षर किए गए) के बीच है, तो यह सिर्फ 1 बाइट का उपयोग करेगा, और यदि 8 मान एक में फिट हो सकता हैSMALLINT, यह केवल 2 बाइट्स लेगा। पूर्णांक प्रकार जो NULLया तो 0कोई स्थान नहीं लेते हैं और स्तंभों को मैप करने वाले एक सरणी में बस NULL"रिक्त" (यानी 0) होने के रूप में इंगित किए जाते हैं। और कई, कई अन्य नियम हैं। यूनिकोड डेटा ( NCHARऔर NVARCHAR(1 - 4000), लेकिन नहीं NVARCHAR(MAX) , भले ही इन-रो संग्रहीत)? यूनिकोड संपीड़न को SQL सर्वर 2008 R2 में जोड़ा गया था, लेकिन नियमों की जटिलता को देखते हुए वास्तविक संपीड़न किए बिना सभी स्थितियों में "संपीड़ित" मूल्य के परिणाम की भविष्यवाणी करने का कोई तरीका नहीं है

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

"डेटा उपयोग" का निर्धारण करने में दूसरी क्वेरी की सटीकता के बारे में, पृष्ठ हैडर बाइट्स का बैक-आउट करना सबसे उचित लगता है क्योंकि वे डेटा उपयोग नहीं हैं: वे लागत-से-व्यवसाय ओवरहेड हैं। यदि किसी डेटा पृष्ठ पर 1 पंक्ति है और वह पंक्ति सिर्फ एक है TINYINT, तो उस 1 बाइट को अभी भी आवश्यक है कि डेटा पेज मौजूद था और इसलिए हेडर के 96 बाइट्स। क्या पूरे डेटा पृष्ठ के लिए 1 विभाग को शुल्क देना चाहिए? यदि उस डेटा पृष्ठ को विभाग # 2 द्वारा भरा जाता है, तो क्या वे समान रूप से उस "ओवरहेड" लागत को विभाजित करेंगे या आनुपातिक रूप से भुगतान करेंगे? लगता है सबसे आसान बस इसे वापस करने के लिए। किस मामले में, के 8विरुद्ध गुणा करने के मूल्य का उपयोग number of pagesबहुत अधिक है। कैसा रहेगा:

-- 8192 byte data page - 96 byte header = 8096 (approx) usable bytes.
SELECT 8060.0 / 1024 -- 7.906250

इसलिए, कुछ का उपयोग करें:

(SUM(a.total_pages) * 7.91) / 1024 AS [TotalSpaceMB]

"नंबर_ऑफ_पेज" कॉलम के खिलाफ सभी गणनाओं के लिए।

और , यह विचार करते हुए कि DATALENGTHप्रत्येक क्षेत्र का उपयोग करके प्रति पंक्ति मेटा-डेटा वापस नहीं किया जा सकता है, जिसे आपकी प्रति-तालिका क्वेरी में जोड़ा जाना चाहिए, जहां आपको DATALENGTHप्रत्येक "विभाग" पर फ़िल्टर करते हुए प्रत्येक फ़ील्ड मिलता है :

  • रिकॉर्ड प्रकार और नल बिटमैप को ऑफसेट: 4 बाइट्स
  • कॉलम गणना: 2 बाइट्स
  • स्लॉट ऐरे: 2 बाइट्स ("रिकॉर्ड आकार" में शामिल नहीं है, लेकिन फिर भी इसके लिए खाता होना चाहिए)
  • पूर्ण बिटमैप: प्रत्येक 8 कॉलम के लिए 1 बाइट ( सभी कॉलम के लिए)
  • पंक्ति संशोधन: 14 बाइट (डेटाबेस या तो है, तो ALLOW_SNAPSHOT_ISOLATIONया READ_COMMITTED_SNAPSHOTकरने के लिए सेट ON)
  • वैरिएबल-लेंथ कॉलम ऑफसेट एरे: 0 बाइट्स यदि सभी कॉलम फिक्स्ड-लेंथ हैं। यदि कोई कॉलम परिवर्तनशील-लंबाई वाला है, तो 2 बाइट्स, प्लस 2 बाइट्स केवल प्रत्येक चर-लंबाई वाले कॉलम हैं।
  • LOB पॉइंटर्स: यह हिस्सा बहुत इंप्रेसिव है क्योंकि वैल्यू होने पर कोई पॉइंटर नहीं होगा NULL, और अगर वैल्यू रो पर फिट होती है तो यह पॉइंटर की तुलना में बहुत छोटा या बहुत बड़ा हो सकता है, और अगर वैल्यू को स्टोर किया जाता है- पंक्ति, तो पॉइंटर का आकार इस बात पर निर्भर हो सकता है कि कितना डेटा है। हालाँकि, जब से हम सिर्फ एक अनुमान (यानी "स्वैग") चाहते हैं, ऐसा लगता है कि 24 बाइट्स का उपयोग करने के लिए एक अच्छा मूल्य है (अच्छी तरह से, किसी भी अन्य के रूप में अच्छा ;-)। यह प्रत्येक MAXक्षेत्र है।

इसलिए, कुछ का उपयोग करें:

  • सामान्य तौर पर (पंक्ति शीर्षलेख + स्तंभों की संख्या + स्लॉट सरणी + NULL बिटमैप):

    ([RowCount] * (( 4 + 2 + 2 + (1 + (({NumColumns} - 1) / 8) ))
  • सामान्य तौर पर (यदि "संस्करण की जानकारी" मौजूद है तो ऑटो-डिटेक्ट करें):

    + (SELECT CASE WHEN snapshot_isolation_state = 1 OR is_read_committed_snapshot_on = 1
                     THEN 14 ELSE 0 END FROM sys.databases WHERE [database_id] = DB_ID())
  • यदि कोई चर-लंबाई वाले स्तंभ हैं, तो जोड़ें:

    + 2 + (2 * {NumVariableLengthColumns})
  • यदि कोई MAX/ LOB कॉलम हैं, तो जोड़ें:

    + (24 * {NumLobColumns})
  • सामान्य रूप में:

    )) AS [MetaDataBytes]

यह सटीक नहीं है, और फिर से काम नहीं करेगा यदि आपके पास हीप या क्लस्टर इंडेक्स पर सक्षम पंक्ति या पृष्ठ संपीड़न है, लेकिन निश्चित रूप से आपको पास होना चाहिए।


अद्यतन 15% अंतर रहस्य के बारे में

हम (स्वयं शामिल हैं) यह सोचने पर केंद्रित थे कि डेटा पृष्ठ कैसे रखे जाते हैं और DATALENGTHउन चीज़ों के लिए कैसे हिसाब- किताब किया जा सकता है जिन्हें हमने 2 जी क्वेरी की समीक्षा किए बहुत समय व्यतीत नहीं किया। मैंने उस क्वेरी को एक ही टेबल के खिलाफ चलाया और फिर उन मूल्यों की तुलना की, जिनके द्वारा रिपोर्ट की जा रही थी sys.dm_db_database_page_allocationsऔर वे पृष्ठों की संख्या के लिए समान मान नहीं थे। कूबड़ पर, मैंने कुल कार्यों को हटा दिया और GROUP BY, और SELECTसूची को बदल दिया a.*, '---' AS [---], p.*। और फिर यह स्पष्ट हो गया: लोगों को सावधान रहना चाहिए, जहां इन घिनौने अंतराल पर उन्हें अपनी जानकारी और स्क्रिप्ट ;-); प्रश्न में पोस्ट की गई 2 क्वेरी बिल्कुल सही नहीं है, विशेष रूप से इस विशेष प्रश्न के लिए।

  • छोटी समस्या: इसके बाहर GROUP BY rows(और एक समग्र कार्य में उस स्तंभ का नहीं होना) से कोई मतलब नहीं है, जो बीच में है sys.allocation_unitsऔर sys.partitionsतकनीकी रूप से सही नहीं है। आवंटन इकाइयों के 3 प्रकार हैं, और उनमें से एक को एक अलग क्षेत्र में शामिल होना चाहिए। अक्सर partition_idऔर hobt_idसमान होते हैं, इसलिए कभी कोई समस्या नहीं हो सकती है, लेकिन कभी-कभी उन दो क्षेत्रों में अलग-अलग मूल्य होते हैं।

  • प्रमुख समस्या: क्वेरी used_pagesफ़ील्ड का उपयोग करती है । वह क्षेत्र सभी प्रकार के पृष्ठों को कवर करता है : डेटा, इंडेक्स, आईएएम, आदि, टीसी। केवल वास्तविक डेटा से संबंधित होने पर उपयोग करने के लिए एक और, अधिक उपयुक्त क्षेत्र है data_pages:।

मैंने उपरोक्त मदों के साथ प्रश्न में 2 की क्वेरी को ध्यान में रखते हुए, और पृष्ठ शीर्ष लेख का समर्थन करने वाले डेटा पृष्ठ आकार का उपयोग किया। मैंने दो JOIN को भी हटा दिया जो अनावश्यक थे: sys.schemas(कॉल के साथ बदल दिया गया SCHEMA_NAME()), और sys.indexes(Clustered Index is always index_id = 1and we have index_idin sys.partitions)।

SELECT  SCHEMA_NAME(st.[schema_id]) AS [SchemaName],
        st.[name] AS [TableName],
        SUM(sp.[rows]) AS [RowCount],
        (SUM(sau.[total_pages]) * 8.0) / 1024 AS [TotalSpaceMB],
        (SUM(CASE sau.[type]
           WHEN 1 THEN sau.[data_pages]
           ELSE (sau.[used_pages] - 1) -- back out the IAM page
         END) * 7.91) / 1024 AS [TotalActualDataMB]
FROM        sys.tables st
INNER JOIN  sys.partitions sp
        ON  sp.[object_id] = st.[object_id]
INNER JOIN  sys.allocation_units sau
        ON  (   sau.[type] = 1
            AND sau.[container_id] = sp.[partition_id]) -- IN_ROW_DATA
        OR  (   sau.[type] = 2
            AND sau.[container_id] = sp.[hobt_id]) -- LOB_DATA
        OR  (   sau.[type] = 3
            AND sau.[container_id] = sp.[partition_id]) -- ROW_OVERFLOW_DATA
WHERE       st.is_ms_shipped = 0
--AND         sp.[object_id] = OBJECT_ID(N'dbo.table_name')
AND         sp.[index_id] < 2 -- 1 = Clustered Index; 0 = Heap
GROUP BY    SCHEMA_NAME(st.[schema_id]), st.[name]
ORDER BY    [TotalSpaceMB] DESC;

टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
पॉल व्हाइट 9

हालाँकि आपने 2 क्वेरी के लिए प्रदान की गई अपडेट क्वेरी को और भी दूर रखा है (अब दूसरी दिशा में :)), मैं इस उत्तर के साथ ठीक हूं। यह स्पष्ट रूप से दरार करने के लिए एक बहुत ही कठिन नट है और इसके लायक है, मुझे खुशी है कि विशेषज्ञों की मदद करने के बावजूद मैं अभी भी यह पता लगाने में असमर्थ था कि दो तरीकों के मेल नहीं खा रहे हैं। मैं अन्य उत्तर देने के लिए सिर्फ कार्यप्रणाली का उपयोग करने जा रहा हूं। काश मैं इन दोनों जवाबों के लिए हाँ कर सकता था, लेकिन @srutzky ने सभी कारणों से मदद की कि दोनों क्यों बंद होंगे।
क्रिस वुड्स

6

हो सकता है कि यह एक मुंहतोड़ जवाब हो लेकिन मैं यही करूंगा।

इसलिए DATALENGTH का कुल खाते में केवल 86% हिस्सा है। यह अभी भी बहुत ही प्रतिनिधि विभाजन है। सरज़्ज़की से उत्कृष्ट जवाब में ओवरहेड एक सुंदर भी विभाजित होना चाहिए।

मैं कुल के लिए आपकी दूसरी क्वेरी (पृष्ठ) का उपयोग करूंगा। और विभाजन को आवंटित करने के लिए पहले (डटलैट्रिक्स) का उपयोग करें। एक सामान्यीकरण का उपयोग करके कई लागतें आवंटित की जाती हैं।

और आपको विचार करना होगा कि एक करीबी जवाब लागत बढ़ाने वाला है, यहां तक ​​कि एक विभाजन पर खो जाने वाले विभाग अभी भी अधिक भुगतान कर सकते हैं।

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