ฐานข้อมูลสำหรับการค้นหารวมช่วงที่มีประสิทธิภาพ?


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))แบบสอบถามต่อ?

ฉันได้เจอโครงสร้างข้อมูลที่เรียกว่า Segment ต้นไม้ตามที่อธิบายไว้ที่นี่ บางครั้งก็เรียกว่าช่วงต้นไม้หรือช่วงต้นไม้แม้ว่าชื่อเหล่านี้มักจะอธิบายว่าเป็นรูปแบบที่แตกต่างกันเล็กน้อยของโครงสร้างข้อมูล

อย่างไรก็ตามฉันยังไม่เจอฐานข้อมูลใด ๆ ที่ใช้โครงสร้างข้อมูลดังกล่าว การนำไปใช้ตั้งแต่เริ่มต้นนั้นเป็นเรื่องง่ายสำหรับโครงสร้างในหน่วยความจำ แต่จะยุ่งยากหากต้องคงไว้หรือมีขนาดใหญ่เกินไปที่จะใส่ลงในหน่วยความจำ หากมีรูปแบบที่มีประสิทธิภาพสำหรับการนำไปใช้งานด้านบนของฐานข้อมูลที่มีอยู่นั่นอาจช่วยได้เช่นกัน

หมายเหตุด้านข้าง: นี่ไม่ใช่ตารางต่อท้ายเท่านั้นดังนั้นโซลูชันเช่นการรักษายอดรวมสะสมจะไม่ทำงานในกรณีนี้


นี้เป็นกรณีที่ใช้งานทั่วไปสำหรับฐานข้อมูลคอลัมน์จัดซึ่งมีจำนวนมาก
mustaccio

แม้แต่ฐานข้อมูลที่จัดเรียงคอลัมน์ยังคงต้องใช้เวลา O (n) ในการสแกนแถว n ที่กล่าวว่าฐานข้อมูลที่มีการจัดเรียงคอลัมน์จำนวนมากนั้นดีมากในการทำแบบสอบถามแบบขนานดังนั้นมันจะทำงานได้เร็วกว่าในฐานข้อมูลนั้น
Brian

คำตอบ:


8

การใช้ดัชนีSQL Server ColumnStore

ก็โอเคแค่ดัชนี CS แบบคลัสเตอร์

หากคุณต้องการที่จะอ่านเกี่ยวกับฮาร์ดแวร์ที่ผมทำนี้ในหัวมากกว่าที่นี่ การเปิดเผยอย่างเต็มรูปแบบฉันเขียนบล็อกโพสต์บนเว็บไซต์ของ บริษัท ที่ฉันทำงาน

เพื่อทดสอบ!

นี่คือรหัสทั่วไปในการสร้างตารางที่ค่อนข้างใหญ่ คำเตือนเดียวกันกับ Evan สิ่งนี้อาจใช้เวลาสักครู่ในการสร้างและจัดทำดัชนี

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

เมื่อดูที่การนับ Id ทุกตัวจะมีการแจกแจงที่สม่ำเสมอ:

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

ด้วย Id ทุกอันที่มีประมาณ 5,005,005 แถวเราสามารถดู ID ที่ค่อนข้างเล็กเพื่อให้ได้ผลรวม 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

PostgreSQL พร้อมดัชนี BRIN

แม้ว่า seq จะถูกจัดทำดัชนีการใช้ฐานข้อมูลทั่วไปจะวนรอบแต่ละแถวเพื่อคำนวณผลรวมในกรณีที่ดีที่สุด O (n) โดยที่ n คือขนาดของช่วง

ที่ไม่เป็นความจริง. อย่างน้อยก็ไม่มีฐานข้อมูลที่เหมาะสมจะทำเช่นนั้น PostgreSQL รองรับการสร้างดัชนี BRINในตารางประเภทนี้ ดัชนี BRINนั้นเล็กมากและสามารถใส่ในหน่วยความจำได้แม้กระทั่งบนโต๊ะตัวใหญ่นี้ แถวหลายร้อยล้านแถวไม่มีอะไรเลย

ที่นี่มีการกำหนด 300 ล้านแถวเหมือนที่คุณสั่ง คำเตือนอาจใช้เวลานานในการสร้าง (เวลา: 336057.807 ms + 95121.809 ms สำหรับดัชนี)

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)

1.4 วินาทีเพื่อรวม / รวม 5,889,135 แถวในช่วงที่กำหนด

แม้ว่าตารางจะมีขนาด 10 GB ก็ตามดัชนี 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;

ตอนนี้คุณจะต้องใช้ brin และ2(1e5-1)แถวรวมมากกว่า 300 ล้านหรืออะไรก็ตาม

ฮาร์ดแวร์

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


ขอบคุณฉันจะอ่านและทดสอบมากขึ้นด้วยดัชนี BRIN นี่เป็นตัวเลือกที่ดีที่สุดจนถึงตอนนี้
Ralf

3
คำแนะนำที่ดีทั้ง (ดัชนี BRIN และมุมมองที่ปรากฏ) แต่เคียวรีแม้จะมีดัชนี BRIN ยังคงเป็น O (n) โปรดแก้ไขและไม่อ้างสิทธิ์เป็นอย่างอื่น มุมมอง materialized อาจจะดีกว่าบางทีO(n) O(sqrt(n))ขึ้นอยู่กับว่าคุณจะกำหนดช่วงเวลาที่จะใช้ในการทำให้เป็นจริง
ypercubeᵀᴹ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.