कुशल रेंज एग्रीगेट प्रश्नों के लिए डेटाबेस?


11

एक सरल उदाहरण के रूप में, मान लीजिए कि मेरे पास इस तरह की एक तालिका है:

seq | value
----+------
102 | 11954
211 | 43292
278 | 19222
499 |  3843

तालिका में सैकड़ों लाखों रिकॉर्ड हो सकते हैं, और मुझे अक्सर इस तरह के प्रश्न करने होते हैं:

SELECT sum(value) WHERE seq > $a and seq < $b

यदि seqअनुक्रमित किया जाता है, तब भी एक विशिष्ट डेटाबेस कार्यान्वयन प्रत्येक पंक्ति के माध्यम से सबसे अच्छे मामले में योग की गणना करने के लिए लूप करेगा O(n), जहां nसीमा का आकार है।

क्या कोई ऐसा डेटाबेस है जो O(log(n))प्रति प्रश्न के अनुसार कुशलतापूर्वक ऐसा कर सकता है ?

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

हालाँकि, मैं ऐसे किसी भी डेटाबेस में नहीं आया हूं जो इस तरह की डेटा संरचना को लागू करता है। इन-स्क्रैच से इसे लागू करना एक इन-मेमोरी संरचना के लिए आसान है, लेकिन यह मुश्किल हो जाता है अगर इसे जारी रखना है या मेमोरी में फिट होने के लिए बहुत बड़ा है। यदि मौजूदा डेटाबेस के शीर्ष पर इसे लागू करने के लिए एक कुशल पैटर्न है, तो यह भी मदद कर सकता है।

साइड नोट: यह एक परिशिष्ट-केवल तालिका नहीं है, इसलिए इस मामले में एक संचयी योग रखने जैसा समाधान काम नहीं करेगा।


यह कॉलम-संगठित डेटाबेस के लिए विशिष्ट उपयोग का मामला है, जिनमें से कई हैं
मसिआस्को

यहां तक ​​कि स्तंभ-संगठित डेटाबेस को अभी भी n पंक्तियों को स्कैन करने के लिए O (n) समय की आवश्यकता होगी। उस ने कहा, कई कॉलम-संगठित डेटाबेस ऐसे प्रश्नों को समानांतर करने में बहुत अच्छे हैं, इसलिए यह ऐसे डेटाबेस पर बहुत तेजी से चलेगा।
ब्रायन

जवाबों:


8

SQL सर्वर ColumnStore इंडेक्स का उपयोग करना

ठीक है, ठीक है, बस एक - एक संकुल सीएस सूचकांक।

आप हार्डवेयर मैं तारीख को किया बारे में पढ़ना चाहते हैं, तो सिर यहाँ पर । पूर्ण प्रकटीकरण, मैंने उस कंपनी की वेबसाइट पर ब्लॉग पोस्ट लिखी जिसके लिए मैं काम करता हूं।

परीक्षण पर!

यहाँ एक बहुत बड़ी तालिका बनाने के लिए कुछ सामान्य कोड हैं। इवान के रूप में एक ही चेतावनी, यह निर्माण और सूचकांक में कुछ समय ले सकता है।

USE tempdb

CREATE TABLE t1 (Id INT NOT NULL, Amount INT NOT NULL)

;WITH T (N)
AS ( SELECT X.N
     FROM ( 
      VALUES (NULL), (NULL), (NULL),
             (NULL), (NULL), (NULL),
             (NULL), (NULL), (NULL), 
             (NULL) ) AS X (N) 
           ), NUMS (N) AS ( 
            SELECT TOP ( 710000000 ) 
                    ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL )) AS N
            FROM   T AS T1, T AS T2, T AS T3, 
                   T AS T4, T AS T5, T AS T6, 
                   T AS T7, T AS T8, T AS T9, 
                   T AS T10 )
INSERT dbo.t1 WITH ( TABLOCK ) (
    Id, Amount )
SELECT NUMS.N % 999 AS Id, NUMS.N % 9999 AS Amount
FROM   NUMS;

--(705032704 row(s) affected) --Aw, close enough

खैर, इवान सादगी के लिए जीतता है, लेकिन मुझे लगता है के बारे में बात की है कि पहले।

यहाँ सूचकांक की परिभाषा है। ला और डे और दाह।

CREATE CLUSTERED COLUMNSTORE INDEX CX_WOAHMAMA ON dbo.t1

एक गिनती को देखते हुए, प्रत्येक आईडी में बहुत समान वितरण होता है:

SELECT t.Id, COUNT(*) AS [Records]
FROM dbo.t1 AS t
GROUP BY t.Id
ORDER BY t.Id

परिणाम:

Id  Records
0   5005005
1   5005006
2   5005006
3   5005006
4   5005006
5   5005006

...

994 5005005
995 5005005
996 5005005
997 5005005
998 5005005

हर ईद के साथ ~ 5,005,005 पंक्तियों के साथ, हम आपको 10 मिलियन पंक्ति की राशि प्राप्त करने के लिए आईडी की एक बहुत छोटी रेंज देख सकते हैं।

SELECT COUNT(*) AS [Records], SUM(t.Amount) AS [Total]
FROM   dbo.t1 AS t
WHERE  t.Id > 0
       AND t.Id < 3;

परिणाम:

Records     Total
10010012    50015062308

क्वेरी प्रोफ़ाइल:

Table 't1'. Scan count 6, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 2560758, lob physical reads 0, lob read-ahead reads 0.
Table 't1'. Segment reads 4773, segment skipped 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 564 ms,  elapsed time = 106 ms.

मनोरंजन के लिए, एक बड़ा एकत्रीकरण:

SELECT COUNT(*) AS [Records], SUM(CONVERT(BIGINT, t.Amount)) AS [Total]
FROM   dbo.t1 AS t
WHERE  t.Id > 0
       AND t.Id < 101;

परिणाम:

Records     Total
500500505   2501989114575

क्वेरी प्रोफ़ाइल:

Table 't1'. Scan count 6, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 2560758, lob physical reads 0, lob read-ahead reads 0.
Table 't1'. Segment reads 4773, segment skipped 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 1859 ms,  elapsed time = 321 ms.

उम्मीद है की यह मदद करेगा!



2

एक BRIN सूचकांक के साथ PostgreSQL

यहां तक ​​कि अगर seq को अनुक्रमित किया जाता है, तो एक विशिष्ट डेटाबेस कार्यान्वयन प्रत्येक पंक्ति के माध्यम से लूप को सबसे अच्छा मामले O (n) में गणना करेगा, जहां n श्रेणी का आकार है।

यह सच नहीं है। कम से कम, कोई भी सभ्य डेटाबेस ऐसा नहीं करेगा। PostgreSQL इस प्रकार की तालिकाओं पर BRIN इंडेक्स बनाने का समर्थन करता है । BRIN इंडेक्स सुपर-छोटे होते हैं और यह बड़े पर टेबल्स में भी फिट हो सकते हैं। सैकड़ों लाखों पंक्तियों में कुछ भी नहीं है।

यहां, 300 मिलियन पंक्तियों को परिभाषित किया गया है जैसे आपने उन्हें आदेश दिया था। चेतावनी इसे बनाने में लंबा समय लग सकता है (समय: 336057.807 एमएस + 95121.809 एमएस सूचकांक के लिए)।

CREATE TABLE foo
AS
  SELECT seq::int, trunc(random()*100000)::int AS v
  FROM generate_series(1,3e8) AS gs(seq);

CREATE INDEX ON foo USING BRIN (seq);

ANALYZE foo;

और अब...

EXPLAIN ANALYZE SELECT sum(v) FROM foo WHERE seq BETWEEN 424242 AND 6313376;
                                                                QUERY PLAN                                                                 
-------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=1486163.53..1486163.54 rows=1 width=4) (actual time=1493.888..1493.888 rows=1 loops=1)
   ->  Bitmap Heap Scan on foo  (cost=58718.12..1471876.19 rows=5714938 width=4) (actual time=12.565..1035.153 rows=5889135 loops=1)
         Recheck Cond: ((seq >= 424242) AND (seq <= 6313376))
         Rows Removed by Index Recheck: 41105
         Heap Blocks: lossy=26240
         ->  Bitmap Index Scan on foo_seq_idx  (cost=0.00..57289.38 rows=5714938 width=0) (actual time=10.378..10.378 rows=262400 loops=1)
               Index Cond: ((seq >= 424242) AND (seq <= 6313376))
 Planning time: 0.125 ms
 Execution time: 1493.948 ms
(9 rows)

दी गई सीमा में 5,889,135 पंक्तियों को एकत्रित करने के लिए 1.4 सेकंड।

तालिका 10 जीबी होने के बावजूद, BRIN इंडेक्स 304 kB है।

और भी तेज

यदि यह अभी भी पर्याप्त तेज नहीं है, तो आप कुलियों को 100k पंक्तियों द्वारा कैश कर सकते हैं।

CREATE MATERIALIZED VIEW cache_foo
AS
  SELECT seq/1e5::int AS grp, sum(v)
  FROM foo GROUP BY seq/1e5::int
  ORDER BY 1;

अब आपको केवल 2(1e5-1)300 मिलियन या जो भी हो , ब्रिन और कुल पंक्तियों का उपयोग करने की आवश्यकता होगी ।

हार्डवेयर

लेनोवो x230, i5-3230M, 16GB रैम, 1tb सैमसंग 840 SSD।


धन्यवाद, मैं पढ़ूंगा और ब्रिन इंडेक्स के साथ अधिक प्रयोग करूंगा। यह अब तक का सबसे अच्छा विकल्प है।
राल्फ

3
अच्छा सुझाव, दोनों (BRIN इंडेक्स और भौतिक दृष्टिकोण)। लेकिन BRIN इंडेक्स के साथ क्वेरी अभी भी O (n) है। कृपया संपादित करें और अन्यथा दावा न करें। Materialized दृश्य की तुलना में बेहतर हो सकता है O(n), शायद O(sqrt(n))। इस बात पर निर्भर करता है कि आप भौतिककरण में उपयोग किए जाने वाले अंतराल को कैसे परिभाषित करेंगे।
ypercube y
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.