Cơ sở dữ liệu cho các truy vấn tổng hợp phạm vi hiệu quả?


11

Như một ví dụ đơn giản, giả sử tôi có một bảng như thế này:

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

Bảng có thể chứa hàng trăm triệu bản ghi và tôi cần thường xuyên thực hiện các truy vấn như thế này:

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

Ngay cả khi seqđược lập chỉ mục, việc triển khai cơ sở dữ liệu thông thường sẽ lặp qua từng hàng để tính tổng trong trường hợp tốt nhất O(n), trong đó nkích thước của phạm vi.

Có cơ sở dữ liệu nào có thể thực hiện việc này một cách hiệu quả, như trong O(log(n))mỗi truy vấn không?

Tôi đã bắt gặp một cấu trúc dữ liệu được gọi là Cây phân đoạn như được mô tả ở đây . Cũng đôi khi được gọi là cây phạm vi hoặc cây khoảng, mặc dù tất cả các tên này thường được mô tả là một biến thể hơi khác nhau của cấu trúc dữ liệu.

Tuy nhiên, tôi không bắt gặp bất kỳ cơ sở dữ liệu nào thực hiện cấu trúc dữ liệu như vậy. Việc thực hiện nó từ đầu rất dễ dàng đối với cấu trúc trong bộ nhớ, nhưng sẽ trở nên khó khăn nếu nó phải được duy trì hoặc quá lớn để phù hợp với bộ nhớ. Nếu có một mô hình hiệu quả để thực hiện điều này trên cơ sở dữ liệu hiện có, điều đó cũng có thể giúp ích.

Lưu ý bên lề: Đây không phải là bảng chỉ phụ lục, vì vậy một giải pháp như giữ một khoản tiền tích lũy sẽ không hoạt động trong trường hợp này.


Đây là trường hợp sử dụng điển hình cho cơ sở dữ liệu được tổ chức theo cột, trong đó có nhiều cơ sở dữ liệu .
mustaccio

Ngay cả một cơ sở dữ liệu được tổ chức theo cột vẫn sẽ cần thời gian O (n) để quét n hàng. Điều đó nói rằng, nhiều cơ sở dữ liệu được tổ chức theo cột rất tốt trong việc song song các truy vấn như vậy, vì vậy nó sẽ chạy nhanh hơn nhiều trên cơ sở dữ liệu như vậy.
Brian

Câu trả lời:


8

Sử dụng chỉ mục SQL Server ColumnStore

Chà, được thôi, chỉ một - một chỉ số CS co cụm.

Nếu bạn muốn đọc về phần cứng tôi đã làm điều này, hãy đi qua đây . Tiết lộ đầy đủ, tôi đã viết bài đăng trên blog của trang web của công ty tôi làm việc.

Vào bài kiểm tra!

Đây là một số mã chung để xây dựng một bảng khá lớn. Cùng một cảnh báo như Evan, điều này có thể mất một thời gian để xây dựng và lập chỉ mục.

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

Chà, Evan thắng vì đơn giản, nhưng tôi đã nói về điều đó trước đây.

Đây là định nghĩa chỉ mục. La và dee và dah.

CREATE CLUSTERED COLUMNSTORE INDEX CX_WOAHMAMA ON dbo.t1

Nhìn vào số đếm, mọi Id đều có phân phối khá đồng đều:

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

Các kết quả:

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

...

994 5005005
995 5005005
996 5005005
997 5005005
998 5005005

Với mỗi Id có ~ 5,005.005 hàng, chúng tôi có thể xem xét một phạm vi ID khá nhỏ để giúp bạn kiếm được 10 triệu hàng.

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

Kết quả:

Records     Total
10010012    50015062308

Hồ sơ truy vấn:

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.

Để giải trí, một tập hợp lớn hơn:

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

Các kết quả:

Records     Total
500500505   2501989114575

Hồ sơ truy vấn:

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.

Hi vọng điêu nay co ich!



2

PostgreSQL với chỉ mục BRIN

Ngay cả khi seq được lập chỉ mục, việc triển khai cơ sở dữ liệu thông thường sẽ lặp qua từng hàng để tính tổng trong trường hợp tốt nhất O (n), trong đó n là kích thước của phạm vi.

Đo không phải sự thật. Ít nhất, không có cơ sở dữ liệu phong nha sẽ làm điều đó. PostgreSQL hỗ trợ tạo các chỉ mục BRIN trên các loại bảng này. Các chỉ số BRIN là siêu nhỏ và có thể phù hợp với ram ngay cả trên các bảng lớn này. Hàng trăm triệu hàng không có gì.

Ở đây, 300 triệu hàng được xác định giống như bạn đặt hàng chúng. Cảnh báo có thể mất nhiều thời gian để tạo ra nó (Thời gian: 336057.807 ms + 95121.809 ms cho chỉ mục).

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;

Và bây giờ...

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)

1,4 giây để tổng hợp / tổng số 5,889,135 hàng trong phạm vi đã cho.

Mặc dù bảng là 10 GB, chỉ số BRIN là 304 kB.

Thậm chí nhanh hơn

Nếu điều này vẫn chưa đủ nhanh, bạn có thể lưu trữ các tập hợp theo hàng 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;

Bây giờ bạn sẽ chỉ cần sử dụng các hàng brin và tổng hợp 2(1e5-1)thay vì 300 triệu hoặc bất cứ thứ gì.

Phần cứng

Lenovo x230, i5-3230M, RAM 16 GB, SSD Samsung 840 1tb.


Cảm ơn, tôi sẽ đọc và thử nghiệm nhiều hơn với các chỉ mục BRIN. Điều này có vẻ như là lựa chọn tốt nhất cho đến nay.
Ralf

3
Đề xuất tốt đẹp, cả hai (chỉ số BRIN và quan điểm cụ thể hóa). Nhưng truy vấn, ngay cả với chỉ số BRIN vẫn là O (n). Vui lòng chỉnh sửa và không yêu cầu khác. Quan điểm cụ thể hóa có thể tốt hơn O(n), có lẽ O(sqrt(n)). Phụ thuộc vào cách bạn sẽ xác định các khoảng sẽ được sử dụng trong quá trình cụ thể hóa.
ypercubeᵀᴹ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.