2011-01-13 46 views
11

Tôi đã có một bảng với khoảng 20 triệu hàng. Đối với lợi ích của đối số, cho phép nói có hai cột trong bảng - một id và dấu thời gian. Tôi đang cố gắng đếm số lượng mặt hàng mỗi ngày. Đây là những gì tôi có vào lúc này.Tăng tốc một nhóm theo truy vấn ngày trên một bảng lớn ở postgres

SELECT DATE(timestamp) AS day, COUNT(*) 
    FROM actions 
    WHERE DATE(timestamp) >= '20100101' 
    AND DATE(timestamp) < '20110101' 
GROUP BY day; 

Không có bất kỳ chỉ mục nào, điều này mất khoảng 30 giây để chạy trên máy của tôi. Dưới đây là giải thích phân tích đầu ra:

GroupAggregate (cost=675462.78..676813.42 rows=46532 width=8) (actual time=24467.404..32417.643 rows=346 loops=1) 
    -> Sort (cost=675462.78..675680.34 rows=87021 width=8) (actual time=24466.730..29071.438 rows=17321121 loops=1) 
     Sort Key: (date("timestamp")) 
     Sort Method: external merge Disk: 372496kB 
     -> Seq Scan on actions (cost=0.00..667133.11 rows=87021 width=8) (actual time=1.981..12368.186 rows=17321121 loops=1) 
       Filter: ((date("timestamp") >= '2010-01-01'::date) AND (date("timestamp") < '2011-01-01'::date)) 
Total runtime: 32447.762 ms 

Kể từ khi tôi nhìn thấy một quét tuần tự, tôi đã cố gắng để chỉ số vào ngày tổng

CREATE INDEX ON actions (DATE(timestamp)); 

nào cắt giảm tốc độ khoảng 50%.

HashAggregate (cost=796710.64..796716.19 rows=370 width=8) (actual time=17038.503..17038.590 rows=346 loops=1) 
    -> Seq Scan on actions (cost=0.00..710202.27 rows=17301674 width=8) (actual time=1.745..12080.877 rows=17321121 loops=1) 
     Filter: ((date("timestamp") >= '2010-01-01'::date) AND (date("timestamp") < '2011-01-01'::date)) 
Total runtime: 17038.663 ms 

Tôi mới làm quen với toàn bộ doanh nghiệp tối ưu hóa truy vấn này và tôi không biết phải làm gì tiếp theo. Bất kỳ đầu mối làm thế nào tôi có thể nhận được truy vấn này chạy nhanh hơn?

--edit--

Dường như tôi đang đánh các giới hạn của chỉ số. Đây là truy vấn duy nhất được chạy trên bảng này (mặc dù các giá trị của ngày thay đổi). Có cách nào để phân vùng lên bảng? Hoặc tạo một bảng bộ nhớ cache với tất cả các giá trị đếm? Hoặc bất kỳ tùy chọn nào khác?

+2

Bạn đang kể cho chúng tôi toàn bộ câu chuyện ở đây? Có vẻ như bạn đã thay đổi cài đặt bộ nhớ giữa gói đầu tiên và gói thứ hai. Đó sẽ là lời khuyên của tôi. ;-) –

+0

Không thay đổi cài đặt bộ nhớ, mặc dù tôi đã phân tích chân không giữa mọi thay đổi lược đồ. Không biết nếu tôi phải làm điều đó, nhưng nó ảnh hưởng nghiêm trọng đến kết quả. – zaius

Trả lời

5

Có cách nào để phân vùng bảng không?

Có:
http://www.postgresql.org/docs/current/static/ddl-partitioning.html

Hoặc tạo một bảng cache với tất cả các giá trị đếm? Hoặc bất kỳ tùy chọn nào khác?

Tạo bảng "cache" chắc chắn là có thể. Nhưng điều này phụ thuộc vào mức độ thường xuyên bạn cần kết quả đó và mức độ chính xác của nó.

 
CREATE TABLE action_report 
AS 
SELECT DATE(timestamp) AS day, COUNT(*) 
    FROM actions 
    WHERE DATE(timestamp) >= '20100101' 
    AND DATE(timestamp) < '20110101' 
GROUP BY day; 

Sau đó, SELECT * FROM action_report sẽ cung cấp cho bạn những gì bạn muốn một cách kịp thời. Sau đó, bạn sẽ lên lịch một công việc định kỳ để tạo lại bảng đó một cách thường xuyên.

Cách tiếp cận này tất nhiên sẽ không giúp ích nếu phạm vi thời gian thay đổi với mọi truy vấn hoặc nếu truy vấn đó chỉ chạy mỗi ngày một lần.

+0

Rất tuyệt.Tạo một bảng bộ nhớ cache chắc chắn sẽ giải quyết vấn đề. Lựa chọn từ bảng đó (rõ ràng) siêu nhanh. Có ngày cũ dữ liệu trong đó là tốt, và có một bản cập nhật 20 giây một lần một ngày là không có vấn đề hoặc. Có cách nào để cập nhật bảng với tuần gần đây nhất hay không, trái với việc giảm và tái tạo mỗi lần? – zaius

+1

Bạn không cần phải tạo mỗi lần. Khi được tạo, chỉ cần thực hiện một 'TRUNCATE action_report' theo sau là' INSERT INTO action_report SELECT .... ' –

+0

Tuyệt vời! Cảm ơn đã giúp đỡ. – zaius

1

Dường như phạm vi bao gồm chỉ bao gồm tất cả dữ liệu có sẵn.

Đây có thể là sự cố thiết kế. Nếu bạn sẽ chạy thường xuyên này, bạn nên tạo thêm một cột timestamp_date chỉ chứa ngày tháng. Sau đó tạo một chỉ mục trên cột đó và thay đổi truy vấn cho phù hợp. Cột nên được duy trì bằng cách chèn + trình kích hoạt cập nhật.

SELECT timestamp_date AS day, COUNT(*) 
FROM actions 
WHERE timestamp_date >= '20100101' 
    AND timestamp_date < '20110101' 
GROUP BY day; 

Nếu tôi sai về số lượng hàng phạm vi ngày sẽ tìm thấy (và nó chỉ là một nhóm nhỏ), sau đó bạn có thể thử một chỉ mục trên chỉ cột timestamp bản thân, áp dụng mệnh đề WHERE để chỉ cột (cho phạm vi hoạt động tương tự)

SELECT DATE(timestamp) AS day, COUNT(*) 
FROM actions 
WHERE timestamp >= '20100101' 
    AND timestamp < '20110101' 
GROUP BY day; 
+0

Có, bộ lọc dành cho một tập hợp con lớn dữ liệu. Các truy vấn của một tập con nhỏ hơn thường đủ nhanh - đó chỉ là các truy vấn lớn chậm. Tôi đã thử thêm cột timestamp_date và chỉ mục. Rất tiếc, tùy chọn này cũng không tăng tốc đáng kể. Đây là đầu ra: http://pastie.org/1454799 – zaius

+2

Tại sao điều đó tạo nên sự khác biệt? Một chỉ mục trên 'date (dấu thời gian)' với một truy vấn liên quan đến 'date (dấu thời gian)' sẽ có tác dụng tương tự như một chỉ mục trên 'timestamp_date' với một truy vấn liên quan đến cột' timestamp_date'. –

+1

@Peter Eisentraut, bạn phải bỏ phiếu giảm giá. Bất kể bạn nghĩ gì, hãy kiểm tra kết quả thực tế từ bài kiểm tra của zaius. Nó đã đi ra nhanh hơn một chút. Tức là, nếu 10% vẫn là cận biên và không đáng kể. – RichardTheKiwi

0

Hãy thử chạy explain analyze verbose ... để xem liệu tổng hợp có đang sử dụng tệp tạm thời hay không. Có lẽ tăng work_mem để cho phép nhiều việc hơn được thực hiện trong bộ nhớ?

+0

Đây là kết quả từ tiết lộ: http://pastie.org/1455439 Hình ảnh không quá khác biệt. (Lưu ý rằng tôi đã thêm nhiều dữ liệu hơn vì tôi đã làm mới từ db sản xuất) – zaius

+0

bạn đang sử dụng phiên bản postgresql nào? – araqnid

+0

Tôi đang chạy tất cả các thử nghiệm này trong môi trường dev của tôi, đó là postgres 9.0.1 trên mac. Sản xuất (nơi hiệu suất thực sự đếm) chạy trên 8.3 trên Ubuntu. Tôi có thể nâng cấp sản xuất nếu cần thiết. – zaius

2

Nói chung hầu hết các cơ sở dữ liệu sẽ bỏ qua các chỉ mục nếu số hàng dự kiến ​​được trả lại sẽ cao. Điều này là do mỗi lần nhấn chỉ mục, nó sẽ cần phải tìm hàng đó, vì vậy sẽ nhanh hơn khi thực hiện quét toàn bộ bảng. Con số này là từ 10.000 đến 100.000. Bạn có thể thử nghiệm với điều này bằng cách thu hẹp phạm vi ngày và xem nơi postgres lật sang sử dụng chỉ mục. Trong trường hợp này, postgres đang lên kế hoạch quét 17.301,674 hàng, vì vậy bảng của bạn khá lớn. Nếu bạn làm cho nó thực sự nhỏ và bạn vẫn cảm thấy như postgres đang làm cho sự lựa chọn sai sau đó thử chạy một phân tích trên bàn để postgres được xấp xỉ của nó đúng.

-1

Đặt work_mem để nói 2GB và xem liệu điều đó có thay đổi gói hay không. Nếu không, bạn có thể không có lựa chọn.

0

Điều bạn thực sự muốn cho các truy vấn loại DSS như vậy là bảng ngày mô tả ngày. Trong thiết kế cơ sở dữ liệu lingo nó được gọi là một chiều ngày tháng. Để điền bảng như vậy, bạn có thể sử dụng mã tôi đã đăng trong bài viết này: http://www.mockbites.com/articles/tech/data_mart_temporal

Sau đó, trong mỗi hàng trong bảng tác vụ, hãy đặt ngày tháng thích hợp.

truy vấn của bạn sau đó trở thành:

SELECT 
    d.full_date, COUNT(*) 
FROM actions a 
JOIN date_dimension d 
    ON a.date_key = d.date_key 
WHERE d.full_date = '2010/01/01' 
GROUP BY d.full_date 

Giả sử chỉ số trên các phím và FULL_DATE, đây sẽ là siêu nhanh bởi vì nó hoạt động trên phím INT4!

Một lợi ích khác là bây giờ bạn có thể cắt và xúc xắc bằng bất kỳ cột date_dimension nào khác.

Các vấn đề liên quan