एक साधारण CCI पंक्ति समूह बनाने में 30 सेकंड तक का समय क्यों लग सकता है?


20

मैं सीसीआई से जुड़े एक डेमो पर काम कर रहा था जब मैंने देखा कि मेरे कुछ आवेषण अपेक्षा से अधिक समय ले रहे थे। पुन: पेश करने के लिए टेबल परिभाषाएँ:

DROP TABLE IF EXISTS dbo.STG_1048576;
CREATE TABLE dbo.STG_1048576 (ID BIGINT NOT NULL);
INSERT INTO dbo.STG_1048576
SELECT TOP (1048576) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

परीक्षण के लिए मैं स्टेजिंग टेबल से सभी 1048576 पंक्तियों को सम्मिलित कर रहा हूं। जब तक यह किसी कारण के लिए छंटनी नहीं करता है, तब तक बिल्कुल एक पंक्तिबद्ध समूह को भरने के लिए पर्याप्त है।

अगर मैं पूर्णांक मॉड 17000 में सम्मिलित करता हूं तो यह एक सेकंड से भी कम समय लगता है:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 17000
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

SQL सर्वर निष्पादन समय: CPU समय = 359 ms, बीता समय = 364 ms।

हालाँकि, अगर मैं एक ही पूर्णांक mod ​​16000 डालूं तो कभी-कभी इसे 30 सेकंड से अधिक समय लगता है:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16000
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

SQL सर्वर निष्पादन समय: CPU समय = 32062 ms, बीता हुआ समय = 32511 ms।

यह एक दोहराने योग्य परीक्षण है जो कई मशीनों पर किया गया है। बीते समय में एक स्पष्ट पैटर्न प्रतीत होता है क्योंकि मॉड वैल्यू में परिवर्तन होता है:

MOD_NUM TIME_IN_MS
1000    2036
2000    3857
3000    5463
4000    6930
5000    8414
6000    10270
7000    12350
8000    13936
9000    17470
10000   19946
11000   21373
12000   24950
13000   28677
14000   31030
15000   34040
16000   37000
17000   563
18000   583
19000   576
20000   584

आप परीक्षण चलाने के लिए चाहते हैं, तो अपने आप को परीक्षण कोड है कि मैंने लिखा संशोधित करने के लिए स्वतंत्र लग रहा है यहाँ

मैं mod 16000 डालने के लिए sysinos_os_wait_stats में कुछ भी दिलचस्प नहीं मिला:

╔════════════════════════════════════╦══════════════╗
             wait_type               diff_wait_ms 
╠════════════════════════════════════╬══════════════╣
 XE_DISPATCHER_WAIT                        164406 
 QDS_PERSIST_TASK_MAIN_LOOP_SLEEP          120002 
 LAZYWRITER_SLEEP                           97718 
 LOGMGR_QUEUE                               97298 
 DIRTY_PAGE_POLL                            97254 
 HADR_FILESTREAM_IOMGR_IOCOMPLETION         97111 
 SQLTRACE_INCREMENTAL_FLUSH_SLEEP           96008 
 REQUEST_FOR_DEADLOCK_SEARCH                95001 
 XE_TIMER_EVENT                             94689 
 SLEEP_TASK                                 48308 
 BROKER_TO_FLUSH                            48264 
 CHECKPOINT_QUEUE                           35589 
 SOS_SCHEDULER_YIELD                           13 
╚════════════════════════════════════╩══════════════╝

इन्सर्ट के लिए इन्सर्ट ID % 16000इतने अधिक समय तक क्यों लेता है ID % 17000?

जवाबों:


12

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

वर्टीकैप एक मालिकाना कार्यान्वयन है, और विवरण एक बारीकी से संरक्षित रहस्य है, क्योंकि आप यहां एक उत्तर देने जा रहे हैं, इसका एक निश्चित सीमा है। फिर भी, हम जानते हैं कि VertiPaq में दिनचर्या शामिल है:

  • मूल्य एन्कोडिंग (स्केलिंग और / या थोड़ी सी संख्या में फिट होने के लिए मूल्यों का अनुवाद)
  • शब्दकोश एन्कोडिंग (अद्वितीय मूल्यों के पूर्णांक संदर्भ)
  • रन लें एन्कोडिंग (बार-बार मानों के रनों को संग्रहीत करता है [मान, गणना] जोड़े)
  • बिट-पैकिंग (स्ट्रीम को यथासंभव कुछ बिट्स में संग्रहीत करना)

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

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

डब्ल्यूपीए विश्लेषण

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

सबसे लंबी अवधि तब होती है, जब 64 पुनरावृत्तियों के साथ विभिन्न मूल्यों की अधिकतम संख्या सबसे बड़े संभावित खंड में दिखाई देती है, अर्थात 1,048,576 पंक्तियों में 16,384 सेट प्रत्येक के साथ 64 प्रविष्टियों के साथ होते हैं। कोड के निरीक्षण से महंगी प्रसंस्करण के लिए एक कठिन-कोडित समय सीमा का पता चलता है। इसे अन्य वर्टीकैप कार्यान्वयन जैसे एसएसएएस में कॉन्फ़िगर किया जा सकता है, लेकिन एसक्यूएल सर्वर में नहीं जहां तक ​​मैं बता सकता हूं।

अंतिम भंडारण व्यवस्था में कुछ अंतर्दृष्टि को अनिर्दिष्ट DBCC CSINDEXकमांड का उपयोग करके अधिग्रहण किया जा सकता है । यह RLE हैडर और सरणी प्रविष्टियाँ, RLE डेटा के किसी भी बुकमार्क और बिट-पैक डेटा (यदि हो तो) का एक संक्षिप्त सारांश दिखाता है।

अधिक जानकारी के लिए देखें:


9

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

पहले मैंने सीसीआई में डाली गई पंक्तियों की संख्या को अलग-अलग करके आजमाया TOP। मैंने ID % 16000सभी परीक्षणों के लिए उपयोग किया । नीचे एक पंक्ति की तुलना की गई पंक्ति है जो संपीड़ित पंक्ति समूह खंड आकार में सम्मिलित की गई है:

शीर्ष बनाम आकार का ग्राफ

नीचे ms में CPU समय के लिए डाली गई पंक्तियों का एक ग्राफ है। ध्यान दें कि X- अक्ष में एक अलग प्रारंभिक बिंदु है:

शीर्ष बनाम सीपीयू

हम देख सकते हैं कि पंक्ति समूह खंड आकार रेखीय दर पर बढ़ता है और लगभग 1 M पंक्तियों तक CPU की थोड़ी मात्रा का उपयोग करता है। उस बिंदु पर पंक्ति समूह आकार नाटकीय रूप से कम हो जाता है और CPU उपयोग नाटकीय रूप से बढ़ जाता है। ऐसा प्रतीत होता है कि हम उस संपीड़न के लिए सीपीयू में भारी कीमत चुकाते हैं।

1024000 से कम पंक्तियों को सम्मिलित करते समय, मैं CCI में एक खुली पंक्ति समूह के साथ समाप्त हुआ। हालांकि, उपयोग करने के लिए मजबूर मजबूर REORGANIZEया REBUILDआकार पर प्रभाव नहीं था। एक तरफ के रूप में, मुझे यह दिलचस्प लगा कि जब मैंने एक चर का उपयोग किया तो मैं TOPएक खुली पंक्ति समूह के साथ RECOMPILEसमाप्त हो गया लेकिन जब मैं एक बंद पंक्ति समूह के साथ समाप्त हुआ।

अगला मैंने पंक्तियों की संख्या को समान रखते हुए मापांक मान को अलग करके परीक्षण किया। यहां 102400 पंक्तियों को सम्मिलित करते समय डेटा का एक नमूना दिया गया है:

╔═══════════╦═════════╦═══════════════╦═════════════╗
 TOP_VALUE  MOD_NUM  SIZE_IN_BYTES  CPU_TIME_MS 
╠═══════════╬═════════╬═══════════════╬═════════════╣
    102400     1580          13504          352 
    102400     1590          13584          316 
    102400     1600          13664          317 
    102400     1601          19624          270 
    102400     1602          25568          283 
    102400     1603          31520          286 
    102400     1604          37464          288 
    102400     1605          43408          273 
    102400     1606          49360          269 
    102400     1607          55304          265 
    102400     1608          61256          262 
    102400     1609          67200          255 
    102400     1610          73144          265 
    102400     1620         132616          132 
    102400     1621         138568          100 
    102400     1622         144512           91 
    102400     1623         150464           75 
    102400     1624         156408           60 
    102400     1625         162352           47 
    102400     1626         164712           41 
╚═══════════╩═════════╩═══════════════╩═════════════╝

1600 तक के एक मॉड वैल्यू तक, प्रत्येक अतिरिक्त 10 अद्वितीय मानों के लिए पंक्ति समूह खंड आकार 80 बाइट्स से रैखिक रूप से बढ़ता है। यह एक दिलचस्प संयोग है कि BIGINTपारंपरिक रूप से 8 बाइट्स लगते हैं और प्रत्येक अतिरिक्त अद्वितीय मूल्य के लिए सेगमेंट का आकार 8 बाइट्स से बढ़ जाता है। जब तक यह स्थिर नहीं हो जाता, तब तक खंड का आकार 1600 के एक मध्यम मूल्य में तेजी से बढ़ता है।

मापांक मान को छोड़ते समय और सम्मिलित पंक्तियों की संख्या में परिवर्तन करते समय डेटा को देखना भी मददगार होता है:

╔═══════════╦═════════╦═══════════════╦═════════════╗
 TOP_VALUE  MOD_NUM  SIZE_IN_BYTES  CPU_TIME_MS 
╠═══════════╬═════════╬═══════════════╬═════════════╣
    300000     5000         600656          131 
    305000     5000         610664          124 
    310000     5000         620672          127 
    315000     5000         630680          132 
    320000     5000          40688         2344 
    325000     5000          40696         2577 
    330000     5000          40704         2589 
    335000     5000          40712         2673 
    340000     5000          40728         2715 
    345000     5000          40736         2744 
    350000     5000          40744         2157 
╚═══════════╩═════════╩═══════════════╩═════════════╝

ऐसा लगता है जब पंक्तियों की सम्मिलित संख्या <~ 64 * अद्वितीय मानों की संख्या है जो हम अपेक्षाकृत खराब संपीड़न देखते हैं (मॉड <= 65000 के लिए प्रति पंक्ति 2 बाइट्स) और कम, रैखिक सीपीयू उपयोग। जब पंक्तियों की सम्मिलित संख्या> ~ 64 * अनूठे मूल्यों की संख्या हम बेहतर संपीड़न और उच्चतर, अभी भी रैखिक सीपीयू उपयोग देखते हैं। दो राज्यों के बीच एक संक्रमण है जो मेरे लिए मॉडल करना आसान नहीं है लेकिन इसे ग्राफ में देखा जा सकता है। यह सच प्रतीत नहीं होता है कि हम प्रत्येक अनूठे मूल्य के लिए ठीक 64 पंक्तियों को सम्मिलित करते समय अधिकतम सीपीयू उपयोग को देखते हैं। इसके बजाय, हम केवल एक पंक्ति समूह में अधिकतम 1048576 पंक्तियाँ सम्मिलित कर सकते हैं और एक बार अनूठे मूल्य पर 64 से अधिक पंक्तियाँ होने पर हम बहुत अधिक CPU उपयोग और संपीड़न देखते हैं।

नीचे एक समोच्च प्लॉट है कि कैसे सीपीयू समय सम्मिलित पंक्तियों की संख्या और अद्वितीय पंक्तियों की संख्या में परिवर्तन होता है। हम ऊपर वर्णित पैटर्न देख सकते हैं:

समोच्च सीपीयू

नीचे खंड द्वारा उपयोग किए जाने वाले स्थान का एक समोच्च भूखंड है। एक निश्चित बिंदु के बाद हम बहुत बेहतर संपीड़न देखना शुरू करते हैं, जैसा कि ऊपर वर्णित है:

समोच्च आकार

ऐसा लगता है कि यहां काम पर कम से कम दो अलग-अलग संपीड़न एल्गोरिदम हैं। उपरोक्त को देखते हुए, यह समझ में आता है कि हम 1048576 पंक्तियों को सम्मिलित करते समय अधिकतम सीपीयू उपयोग देखेंगे। यह भी समझ में आता है कि हम लगभग 16000 पंक्तियों को सम्मिलित करते समय उस बिंदु पर सबसे अधिक सीपीयू उपयोग देखते हैं। 1048576/64 = 16384।

यदि कोई इसका विश्लेषण करना चाहता है तो मैंने अपने सभी कच्चे डेटा यहाँ अपलोड कर दिए हैं।

यह उल्लेखनीय है कि समानांतर योजनाओं के साथ क्या होता है। मैंने केवल समान रूप से वितरित मूल्यों के साथ इस व्यवहार का अवलोकन किया। जब एक समानांतर सम्मिलित करते हैं तो अक्सर यादृच्छिकता का एक तत्व होता है और धागे आमतौर पर असंतुलित होते हैं।

मंचन तालिका में 2097152 पंक्तियाँ डालें:

DROP TABLE IF EXISTS STG_2097152;
CREATE TABLE dbo.STG_2097152 (ID BIGINT NOT NULL);
INSERT INTO dbo.STG_2097152 WITH (TABLOCK)
SELECT TOP (2097152) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

यह इंसर्ट एक सेकंड से भी कम समय में खत्म हो जाता है और इसमें खराब संपीड़न होता है:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16000
FROM dbo.STG_2097152 
OPTION (MAXDOP 2);

हम असंतुलित धागों के प्रभाव को देख सकते हैं:

╔════════════╦════════════╦══════════════╦═══════════════╗
 state_desc  total_rows  deleted_rows  size_in_bytes 
╠════════════╬════════════╬══════════════╬═══════════════╣
 OPEN             13540             0         311296 
 COMPRESSED     1048576             0        2095872 
 COMPRESSED     1035036             0        2070784 
╚════════════╩════════════╩══════════════╩═══════════════╝

विभिन्न चालें हैं जो हम थ्रेड्स को संतुलित करने और पंक्तियों के समान वितरण के लिए बाध्य कर सकते हैं। उनमें से एक यहां पर है:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT FLOOR(0.5 * ROW_NUMBER() OVER (ORDER BY (SELECT NULL)))  % 15999
FROM dbo.STG_2097152
OPTION (MAXDOP 2)

मापांक के लिए विषम संख्या चुनना यहाँ महत्वपूर्ण है। SQL सर्वर सीरियल में स्टेजिंग टेबल को स्कैन करता है, पंक्ति संख्या की गणना करता है, फिर समानांतर थ्रेड्स पर पंक्तियों को लगाने के लिए राउंड रॉबिन वितरण का उपयोग करता है। इसका मतलब है कि हम पूरी तरह से संतुलित धागे के साथ समाप्त करेंगे।

संतुलन 1

इन्सर्ट में लगभग 40 सेकंड लगते हैं जो कि सीरियल इंसर्ट के समान है। हम अच्छी तरह से संकुचित rowgroups मिल:

╔════════════╦════════════╦══════════════╦═══════════════╗
 state_desc  total_rows  deleted_rows  size_in_bytes 
╠════════════╬════════════╬══════════════╬═══════════════╣
 COMPRESSED     1048576             0         128568 
 COMPRESSED     1048576             0         128568 
╚════════════╩════════════╩══════════════╩═══════════════╝

हम मूल चरण तालिका से डेटा सम्मिलित करके समान परिणाम प्राप्त कर सकते हैं:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT t.ID % 16000 ID
FROM  (
    SELECT TOP (2) ID 
    FROM (SELECT 1 ID UNION ALL SELECT 2 ) r
) s
CROSS JOIN dbo.STG_1048576 t
OPTION (MAXDOP 2, NO_PERFORMANCE_SPOOL);

यहां राउंड रॉबिन वितरण का उपयोग व्युत्पन्न तालिका के लिए किया जाता है, sइसलिए तालिका का एक स्कैन प्रत्येक समानांतर धागे पर किया जाता है:

संतुलित २

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


8

मुझे विश्वास है, कि यह एकल स्तंभ तालिकाओं के लिए संपीड़न की आंतरिक आशाओं और शब्दकोश द्वारा कब्जा किए गए 64 KB की जादुई संख्या के साथ करना है।

उदाहरण: यदि आप MOD 16600 के साथ चलते हैं , तो पंक्ति समूह के आकार का अंतिम परिणाम 1.683 MB होगा , जबकि MOD 17000 को चलाने पर आपको 2.001 MB के आकार के साथ एक पंक्ति समूह मिलेगा ।

अब, बनाए गए शब्दकोशों पर एक नज़र डालें (आप इसके लिए मेरी CISL लाइब्रेरी का उपयोग कर सकते हैं , आपको फ़ंक्शन cstore_GetD शब्दकोशों की आवश्यकता होगी, या वैकल्पिक रूप से जाएं और क्वेरी करें sys.column_store_d शब्दकोशों DMV):

(मॉड 16600) 61 केबी

यहां छवि विवरण दर्ज करें

(MOD 17000) 65 केबी

यहां छवि विवरण दर्ज करें

मजेदार बात, अगर आप अपनी तालिका में एक और कॉलम जोड़ेंगे, और चलिए इसे REALID कहते हैं:

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, REALID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);

MOD 16600 के लिए डेटा पुनः लोड करें:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16600, ID
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

इस बार निष्पादन तेजी से होगा, क्योंकि ऑप्टिमाइज़र इसे बहुत दूर करने और संपीड़ित न करने का फैसला करेगा:

select column_id, segment_id, cast(sum(seg.on_disk_size) / 1024. / 1024 as Decimal(8,3) ) as SizeInMB
    from sys.column_store_segments seg
        inner join sys.partitions part
            on seg.hobt_id = part.hobt_id 
    where object_id = object_id('dbo.CCI_BIGINT')
    group by column_id, segment_id;

भले ही रो समूह के आकारों के बीच एक छोटा सा अंतर होगा, यह नगण्य होगा (2.000 (MOD 16600) बनाम 2.001 (MOD 17000))

इस परिदृश्य के लिए, MOD 16000 का शब्दकोश 1 कॉलम (0.63 बनाम 0.61) के साथ पहले परिदृश्य के लिए बड़ा होगा।

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